diff options
| -rw-r--r-- | modules/quassel/default.nix | 9 | ||||
| -rw-r--r-- | modules/quassel/quassel.nix | 295 |
2 files changed, 158 insertions, 146 deletions
diff --git a/modules/quassel/default.nix b/modules/quassel/default.nix index efb9661..8961546 100644 --- a/modules/quassel/default.nix +++ b/modules/quassel/default.nix @@ -22,12 +22,9 @@ in settings = { listen = [ "178.63.224.10" "2a01:4f8:231:56a::10" ]; dataDir = "/var/lib/quassel"; - configFromEnvironment = true; + useDeclarativeConfig = true; db = { - backend = "PostgreSQL"; - pgsql = { - database = "quassel"; - }; + backend = "postgresql"; }; ssl = { required = true; @@ -56,7 +53,7 @@ in }; }; - users.users.quassel.extraGroups = [ "acme" ]; + # users.users.quassel.extraGroups = [ "acme" ]; security.acme.certs = { "cocaine.farm" = { diff --git a/modules/quassel/quassel.nix b/modules/quassel/quassel.nix index 13f6ba9..e3eeac6 100644 --- a/modules/quassel/quassel.nix +++ b/modules/quassel/quassel.nix @@ -6,44 +6,54 @@ let cfg = config.services.quassel; opt = options.services.quassel; quassel = cfg.package; - user = if cfg.user != null then cfg.user else "quassel"; in { + imports = [ + (mkRenamedOptionModule [ "services" "quassel" "certificateFile" ] [ "services" "quassel" "settings" "ssl" "certFile" ]) + (mkRenamedOptionModule [ "services" "quassel" "requireSSL" ] [ "services" "quassel" "settings" "ssl" "required" ]) + ]; + 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. - ''; - }; + enable = mkEnableOption (lib.mdDoc "the Quassel IRC client daemon"); + + package = lib.mkPackageOptionMD pkgs "quasselDaemon" { }; 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. + type = types.str; + default = "quassel"; + description = lib.mdDoc '' + The user the Quassel daemon should run as. By default a systemd DynamicUser + is used with the name specified here. DynamicUser functionality will be + automatically disabled if the specified user already exists. ''; }; environmentFile = mkOption { - type = types.nullOr types.str; + type = types.nullOr types.path; default = null; - description = '' + description = lib.mdDoc '' 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. + For example to inject the `DB_PSQL_PASSWORD` and `AUTH_LDAP_BIND_PASSWORD` variables instead + of setting {option}`services.quassel.settings.db.psql.password` and {option}`services.quassel.settings.auth.ldap.bindPassword`. + + Example Content: + ``` + DB_PSQL_PASSWORD=changeme + AUTH_LDAP_BIND_PASSWORD=changemetoo + ``` + + ::: {.note} Since this file is read by systemd, it may have permission 0400 and be owned by root. + ::: ''; }; settings = mkOption { - description = literalExpression '' + description = lib.mdDoc '' Configuration for quassel daemon. ''; type = types.submodule { @@ -51,7 +61,7 @@ in listen = mkOption { type = types.listOf types.str; default = [ "127.0.0.1" "::1" ]; - description = '' + description = lib.mdDoc '' The address(es) quasselcore will listen on. ''; }; @@ -59,50 +69,49 @@ in port = mkOption { default = 4242; type = types.port; - description = '' + description = lib.mdDoc '' 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 = '' + type = types.path; + default = "/var/lib/quassel"; + description = lib.mdDoc '' The directory holding configuration files, the SQlite database and the SSL Cert. + The default directory will be created by systemd using StateDirectory + + ::: {note} + If set to a custom directory you might have to create a user and adjust the + user used in {option}`services.quassel.user`. + ::: ''; }; - configFromEnvironment = mkOption { + useDeclarativeConfig = 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. + description = lib.mdDoc '' + Configure quassels authenticator and database settings using the + {option}`services.quassel.settings.auth` and {option}`services.quassel.settings.db` sections. + + Overrides whatever configuration for the database and authentication you have made using the setup wizard. ''; }; ident = mkOption { - description = literalExpression '' + description = lib.mdDoc '' Configuration for quassels internal ident daemon. ''; default = { }; type = types.submodule { options = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Enable internal ident daemon. - ''; - }; + enable = lib.mkEnableOption (lib.mdDoc "internal ident daemon."); strict = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Use users quasselcore username as ident reply. Ignores each user's configured ident setting. ''; }; @@ -110,7 +119,7 @@ in listen = mkOption { default = [ "127.0.0.1" "::1" ]; type = types.listOf types.str; - description = '' + description = lib.mdDoc '' The address(es) quasselcore will listen on for ident requests. ''; }; @@ -118,7 +127,7 @@ in port = mkOption { default = 10113; type = types.port; - description = '' + description = lib.mdDoc '' The port quasselcore will listen at for ident requests. ''; }; @@ -127,24 +136,18 @@ in }; oidentd = mkOption { - description = literalExpression '' + description = lib.mdDoc '' Configuration for quassels integration with oidentd. ''; default = { }; type = types.submodule { options = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Enable oidentd integration. - ''; - }; + enable = lib.mkEnableOption (lib.mdDoc "oidentd integration."); - confFile = mkOption { - type = types.nullOr types.str; + configFile = mkOption { + type = types.nullOr types.path; default = null; - description = '' + description = lib.mdDoc '' Set path to oidentd configuration file. ''; }; @@ -154,29 +157,40 @@ in ssl = mkOption { default = { }; + description = lib.mdDoc '' + Configuration for quassel ssl + ''; type = types.submodule { options = { required = mkOption { type = types.bool; default = false; - description = '' + description = lib.mdDoc '' Require SSL for remote (non-loopback) client connections. ''; }; certFile = mkOption { - type = types.nullOr types.str; + type = types.nullOr types.path; default = null; - description = '' - Specify the path to the SSL certificate. + description = lib.mdDoc '' + Specify the path to the SSL certificate. Passed to quassel using systemd's LoadCredential. + + ::: {.note} + Since this file is read by systemd, it may have permission 0400 and be owned by root. + ::: ''; }; keyFile = mkOption { - type = types.nullOr types.str; + type = types.nullOr types.path; default = null; - description = '' - Specify the path to the SSL key. + description = lib.mdDoc '' + Specify the path to the SSL key. Passed to quassel using systemd's LoadCredential. + + ::: {.note} + Since this file is read by systemd, it may have permission 0400 and be owned by root. + ::: ''; }; }; @@ -184,24 +198,18 @@ in }; metrics = mkOption { - description = literalExpression '' + description = lib.mdDoc '' Export metrics in prometheus format ''; default = { }; type = types.submodule { options = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Enable prometheus metrics API. - ''; - }; + enable = lib.mkEnableOption (lib.mdDoc "prometheus metrics API."); listen = mkOption { default = [ "127.0.0.1" "::1" ]; type = types.listOf types.str; - description = '' + description = lib.mdDoc '' The address(es) quasselcore will listen on for metrics requests. ''; }; @@ -209,7 +217,7 @@ in port = mkOption { default = 9558; type = types.port; - description = '' + description = lib.mdDoc '' The port quasselcore will listen at for metrics requests. ''; }; @@ -220,38 +228,49 @@ in logLevel = mkOption { type = types.enum [ "Debug" "Info" "Warning" "Error" ]; default = "Info"; - description = '' - Supports one of Debug|Info|Warning|Error; + description = lib.mdDoc '' + Log level of the quassel core. ''; }; db = mkOption { default = { }; + description = lib.mdDoc '' + Configuration for quassel database + ''; type = types.submodule { options = { backend = mkOption { - type = types.enum [ "SQLite" "PostgreSQL" ]; - default = "SQLite"; - description = literalExpression '' + type = types.enum [ "sqlite" "postgresql" ]; + default = "sqlite"; + apply = value: if value == "sqlite" then "SQLite" else "PostgreSQL"; + description = lib.mdDoc '' Specify the database backend. - In case SQLite is used, the database will be stored in ''${opt.settings.dataDir}/quassel-storage.sqlite + In case SQLite is used, the database will be stored in `''${services.quassel.settings.dataDir}/quassel-storage.sqlite` ''; }; pgsql = mkOption { - description = '' - Configuration for PostgreSQL Connection if ''${opt.settings.db.type} is set to "PostgreSQL". + description = lib.mdDoc '' + Configuration for PostgreSQL Connection if {option}`services.quassel.settings.db.type` is set to "PostgreSQL". TCP and UNIX Sockets are supported + + ::: {.note} + If the postgresql server is on the same machine you can add an `after` to the quassel systemd service: + ``` + systemd.services.quassel.after = [ "postgresql.service" ] + ``` + ::: ''; - default = null; - type = types.nullOr (types.submodule { + default = { }; + type = types.submodule { options = { username = mkOption { type = types.str; - default = user; - description = '' + default = "quassel"; + description = lib.mdDoc '' Specifies the Postgres connection username. ''; }; @@ -259,16 +278,19 @@ in 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. + description = lib.mdDoc '' + Specifies the Postgres connection user password. + Can also be set as `DB_PGSQL_PASSWORD` in the `service.quassel.environmentFile` + + 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 = '' + description = lib.mdDoc '' Specifies the Postgres connection hostname. Either an IP Address or hostname for a TCP Connection or the path to the directory @@ -277,9 +299,9 @@ in }; port = mkOption { - default = 5432; type = types.port; - description = '' + default = 5432; + description = lib.mdDoc '' Specifies the Postgres connection port. ''; }; @@ -287,12 +309,12 @@ in database = mkOption { type = types.str; default = "quassel"; - description = '' + description = lib.mdDoc '' Specifies the Postgres connection database name. ''; }; }; - }); + }; }; }; }; @@ -300,24 +322,37 @@ in auth = mkOption { default = { }; + description = lib.mdDoc '' + Configuration for quassel authentication backends + ''; type = types.submodule { options = { authenticator = mkOption { type = types.enum [ "Database" "LDAP" ]; default = "Database"; - description = '' + description = lib.mdDoc '' Specify the backend used to authenticate users to quassel. Either "Database" to - use quassel database or "Ldap" to use an external LDAP Server + use quassel database or "LDAP" to use an external LDAP Server ''; }; ldap = mkOption { - default = null; - type = types.nullOr (types.submodule { + description = lib.mdDoc '' + Configuration for quassel LDAP authentication backend + + ::: {.note} + If the ldap server is on the same machine you can add an `after` to the quassel systemd service: + ``` + systemd.services.quassel.after = [ "your-ldap-server.service" ] + ``` + ::: + ''; + type = types.submodule { options = { hostname = mkOption { type = types.str; - description = '' + example = "ldap://example.com"; + description = lib.mdDoc '' Specifies the LDAP authenticator connection hostname. ''; }; @@ -325,14 +360,14 @@ in port = mkOption { default = 389; type = types.port; - description = '' + description = lib.mdDoc '' Specifies the LDAP authenticator connection port. ''; }; bindDN = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Specifies the LDAP authenticator bind DN. ''; }; @@ -340,22 +375,25 @@ in 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. + description = lib.mdDoc '' + Specifies the LDAP authenticator bind password. + Can also be set as `AUTH_LDAP_BIND_PASSWORD` in the `service.quassel.environmentFile` + + Warning: do not set confidential information here because it + is world-readable in the Nix store. ''; }; baseDN = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Specifies the LDAP authenticator base DN. ''; }; filter = mkOption { type = types.str; - description = '' + description = lib.mdDoc '' Specifies the LDAP authenticator filter. ''; example = "(objectClass=inetOrgPerson)"; @@ -364,13 +402,13 @@ in uidAttribute = mkOption { default = "uid"; type = types.str; - description = '' + description = lib.mdDoc '' Specifies the LDAP authenticator UID attribute. ''; example = "cn"; }; }; - }); + }; }; }; }; @@ -389,34 +427,6 @@ in 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 = @@ -424,9 +434,7 @@ in 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"; + after = [ "network.target" ]; serviceConfig = { @@ -452,19 +460,27 @@ in "--metrics-listen=${concatStringsSep "," cfg.settings.metrics.listen}" "--metrics-port=${toString cfg.settings.metrics.port}" ] - ++ optional cfg.settings.configFromEnvironment "--config-from-environment" + ++ optional cfg.settings.useDeclarativeConfig "--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}" + ++ optional (cfg.settings.ssl.certFile != null) "--ssl-cert=%d/certfile" + ++ optional (cfg.settings.ssl.keyFile != null) "--ssl-key=%d/keyfile" )); - ExecReload="${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + LoadCredential = + optional (cfg.settings.ssl.certFile != null) "certfile:${cfg.settings.ssl.certFile}" + ++ optional (cfg.settings.ssl.keyFile != null) "keyfile:${cfg.settings.ssl.keyFile}"; + + DynamicUser = true; + User = cfg.user; + StateDirectory = "quassel"; + + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; - Environment = mkIf cfg.settings.configFromEnvironment ([ + Environment = mkIf cfg.settings.useDeclarativeConfig ([ "AUTH_AUTHENTICATOR=${cfg.settings.auth.authenticator}" "DB_BACKEND=${cfg.settings.db.backend}" ] ++ (optional (cfg.settings.db.backend == "PostgreSQL") [ @@ -482,7 +498,6 @@ in "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; }; }; }; |
