{ config, lib, options, pkgs, ... }: with lib; let cfg = config.services.quassel; opt = options.services.quassel; quassel = cfg.package; user = if cfg.user != null then cfg.user else "quassel"; in { options = { services.quassel = { enable = mkEnableOption ("the Quassel IRC client daemon"); package = mkOption { type = types.package; default = pkgs.quasselDaemon; defaultText = literalExpression "pkgs.quasselDaemon"; description = '' The package of the quassel daemon. ''; }; user = mkOption { default = null; type = types.nullOr types.str; description = '' The existing user the Quassel daemon should run as. If left empty, a default "quassel" user will be created. ''; }; environmentFile = mkOption { type = types.nullOr types.str; default = null; description = '' Path to an environment file loaded for the quassel service. This can be used to securely store tokens and secrets outside of the world-readable Nix store. Since this file is read by systemd, it may have permission 0400 and be owned by root. ''; }; settings = mkOption { description = literalExpression '' Configuration for quassel daemon. ''; type = types.submodule { options = { listen = mkOption { type = types.listOf types.str; default = [ "127.0.0.1" "::1" ]; description = '' The address(es) quasselcore will listen on. ''; }; port = mkOption { default = 4242; type = types.port; description = '' The port quasselcore will listen at. ''; }; dataDir = mkOption { default = "/home/${user}/.config/quassel-irc.org"; defaultText = literalExpression '' "/home/''${config.${opt.user}}/.config/quassel-irc.org" ''; type = types.str; description = '' The directory holding configuration files, the SQlite database and the SSL Cert. ''; }; configFromEnvironment = mkOption { default = false; type = types.bool; description = '' Configure quassels authenticator and database settings using environment variables, Instead of imperatively setting it up using the setup wizard during first connection to the quassel core. ''; }; ident = mkOption { description = literalExpression '' Configuration for quassels internal ident daemon. ''; default = { }; type = types.submodule { options = { enable = mkOption { default = false; type = types.bool; description = '' Enable internal ident daemon. ''; }; strict = mkOption { type = types.bool; default = false; description = '' Use users quasselcore username as ident reply. Ignores each user's configured ident setting. ''; }; listen = mkOption { default = [ "127.0.0.1" "::1" ]; type = types.listOf types.str; description = '' The address(es) quasselcore will listen on for ident requests. ''; }; port = mkOption { default = 10113; type = types.port; description = '' The port quasselcore will listen at for ident requests. ''; }; }; }; }; oidentd = mkOption { description = literalExpression '' Configuration for quassels integration with oidentd. ''; default = { }; type = types.submodule { options = { enable = mkOption { type = types.bool; default = false; description = '' Enable oidentd integration. ''; }; confFile = mkOption { type = types.nullOr types.str; default = null; description = '' Set path to oidentd configuration file. ''; }; }; }; }; ssl = mkOption { default = { }; type = types.submodule { options = { required = mkOption { type = types.bool; default = false; description = '' Require SSL for remote (non-loopback) client connections. ''; }; certFile = mkOption { type = types.nullOr types.str; default = null; description = '' Specify the path to the SSL certificate. ''; }; keyFile = mkOption { type = types.nullOr types.str; default = null; description = '' Specify the path to the SSL key. ''; }; }; }; }; metrics = mkOption { description = literalExpression '' Export metrics in prometheus format ''; default = { }; type = types.submodule { options = { enable = mkOption { type = types.bool; default = false; description = '' Enable prometheus metrics API. ''; }; listen = mkOption { default = [ "127.0.0.1" "::1" ]; type = types.listOf types.str; description = '' The address(es) quasselcore will listen on for metrics requests. ''; }; port = mkOption { default = 9558; type = types.port; description = '' The port quasselcore will listen at for metrics requests. ''; }; }; }; }; logLevel = mkOption { type = types.enum [ "Debug" "Info" "Warning" "Error" ]; default = "Info"; description = '' Supports one of Debug|Info|Warning|Error; ''; }; db = mkOption { default = { }; type = types.submodule { options = { backend = mkOption { type = types.enum [ "SQLite" "PostgreSQL" ]; default = "SQLite"; description = literalExpression '' Specify the database backend. In case SQLite is used, the database will be stored in ''${opt.settings.dataDir}/quassel-storage.sqlite ''; }; pgsql = mkOption { description = '' Configuration for PostgreSQL Connection if ''${opt.settings.db.type} is set to "PostgreSQL". TCP and UNIX Sockets are supported ''; default = null; type = types.nullOr (types.submodule { options = { username = mkOption { type = types.str; default = user; description = '' Specifies the Postgres connection username. ''; }; password = mkOption { type = types.nullOr types.str; default = null; description = '' Specifies the Postgres connection user password. Warning: do not set confidential information here because it is world-readable in the Nix store. ''; }; hostname = mkOption { type = types.nullOr types.str; default = "/var/run/postgresql/"; description = '' Specifies the Postgres connection hostname. Either an IP Address or hostname for a TCP Connection or the path to the directory that contains a UNIX Socket. ''; }; port = mkOption { default = 5432; type = types.port; description = '' Specifies the Postgres connection port. ''; }; database = mkOption { type = types.str; default = "quassel"; description = '' Specifies the Postgres connection database name. ''; }; }; }); }; }; }; }; auth = mkOption { default = { }; type = types.submodule { options = { authenticator = mkOption { type = types.enum [ "Database" "LDAP" ]; default = "Database"; description = '' Specify the backend used to authenticate users to quassel. Either "Database" to use quassel database or "Ldap" to use an external LDAP Server ''; }; ldap = mkOption { default = null; type = types.nullOr (types.submodule { options = { hostname = mkOption { type = types.str; description = '' Specifies the LDAP authenticator connection hostname. ''; }; port = mkOption { default = 389; type = types.port; description = '' Specifies the LDAP authenticator connection port. ''; }; bindDN = mkOption { type = types.str; description = '' Specifies the LDAP authenticator bind DN. ''; }; bindPassword = mkOption { type = types.nullOr types.str; default = null; description = '' Specifies the LDAP authenticator bind password. Warning: do not set confidential information here because it is world-readable in the Nix store. ''; }; baseDN = mkOption { type = types.str; description = '' Specifies the LDAP authenticator base DN. ''; }; filter = mkOption { type = types.str; description = '' Specifies the LDAP authenticator filter. ''; example = "(objectClass=inetOrgPerson)"; }; uidAttribute = mkOption { default = "uid"; type = types.str; description = '' Specifies the LDAP authenticator UID attribute. ''; example = "cn"; }; }; }); }; }; }; }; }; }; }; }; }; ###### implementation config = mkIf cfg.enable { assertions = [ { assertion = cfg.settings.ssl.required -> cfg.settings.ssl.certFile != null; message = "Quassel needs a certificate file in order to require SSL"; } { assertion = cfg.settings.db.backend == "PostgreSQL" -> cfg.settings.db.pgsql != null; message = "Quassel needs postgresql connection settings if database type is set to PostgreSQL"; } { assertion = cfg.settings.auth.authenticator == "Ldap" -> cfg.settings.auth.ldap != null; message = "Quassel needs ldap connection settings if authenticator type is set to Ldap"; } ]; users.users = optionalAttrs (cfg.user == null) { quassel = { name = "quassel"; description = "Quassel IRC client daemon"; group = "quassel"; uid = config.ids.uids.quassel; }; }; users.groups = optionalAttrs (cfg.user == null) { quassel = { name = "quassel"; gid = config.ids.gids.quassel; }; }; systemd.tmpfiles.rules = [ "d '${cfg.settings.dataDir}' - ${user} - - -" ]; systemd.services.quassel = { description = "Quassel IRC client daemon"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ] ++ optional config.services.postgresql.enable "postgresql.service" ++ optional config.services.mysql.enable "mysql.service"; serviceConfig = { ExecStart = (concatStringsSep " " ([ "${quassel}/bin/quasselcore" "--listen=${concatStringsSep "," cfg.settings.listen}" "--port=${toString cfg.settings.port}" "--configdir=${cfg.settings.dataDir}" "--loglevel=${cfg.settings.logLevel}" ] ++ (optionals cfg.settings.ident.enable [ "--ident-daemon" "--ident-listen=${concatStringsSep "," cfg.settings.ident.listen}" "--ident-port=${toString cfg.settings.ident.port}" ] ++ (optional cfg.settings.ident.strict "--strict-ident")) ++ optionals cfg.settings.oidentd.enable [ "--oidentd" "--oidentd-conffile=${cfg.settings.ident.listen}" ] ++ optionals cfg.settings.metrics.enable [ "--metrics-daemon" "--metrics-listen=${concatStringsSep "," cfg.settings.metrics.listen}" "--metrics-port=${toString cfg.settings.metrics.port}" ] ++ optional cfg.settings.configFromEnvironment "--config-from-environment" # SSL ++ optional cfg.settings.ssl.required "--require-ssl" ++ optional (cfg.settings.ssl.certFile != null) "--ssl-cert=${cfg.settings.ssl.certFile}" ++ optional (cfg.settings.ssl.keyFile != null) "--ssl-key=${cfg.settings.ssl.keyFile}" )); ExecReload="${pkgs.coreutils}/bin/kill -HUP $MAINPID"; EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; Environment = mkIf cfg.settings.configFromEnvironment ([ "AUTH_AUTHENTICATOR=${cfg.settings.auth.authenticator}" "DB_BACKEND=${cfg.settings.db.backend}" ] ++ (optional (cfg.settings.db.backend == "PostgreSQL") [ "DB_PGSQL_DATABASE=${cfg.settings.db.pgsql.database}" "DB_PGSQL_HOSTNAME=${cfg.settings.db.pgsql.hostname}" "DB_PGSQL_USERNAME=${cfg.settings.db.pgsql.username}" "DB_PGSQL_PORT=${toString cfg.settings.db.pgsql.port}" ] ++ optional (cfg.settings.db.pgsql.password != null) "DB_PGSQL_PASSWORD=${cfg.settings.db.pgsql.password}" ) ++ (optional (cfg.settings.auth.authenticator == "LDAP") [ "AUTH_LDAP_BASE_DN=${cfg.settings.auth.ldap.baseDN}" "AUTH_LDAP_BIND_DN=${cfg.settings.auth.ldap.bindDN}" "AUTH_LDAP_FILTER=${cfg.settings.auth.ldap.filter}" "AUTH_LDAP_HOSTNAME=${cfg.settings.auth.ldap.hostname}" "AUTH_LDAP_PORT=${toString cfg.settings.auth.ldap.port}" "AUTH_LDAP_UID_ATTRIBUTE=${cfg.settings.auth.ldap.uidAttribute}" ] ++ optional (cfg.settings.auth.ldap.bindPassword != null) "AUTH_LDAP_BIND_PASSWORD=${cfg.settings.auth.ldap.bindPassword}" )); User = user; }; }; }; }