diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services')
21 files changed, 662 insertions, 390 deletions
diff --git a/nixpkgs/nixos/modules/services/hardware/auto-cpufreq.nix b/nixpkgs/nixos/modules/services/hardware/auto-cpufreq.nix index cf27bdd8b6eb..9c69ba8920f3 100644 --- a/nixpkgs/nixos/modules/services/hardware/auto-cpufreq.nix +++ b/nixpkgs/nixos/modules/services/hardware/auto-cpufreq.nix @@ -44,5 +44,8 @@ in { }; # uses attributes of the linked package - meta.buildDocsInSandbox = false; + meta = { + buildDocsInSandbox = false; + maintainers = with lib.maintainers; [ nicoo ]; + }; } diff --git a/nixpkgs/nixos/modules/services/matrix/synapse-log_config.yaml b/nixpkgs/nixos/modules/services/matrix/synapse-log_config.yaml deleted file mode 100644 index d85bdd1208f9..000000000000 --- a/nixpkgs/nixos/modules/services/matrix/synapse-log_config.yaml +++ /dev/null @@ -1,25 +0,0 @@ -version: 1 - -# In systemd's journal, loglevel is implicitly stored, so let's omit it -# from the message text. -formatters: - journal_fmt: - format: '%(name)s: [%(request)s] %(message)s' - -filters: - context: - (): synapse.util.logcontext.LoggingContextFilter - request: "" - -handlers: - journal: - class: systemd.journal.JournalHandler - formatter: journal_fmt - filters: [context] - SYSLOG_IDENTIFIER: synapse - -root: - level: INFO - handlers: [journal] - -disable_existing_loggers: False diff --git a/nixpkgs/nixos/modules/services/matrix/synapse.nix b/nixpkgs/nixos/modules/services/matrix/synapse.nix index ef69a8adebb0..5cce36f41e50 100644 --- a/nixpkgs/nixos/modules/services/matrix/synapse.nix +++ b/nixpkgs/nixos/modules/services/matrix/synapse.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.matrix-synapse; - format = pkgs.formats.yaml {}; + format = pkgs.formats.yaml { }; # remove null values from the final configuration finalSettings = lib.filterAttrsRecursive (_: v: v != null) cfg.settings; @@ -13,27 +13,28 @@ let usePostgresql = cfg.settings.database.name == "psycopg2"; hasLocalPostgresDB = let args = cfg.settings.database.args; in usePostgresql && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ])); + hasWorkers = cfg.workers != { }; + + listenerSupportsResource = resource: listener: + lib.any ({ names, ... }: builtins.elem resource names) listener.resources; + + clientListener = findFirst + (listenerSupportsResource "client") + null + (cfg.settings.listeners + ++ concatMap ({ worker_listeners, ... }: worker_listeners) (attrValues cfg.workers)); registerNewMatrixUser = let - isIpv6 = x: lib.length (lib.splitString ":" x) > 1; - listener = - lib.findFirst ( - listener: lib.any ( - resource: lib.any ( - name: name == "client" - ) resource.names - ) listener.resources - ) (lib.last cfg.settings.listeners) cfg.settings.listeners; - # FIXME: Handle cases with missing client listener properly, - # don't rely on lib.last, this will not work. + isIpv6 = hasInfix ":"; # add a tail, so that without any bind_addresses we still have a useable address - bindAddress = head (listener.bind_addresses ++ [ "127.0.0.1" ]); - listenerProtocol = if listener.tls + bindAddress = head (clientListener.bind_addresses ++ [ "127.0.0.1" ]); + listenerProtocol = if clientListener.tls then "https" else "http"; in + assert assertMsg (clientListener != null) "No client listener found in synapse or one of its workers"; pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" '' exec ${cfg.package}/bin/register_new_matrix_user \ $@ \ @@ -43,7 +44,7 @@ let "[${bindAddress}]" else "${bindAddress}" - }:${builtins.toString listener.port}/" + }:${builtins.toString clientListener.port}/" ''; defaultExtras = [ @@ -68,6 +69,48 @@ let extras = wantedExtras; inherit (cfg) plugins; }; + + logConfig = logName: { + version = 1; + formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s"; + handlers.journal = { + class = "systemd.journal.JournalHandler"; + formatter = "journal_fmt"; + SYSLOG_IDENTIFIER = logName; + }; + root = { + level = "INFO"; + handlers = [ "journal" ]; + }; + disable_existing_loggers = false; + }; + logConfigText = logName: + let + expr = '' + { + version = 1; + formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s"; + handlers.journal = { + class = "systemd.journal.JournalHandler"; + formatter = "journal_fmt"; + SYSLOG_IDENTIFIER = "${logName}"; + }; + root = { + level = "INFO"; + handlers = [ "journal" ]; + }; + disable_existing_loggers = false; + }; + ''; + in + lib.literalMD '' + Path to a yaml file generated from this Nix expression: + + ``` + ${expr} + ``` + ''; + genLogConfigFile = logName: format.generate "synapse-log-${logName}.yaml" (logConfig logName); in { imports = [ @@ -154,7 +197,108 @@ in { ]; - options = { + options = let + listenerType = workerContext: types.submodule { + options = { + port = mkOption { + type = types.port; + example = 8448; + description = lib.mdDoc '' + The port to listen for HTTP(S) requests on. + ''; + }; + + bind_addresses = mkOption { + type = types.listOf types.str; + default = [ + "::1" + "127.0.0.1" + ]; + example = literalExpression '' + [ + "::" + "0.0.0.0" + ] + ''; + description = lib.mdDoc '' + IP addresses to bind the listener to. + ''; + }; + + type = mkOption { + type = types.enum [ + "http" + "manhole" + "metrics" + "replication" + ]; + default = "http"; + example = "metrics"; + description = lib.mdDoc '' + The type of the listener, usually http. + ''; + }; + + tls = mkOption { + type = types.bool; + default = !workerContext; + example = false; + description = lib.mdDoc '' + Whether to enable TLS on the listener socket. + ''; + }; + + x_forwarded = mkOption { + type = types.bool; + default = false; + example = true; + description = lib.mdDoc '' + Use the X-Forwarded-For (XFF) header as the client IP and not the + actual client IP. + ''; + }; + + resources = mkOption { + type = types.listOf (types.submodule { + options = { + names = mkOption { + type = types.listOf (types.enum [ + "client" + "consent" + "federation" + "health" + "keys" + "media" + "metrics" + "openid" + "replication" + "static" + ]); + description = lib.mdDoc '' + List of resources to host on this listener. + ''; + example = [ + "client" + ]; + }; + compress = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Whether synapse should compress HTTP responses to clients that support it. + This should be disabled if running synapse behind a load balancer + that can do automatic compression. + ''; + }; + }; + }); + description = lib.mdDoc '' + List of HTTP resources to serve on this listener. + ''; + }; + }; + }; + in { services.matrix-synapse = { enable = mkEnableOption (lib.mdDoc "matrix.org synapse"); @@ -251,7 +395,7 @@ in { }; settings = mkOption { - default = {}; + default = { }; description = mdDoc '' The primary synapse configuration. See the [sample configuration](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_config.yaml) @@ -346,8 +490,8 @@ in { log_config = mkOption { type = types.path; - default = ./synapse-log_config.yaml; - defaultText = lib.literalExpression "nixos/modules/services/matrix/synapse-log_config.yaml"; + default = genLogConfigFile "synapse"; + defaultText = logConfigText "synapse"; description = lib.mdDoc '' The file that holds the logging configuration. ''; @@ -409,120 +553,37 @@ in { }; listeners = mkOption { - type = types.listOf (types.submodule { - options = { - port = mkOption { - type = types.port; - example = 8448; - description = lib.mdDoc '' - The port to listen for HTTP(S) requests on. - ''; - }; - - bind_addresses = mkOption { - type = types.listOf types.str; - default = [ - "::1" - "127.0.0.1" - ]; - example = literalExpression '' - [ - "::" - "0.0.0.0" - ] - ''; - description = lib.mdDoc '' - IP addresses to bind the listener to. - ''; - }; - - type = mkOption { - type = types.enum [ - "http" - "manhole" - "metrics" - "replication" - ]; - default = "http"; - example = "metrics"; - description = lib.mdDoc '' - The type of the listener, usually http. - ''; - }; - - tls = mkOption { - type = types.bool; - default = true; - example = false; - description = lib.mdDoc '' - Whether to enable TLS on the listener socket. - ''; - }; - - x_forwarded = mkOption { - type = types.bool; - default = false; - example = true; - description = lib.mdDoc '' - Use the X-Forwarded-For (XFF) header as the client IP and not the - actual client IP. - ''; - }; - - resources = mkOption { - type = types.listOf (types.submodule { - options = { - names = mkOption { - type = types.listOf (types.enum [ - "client" - "consent" - "federation" - "keys" - "media" - "metrics" - "openid" - "replication" - "static" - ]); - description = lib.mdDoc '' - List of resources to host on this listener. - ''; - example = [ - "client" - ]; - }; - compress = mkOption { - type = types.bool; - description = lib.mdDoc '' - Should synapse compress HTTP responses to clients that support it? - This should be disabled if running synapse behind a load balancer - that can do automatic compression. - ''; - }; - }; - }); - description = lib.mdDoc '' - List of HTTP resources to serve on this listener. - ''; - }; - }; - }); - default = [ { + type = types.listOf (listenerType false); + default = [{ port = 8008; bind_addresses = [ "127.0.0.1" ]; type = "http"; tls = false; x_forwarded = true; - resources = [ { + resources = [{ names = [ "client" ]; compress = true; } { names = [ "federation" ]; compress = false; - } ]; - } ]; + }]; + }] ++ lib.optional hasWorkers { + port = 9093; + bind_addresses = [ "127.0.0.1" ]; + type = "http"; + tls = false; + x_forwarded = false; + resources = [{ + names = [ "replication" ]; + compress = false; + }]; + }; description = lib.mdDoc '' List of ports that Synapse should listen on, their purpose and their configuration. + + By default, synapse will be configured for client and federation traffic on port 8008, and + for worker replication traffic on port 9093. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers) + for more details. ''; }; @@ -534,7 +595,7 @@ in { default = if versionAtLeast config.system.stateVersion "18.03" then "psycopg2" else "sqlite3"; - defaultText = literalExpression '' + defaultText = literalExpression '' if versionAtLeast config.system.stateVersion "18.03" then "psycopg2" else "sqlite3" @@ -551,10 +612,10 @@ in { psycopg2 = "matrix-synapse"; }.${cfg.settings.database.name}; defaultText = literalExpression '' - { - sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db"; - psycopg2 = "matrix-synapse"; - }.''${${options.services.matrix-synapse.settings}.database.name}; + { + sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db"; + psycopg2 = "matrix-synapse"; + }.''${${options.services.matrix-synapse.settings}.database.name}; ''; description = lib.mdDoc '' Name of the database when using the psycopg2 backend, @@ -622,7 +683,7 @@ in { url_preview_ip_range_whitelist = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; description = lib.mdDoc '' List of IP address CIDR ranges that the URL preview spider is allowed to access even if they are specified in url_preview_ip_range_blacklist. @@ -630,8 +691,27 @@ in { }; url_preview_url_blacklist = mkOption { - type = types.listOf types.str; - default = []; + # FIXME revert to just `listOf (attrsOf str)` after some time(tm). + type = types.listOf ( + types.coercedTo + types.str + (const (throw '' + Setting `config.services.matrix-synapse.settings.url_preview_url_blacklist` + to a list of strings has never worked. Due to a bug, this was the type accepted + by the module, but in practice it broke on runtime and as a result, no URL + preview worked anywhere if this was set. + + See https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#url_preview_url_blacklist + on how to configure it properly. + '')) + (types.attrsOf types.str)); + default = [ ]; + example = literalExpression '' + [ + { scheme = "http"; } # no http previews + { netloc = "www.acme.com"; path = "/foo"; } # block http(s)://www.acme.com/foo + ] + ''; description = lib.mdDoc '' Optional list of URL matches that the URL preview spider is denied from accessing. @@ -671,7 +751,7 @@ in { turn_uris = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; example = [ "turn:turn.example.com:3487?transport=udp" "turn:turn.example.com:3487?transport=tcp" @@ -708,12 +788,12 @@ in { }; }; }); - default = [ { + default = [{ server_name = "matrix.org"; verify_keys = { "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; }; - } ]; + }]; description = lib.mdDoc '' The trusted servers to download signing keys from. ''; @@ -727,13 +807,114 @@ in { ''; }; + redis = lib.mkOption { + type = types.submodule { + freeformType = format.type; + options = { + enabled = lib.mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to use redis support + ''; + }; + }; + }; + default = { }; + description = lib.mdDoc '' + Redis configuration for synapse. + + See the + [upstream documentation](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/usage/configuration/config_documentation.md#redis) + for available options. + ''; + }; }; }; }; + workers = lib.mkOption { + default = { }; + description = lib.mdDoc '' + Options for configuring workers. Worker support will be enabled if at least one worker is configured here. + + See the [worker documention](https://matrix-org.github.io/synapse/latest/workers.html#worker-configuration) + for possible options for each worker. Worker-specific options overriding the shared homeserver configuration can be + specified here for each worker. + + ::: {.note} + Worker support will add a replication listener on port 9093 to the main synapse process using the default + value of [`services.matrix-synapse.settings.listeners`](#opt-services.matrix-synapse.settings.listeners) and configure that + listener as `services.matrix-synapse.settings.instance_map.main`. + If you set either of those options, make sure to configure a replication listener yourself. + + A redis server is required for running workers. A local one can be enabled + using [`services.matrix-synapse.configureRedisLocally`](#opt-services.matrix-synapse.configureRedisLocally). + + Workers also require a proper reverse proxy setup to direct incoming requests to the appropriate process. See + the [reverse proxy documentation](https://matrix-org.github.io/synapse/latest/reverse_proxy.html) for a + general reverse proxying setup and + the [worker documentation](https://matrix-org.github.io/synapse/latest/workers.html#available-worker-applications) + for the available endpoints per worker application. + ::: + ''; + type = types.attrsOf (types.submodule ({name, ...}: { + freeformType = format.type; + options = { + worker_app = lib.mkOption { + type = types.enum [ + "synapse.app.generic_worker" + "synapse.app.media_repository" + ]; + description = "Type of this worker"; + default = "synapse.app.generic_worker"; + }; + worker_listeners = lib.mkOption { + default = [ ]; + type = types.listOf (listenerType true); + description = lib.mdDoc '' + List of ports that this worker should listen on, their purpose and their configuration. + ''; + }; + worker_log_config = lib.mkOption { + type = types.path; + default = genLogConfigFile "synapse-${name}"; + defaultText = logConfigText "synapse-${name}"; + description = lib.mdDoc '' + The file for log configuration. + + See the [python documentation](https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema) + for the schema and the [upstream repository](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_log_config.yaml) + for an example. + ''; + }; + }; + })); + default = { }; + example = lib.literalExpression '' + { + "federation_sender" = { }; + "federation_receiver" = { + worker_listeners = [ + { + type = "http"; + port = 8009; + bind_addresses = [ "127.0.0.1" ]; + tls = false; + x_forwarded = true; + resources = [{ + names = [ "federation" ]; + }]; + } + ]; + }; + } + ''; + }; + extraConfigFiles = mkOption { type = types.listOf types.path; - default = []; + default = [ ]; description = lib.mdDoc '' Extra config files to include. @@ -743,12 +924,28 @@ in { NixOps is in use. ''; }; + + configureRedisLocally = lib.mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to automatically configure a local redis server for matrix-synapse. + ''; + }; }; }; config = mkIf cfg.enable { assertions = [ - { assertion = hasLocalPostgresDB -> config.services.postgresql.enable; + { + assertion = clientListener != null; + message = '' + At least one listener which serves the `client` resource via HTTP is required + by synapse in `services.matrix-synapse.settings.listeners` or in one of the workers! + ''; + } + { + assertion = hasLocalPostgresDB -> config.services.postgresql.enable; message = '' Cannot deploy matrix-synapse with a configuration for a local postgresql database and a missing postgresql service. Since 20.03 it's mandatory to manually configure the @@ -764,8 +961,47 @@ in { For further information about this update, please read the release-notes of 20.03 carefully. ''; } + { + assertion = hasWorkers -> cfg.settings.redis.enabled; + message = '' + Workers for matrix-synapse require configuring a redis instance. This can be done + automatically by setting `services.matrix-synapse.configureRedisLocally = true`. + ''; + } + { + assertion = + let + main = cfg.settings.instance_map.main; + listener = lib.findFirst + ( + listener: + listener.port == main.port + && listenerSupportsResource "replication" listener + && (lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses) + ) + null + cfg.settings.listeners; + in + hasWorkers -> (cfg.settings.instance_map ? main && listener != null); + message = '' + Workers for matrix-synapse require setting `services.matrix-synapse.settings.instance_map.main` + to any listener configured in `services.matrix-synapse.settings.listeners` with a `"replication"` + resource. + + This is done by default unless you manually configure either of those settings. + ''; + } ]; + services.matrix-synapse.settings.redis = lib.mkIf cfg.configureRedisLocally { + enabled = true; + path = config.services.redis.servers.matrix-synapse.unixSocket; + }; + services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (lib.mkDefault { + host = "127.0.0.1"; + port = 9093; + }); + services.matrix-synapse.configFile = configFile; services.matrix-synapse.package = wrapped; @@ -784,64 +1020,124 @@ in { gid = config.ids.gids.matrix-synapse; }; - systemd.services.matrix-synapse = { - description = "Synapse Matrix homeserver"; + systemd.targets.matrix-synapse = lib.mkIf hasWorkers { + description = "Synapse Matrix parent target"; after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service"; wantedBy = [ "multi-user.target" ]; - preStart = '' - ${cfg.package}/bin/synapse_homeserver \ - --config-path ${configFile} \ - --keys-directory ${cfg.dataDir} \ - --generate-keys - ''; - environment = optionalAttrs (cfg.withJemalloc) { - LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so"; - }; - serviceConfig = { - Type = "notify"; - User = "matrix-synapse"; - Group = "matrix-synapse"; - WorkingDirectory = cfg.dataDir; - ExecStartPre = [ ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" '' - chown matrix-synapse:matrix-synapse ${cfg.settings.signing_key_path} - chmod 0600 ${cfg.settings.signing_key_path} - '')) ]; - ExecStart = '' - ${cfg.package}/bin/synapse_homeserver \ - ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) } - --keys-directory ${cfg.dataDir} - ''; - ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; - Restart = "on-failure"; - UMask = "0077"; - - # Security Hardening - # Refer to systemd.exec(5) for option descriptions. - CapabilityBoundingSet = [ "" ]; - LockPersonality = true; - NoNewPrivileges = true; - PrivateDevices = true; - PrivateTmp = true; - PrivateUsers = true; - ProcSubset = "pid"; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectProc = "invisible"; - ProtectSystem = "strict"; - ReadWritePaths = [ cfg.dataDir ]; - RemoveIPC = true; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; - }; + }; + + systemd.services = + let + targetConfig = + if hasWorkers + then { + partOf = [ "matrix-synapse.target" ]; + wantedBy = [ "matrix-synapse.target" ]; + unitConfig.ReloadPropagatedFrom = "matrix-synapse.target"; + } + else { + after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service"; + wantedBy = [ "multi-user.target" ]; + }; + baseServiceConfig = { + environment = optionalAttrs (cfg.withJemalloc) { + LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so"; + }; + serviceConfig = { + Type = "notify"; + User = "matrix-synapse"; + Group = "matrix-synapse"; + WorkingDirectory = cfg.dataDir; + ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; + Restart = "on-failure"; + UMask = "0077"; + + # Security Hardening + # Refer to systemd.exec(5) for option descriptions. + CapabilityBoundingSet = [ "" ]; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ReadWritePaths = [ cfg.dataDir ]; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; + }; + } + // targetConfig; + genWorkerService = name: workerCfg: + let + finalWorkerCfg = workerCfg // { worker_name = name; }; + workerConfigFile = format.generate "worker-${name}.yaml" finalWorkerCfg; + in + { + name = "matrix-synapse-worker-${name}"; + value = lib.mkMerge [ + baseServiceConfig + { + description = "Synapse Matrix worker ${name}"; + # make sure the main process starts first for potential database migrations + after = [ "matrix-synapse.service" ]; + requires = [ "matrix-synapse.service" ]; + serviceConfig = { + ExecStart = '' + ${cfg.package}/bin/synapse_worker \ + ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile workerConfigFile ] ++ cfg.extraConfigFiles) } + --keys-directory ${cfg.dataDir} + ''; + }; + } + ]; + }; + in + { + matrix-synapse = lib.mkMerge [ + baseServiceConfig + { + description = "Synapse Matrix homeserver"; + preStart = '' + ${cfg.package}/bin/synapse_homeserver \ + --config-path ${configFile} \ + --keys-directory ${cfg.dataDir} \ + --generate-keys + ''; + serviceConfig = { + ExecStartPre = [ + ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" '' + chown matrix-synapse:matrix-synapse ${cfg.settings.signing_key_path} + chmod 0600 ${cfg.settings.signing_key_path} + '')) + ]; + ExecStart = '' + ${cfg.package}/bin/synapse_homeserver \ + ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) } + --keys-directory ${cfg.dataDir} + ''; + }; + } + ]; + } + // (lib.mapAttrs' genWorkerService cfg.workers); + + services.redis.servers.matrix-synapse = lib.mkIf cfg.configureRedisLocally { + enable = true; + user = "matrix-synapse"; }; environment.systemPackages = [ registerNewMatrixUser ]; diff --git a/nixpkgs/nixos/modules/services/misc/jellyfin.nix b/nixpkgs/nixos/modules/services/misc/jellyfin.nix index 2a4483199d7d..43fdc09f4559 100644 --- a/nixpkgs/nixos/modules/services/misc/jellyfin.nix +++ b/nixpkgs/nixos/modules/services/misc/jellyfin.nix @@ -46,7 +46,8 @@ in config = mkIf cfg.enable { systemd.services.jellyfin = { description = "Jellyfin Media Server"; - after = [ "network.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; # This is mostly follows: https://github.com/jellyfin/jellyfin/blob/master/fedora/jellyfin.service diff --git a/nixpkgs/nixos/modules/services/monitoring/below.nix b/nixpkgs/nixos/modules/services/monitoring/below.nix index 92ee3882cac8..4a7135162ac4 100644 --- a/nixpkgs/nixos/modules/services/monitoring/below.nix +++ b/nixpkgs/nixos/modules/services/monitoring/below.nix @@ -103,4 +103,6 @@ in { }; }; }; + + meta.maintainers = with lib.maintainers; [ nicoo ]; } diff --git a/nixpkgs/nixos/modules/services/monitoring/grafana.nix b/nixpkgs/nixos/modules/services/monitoring/grafana.nix index 571b9a3aeebd..e90a0e9d16db 100644 --- a/nixpkgs/nixos/modules/services/monitoring/grafana.nix +++ b/nixpkgs/nixos/modules/services/monitoring/grafana.nix @@ -88,26 +88,6 @@ let # Get a submodule without any embedded metadata: _filter = x: filterAttrs (k: v: k != "_module") x; - # FIXME(@Ma27) remove before 23.05. This is just a helper-type - # because `mkRenamedOptionModule` doesn't work if `foo.bar` is renamed - # to `foo.bar.baz`. - submodule' = module: types.coercedTo - (mkOptionType { - name = "grafana-provision-submodule"; - description = "Wrapper-type for backwards compat of Grafana's declarative provisioning"; - check = x: - if builtins.isList x then - throw '' - Provisioning dashboards and datasources declaratively by - setting `dashboards` or `datasources` to a list is not supported - anymore. Use `services.grafana.provision.datasources.settings.datasources` - (or `services.grafana.provision.dashboards.settings.providers`) instead. - '' - else isAttrs x || isFunction x; - }) - id - (types.submodule module); - # http://docs.grafana.org/administration/provisioning/#datasources grafanaTypes.datasourceConfig = types.submodule { freeformType = provisioningSettingsFormat.type; @@ -1160,7 +1140,7 @@ in Declaratively provision Grafana's datasources. ''; default = { }; - type = submodule' { + type = types.submodule { options.settings = mkOption { description = lib.mdDoc '' Grafana datasource configuration in Nix. Can't be used with @@ -1235,7 +1215,7 @@ in Declaratively provision Grafana's dashboards. ''; default = { }; - type = submodule' { + type = types.submodule { options.settings = mkOption { description = lib.mdDoc '' Grafana dashboard configuration in Nix. Can't be used with diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix index 8bb017894ee2..66aff30b5ed1 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -304,6 +304,14 @@ in 'services.mysql.enable' is set to false. ''; } { + assertion = cfg.nextcloud.enable -> ( + (cfg.nextcloud.passwordFile == null) != (cfg.nextcloud.tokenFile == null) + ); + message = '' + Please specify either 'services.prometheus.exporters.nextcloud.passwordFile' or + 'services.prometheus.exporters.nextcloud.tokenFile' + ''; + } { assertion = cfg.sql.enable -> ( (cfg.sql.configFile == null) != (cfg.sql.configuration == null) ); diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix index 66eaed51d2ea..407bff1d62de 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix @@ -21,7 +21,7 @@ let throw "${logPrefix}: configuration file must not reside within /tmp - it won't be visible to the systemd service." else - true; + file; checkConfig = file: pkgs.runCommand "checked-blackbox-exporter.conf" { preferLocalBuild = true; diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix index 28add020f5cc..28a3eb6a134c 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix @@ -23,10 +23,12 @@ in description = lib.mdDoc '' Username for connecting to Nextcloud. Note that this account needs to have admin privileges in Nextcloud. + Unused when using token authentication. ''; }; passwordFile = mkOption { - type = types.path; + type = types.nullOr types.path; + default = null; example = "/path/to/password-file"; description = lib.mdDoc '' File containing the password for connecting to Nextcloud. @@ -34,9 +36,9 @@ in ''; }; tokenFile = mkOption { - type = types.path; + type = types.nullOr types.path; + default = null; example = "/path/to/token-file"; - default = ""; description = lib.mdDoc '' File containing the token for connecting to Nextcloud. Make sure that this file is readable by the exporter user. @@ -58,12 +60,13 @@ in --addr ${cfg.listenAddress}:${toString cfg.port} \ --timeout ${cfg.timeout} \ --server ${cfg.url} \ - ${if cfg.tokenFile == "" then '' + ${if cfg.passwordFile != null then '' --username ${cfg.username} \ --password ${escapeShellArg "@${cfg.passwordFile}"} \ - '' else '' + '' else '' --auth-token ${escapeShellArg "@${cfg.tokenFile}"} \ - ''} ${concatStringsSep " \\\n " cfg.extraFlags}''; + ''} \ + ${concatStringsSep " \\\n " cfg.extraFlags}''; }; }; } diff --git a/nixpkgs/nixos/modules/services/networking/frp.nix b/nixpkgs/nixos/modules/services/networking/frp.nix new file mode 100644 index 000000000000..e4f9a220b5e8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/frp.nix @@ -0,0 +1,93 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.frp; + settingsFormat = pkgs.formats.ini { }; + configFile = settingsFormat.generate "frp.ini" cfg.settings; + isClient = (cfg.role == "client"); + isServer = (cfg.role == "server"); +in +{ + options = { + services.frp = { + enable = mkEnableOption (mdDoc "frp"); + + package = mkPackageOptionMD pkgs "frp" { }; + + role = mkOption { + type = types.enum [ "server" "client" ]; + description = mdDoc '' + The frp consists of `client` and `server`. The server is usually + deployed on the machine with a public IP address, and + the client is usually deployed on the machine + where the Intranet service to be penetrated resides. + ''; + }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + description = mdDoc '' + Frp configuration, for configuration options + see the example of [client](https://github.com/fatedier/frp/blob/dev/conf/frpc_legacy_full.ini) + or [server](https://github.com/fatedier/frp/blob/dev/conf/frps_legacy_full.ini) on github. + ''; + example = literalExpression '' + { + common = { + server_addr = "x.x.x.x"; + server_port = 7000; + }; + } + ''; + }; + }; + }; + + config = + let + serviceCapability = optionals isServer [ "CAP_NET_BIND_SERVICE" ]; + executableFile = if isClient then "frpc" else "frps"; + in + mkIf cfg.enable { + systemd.services = { + frp = { + wants = optionals isClient [ "network-online.target" ]; + after = if isClient then [ "network-online.target" ] else [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + description = "A fast reverse proxy frp ${cfg.role}"; + serviceConfig = { + Type = "simple"; + Restart = "on-failure"; + RestartSec = 15; + ExecStart = "${cfg.package}/bin/${executableFile} -c ${configFile}"; + StateDirectoryMode = optionalString isServer "0700"; + DynamicUser = true; + # Hardening + UMask = optionalString isServer "0007"; + CapabilityBoundingSet = serviceCapability; + AmbientCapabilities = serviceCapability; + PrivateDevices = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ] ++ optionals isClient [ "AF_UNIX" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + PrivateMounts = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" ]; + }; + }; + }; + }; + + meta.maintainers = with maintainers; [ zaldnoay ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/frr.nix b/nixpkgs/nixos/modules/services/networking/frr.nix index d350fe3548ae..8488a4e4ef48 100644 --- a/nixpkgs/nixos/modules/services/networking/frr.nix +++ b/nixpkgs/nixos/modules/services/networking/frr.nix @@ -23,6 +23,7 @@ let "pbr" "bfd" "fabric" + "mgmt" ]; allServices = services ++ [ "zebra" ]; diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix index 6bc46a9a90e4..53c847ee3ca2 100644 --- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix +++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix @@ -30,13 +30,11 @@ let configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [ (mkSection "main" { plugins = "keyfile"; - dhcp = cfg.dhcp; - dns = cfg.dns; + inherit (cfg) dhcp dns; # If resolvconf is disabled that means that resolv.conf is managed by some other module. rc-manager = if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"; - firewall-backend = cfg.firewallBackend; }) (mkSection "keyfile" { unmanaged-devices = @@ -233,15 +231,6 @@ in ''; }; - firewallBackend = mkOption { - type = types.enum [ "iptables" "nftables" "none" ]; - default = "iptables"; - description = lib.mdDoc '' - Which firewall backend should be used for configuring masquerading with shared mode. - If set to none, NetworkManager doesn't manage the configuration at all. - ''; - }; - logLevel = mkOption { type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ]; default = "WARN"; @@ -340,20 +329,20 @@ in default = [ ]; example = literalExpression '' [ { - source = pkgs.writeText "upHook" ''' - - if [ "$2" != "up" ]; then - logger "exit: event $2 != up" - exit - fi - - # coreutils and iproute are in PATH too - logger "Device $DEVICE_IFACE coming up" - '''; - type = "basic"; - } ]''; + source = pkgs.writeText "upHook" ''' + if [ "$2" != "up" ]; then + logger "exit: event $2 != up" + exit + fi + + # coreutils and iproute are in PATH too + logger "Device $DEVICE_IFACE coming up" + '''; + type = "basic"; + } ] + ''; description = lib.mdDoc '' - A list of scripts which will be executed in response to network events. + A list of scripts which will be executed in response to network events. ''; }; @@ -413,6 +402,9 @@ in them via the DNS server in your network, or use environment.etc to add a file into /etc/NetworkManager/dnsmasq.d reconfiguring hostsdir. '') + (mkRemovedOptionModule [ "networking" "networkmanager" "firewallBackend" ] '' + This option was removed as NixOS is now using iptables-nftables-compat even when using iptables, therefore Networkmanager now uses the nftables backend unconditionally. + '') ]; diff --git a/nixpkgs/nixos/modules/services/networking/nftables.nix b/nixpkgs/nixos/modules/services/networking/nftables.nix index 47159ade328c..a0afdb452752 100644 --- a/nixpkgs/nixos/modules/services/networking/nftables.nix +++ b/nixpkgs/nixos/modules/services/networking/nftables.nix @@ -248,7 +248,6 @@ in config = mkIf cfg.enable { boot.blacklistedKernelModules = [ "ip_tables" ]; environment.systemPackages = [ pkgs.nftables ]; - networking.networkmanager.firewallBackend = mkDefault "nftables"; # versionOlder for backportability, remove afterwards networking.nftables.flushRuleset = mkDefault (versionOlder config.system.stateVersion "23.11" || (cfg.rulesetFile != null || cfg.ruleset != "")); systemd.services.nftables = { diff --git a/nixpkgs/nixos/modules/services/networking/tedicross.nix b/nixpkgs/nixos/modules/services/networking/tedicross.nix deleted file mode 100644 index cee7e11f4fb1..000000000000 --- a/nixpkgs/nixos/modules/services/networking/tedicross.nix +++ /dev/null @@ -1,100 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - dataDir = "/var/lib/tedicross"; - cfg = config.services.tedicross; - configJSON = pkgs.writeText "tedicross-settings.json" (builtins.toJSON cfg.config); - configYAML = pkgs.runCommand "tedicross-settings.yaml" { preferLocalBuild = true; } '' - ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out - ''; - -in { - options = { - services.tedicross = { - enable = mkEnableOption (lib.mdDoc "the TediCross Telegram-Discord bridge service"); - - config = mkOption { - type = types.attrs; - # from https://github.com/TediCross/TediCross/blob/master/example.settings.yaml - example = literalExpression '' - { - telegram = { - useFirstNameInsteadOfUsername = false; - colonAfterSenderName = false; - skipOldMessages = true; - sendEmojiWithStickers = true; - }; - discord = { - useNickname = false; - skipOldMessages = true; - displayTelegramReplies = "embed"; - replyLength = 100; - }; - bridges = [ - { - name = "Default bridge"; - direction = "both"; - telegram = { - chatId = -123456789; - relayJoinMessages = true; - relayLeaveMessages = true; - sendUsernames = true; - ignoreCommands = true; - }; - discord = { - serverId = "DISCORD_SERVER_ID"; - channelId = "DISCORD_CHANNEL_ID"; - relayJoinMessages = true; - relayLeaveMessages = true; - sendUsernames = true; - crossDeleteOnTelegram = true; - }; - } - ]; - - debug = false; - } - ''; - description = lib.mdDoc '' - {file}`settings.yaml` configuration as a Nix attribute set. - Secret tokens should be specified using {option}`environmentFile` - instead of this world-readable file. - ''; - }; - - environmentFile = mkOption { - type = types.nullOr types.path; - default = null; - description = lib.mdDoc '' - File containing environment variables to be passed to the TediCross service, - in which secret tokens can be specified securely using the - `TELEGRAM_BOT_TOKEN` and `DISCORD_BOT_TOKEN` - keys. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - # from https://github.com/TediCross/TediCross/blob/master/guides/autostart/Linux.md - systemd.services.tedicross = { - description = "TediCross Telegram-Discord bridge service"; - wantedBy = [ "multi-user.target" ]; - wants = [ "network-online.target" ]; - after = [ "network-online.target" ]; - serviceConfig = { - Type = "simple"; - ExecStart = "${pkgs.nodePackages.tedicross}/bin/tedicross --config='${configYAML}' --data-dir='${dataDir}'"; - Restart = "always"; - DynamicUser = true; - StateDirectory = baseNameOf dataDir; - EnvironmentFile = cfg.environmentFile; - }; - }; - }; - - meta.maintainers = with maintainers; [ pacien ]; -} - diff --git a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix index d22e6b5b40cd..0517615a4c6a 100644 --- a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix +++ b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix @@ -60,10 +60,8 @@ in { config = mkOption { type = attrsOf (nullOr (oneOf [ bool int str ])); default = { - config = { - ROCKET_ADDRESS = "::1"; # default to localhost - ROCKET_PORT = 8222; - }; + ROCKET_ADDRESS = "::1"; # default to localhost + ROCKET_PORT = 8222; }; example = literalExpression '' { diff --git a/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix b/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix index 143decfc0917..80567db10c97 100644 --- a/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix +++ b/nixpkgs/nixos/modules/services/web-apps/calibre-web.nix @@ -10,6 +10,8 @@ in services.calibre-web = { enable = mkEnableOption (lib.mdDoc "Calibre-Web"); + package = lib.mkPackageOption pkgs "calibre-web" { }; + listen = { ip = mkOption { type = types.str; @@ -73,6 +75,8 @@ in ''; }; + enableKepubify = mkEnableOption (lib.mdDoc "kebup conversion support"); + enableBookUploading = mkOption { type = types.bool; default = false; @@ -106,7 +110,7 @@ in systemd.services.calibre-web = let appDb = "/var/lib/${cfg.dataDir}/app.db"; gdriveDb = "/var/lib/${cfg.dataDir}/gdrive.db"; - calibreWebCmd = "${pkgs.calibre-web}/bin/calibre-web -p ${appDb} -g ${gdriveDb}"; + calibreWebCmd = "${cfg.package}/bin/calibre-web -p ${appDb} -g ${gdriveDb}"; settings = concatStringsSep ", " ( [ @@ -117,6 +121,7 @@ in ] ++ optional (cfg.options.calibreLibrary != null) "config_calibre_dir = '${cfg.options.calibreLibrary}'" ++ optional cfg.options.enableBookConversion "config_converterpath = '${pkgs.calibre}/bin/ebook-convert'" + ++ optional cfg.options.enableKepubify "config_kepubifypath = '${pkgs.kepubify}/bin/kepubify'" ); in { diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix index 4b308d2ee56e..e2d5cdc4f7c7 100644 --- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix +++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix @@ -248,11 +248,10 @@ in { # setup ${cfg.package}/createdb.sh ${cfg.package}/migrate.sh + export IP_GEOLOCATION_DB=${pkgs.dbip-country-lite}/share/dbip/dbip-country-lite.mmdb ${cfg.package}/bin/plausible eval "(Plausible.Release.prepare() ; Plausible.Auth.create_user(\"$ADMIN_USER_NAME\", \"$ADMIN_USER_EMAIL\", \"$ADMIN_USER_PWD\"))" ${optionalString cfg.adminUser.activate '' - if ! ${cfg.package}/init-admin.sh | grep 'already exists'; then - psql -d plausible <<< "UPDATE users SET email_verified=true;" - fi + psql -d plausible <<< "UPDATE users SET email_verified=true where email = '$ADMIN_USER_EMAIL';" ''} exec plausible start diff --git a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix index 8bc8e8c29259..6b1d4da532bf 100644 --- a/nixpkgs/nixos/modules/services/web-apps/vikunja.nix +++ b/nixpkgs/nixos/modules/services/web-apps/vikunja.nix @@ -147,5 +147,9 @@ in { }; environment.etc."vikunja/config.yaml".source = configFile; + + environment.systemPackages = [ + cfg.package-api # for admin `vikunja` CLI + ]; }; } diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix index cec0b379f67a..ce74e243a181 100644 --- a/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix +++ b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix @@ -36,6 +36,7 @@ let ${cfg.globalConfig} } ${cfg.extraConfig} + ${concatMapStringsSep "\n" mkVHostConf virtualHosts} ''; Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" { nativeBuildInputs = [ cfg.package ]; } '' @@ -340,7 +341,6 @@ in groups = config.users.groups; }) acmeHosts; - services.caddy.extraConfig = concatMapStringsSep "\n" mkVHostConf virtualHosts; services.caddy.globalConfig = '' ${optionalString (cfg.email != null) "email ${cfg.email}"} ${optionalString (cfg.acmeCA != null) "acme_ca ${cfg.acmeCA}"} diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix index 15a510fd8f96..282a34f6b011 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -172,24 +172,19 @@ in (mkIf (cfg.enable || cfg.mobile.enable || cfg.bigscreen.enable) { security.wrappers = { - kscreenlocker_greet = { - setuid = true; + kwin_wayland = { owner = "root"; group = "root"; - source = "${getBin libsForQt5.kscreenlocker}/libexec/kscreenlocker_greet"; + capabilities = "cap_sys_nice+ep"; + source = "${getBin plasma5.kwin}/bin/kwin_wayland"; }; + } // mkIf (!cfg.runUsingSystemd) { start_kdeinit = { setuid = true; owner = "root"; group = "root"; source = "${getBin libsForQt5.kinit}/libexec/kf5/start_kdeinit"; }; - kwin_wayland = { - owner = "root"; - group = "root"; - capabilities = "cap_sys_nice+ep"; - source = "${getBin plasma5.kwin}/bin/kwin_wayland"; - }; }; environment.systemPackages = diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix index e6923bcbb56c..400e5601dc59 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix @@ -97,6 +97,19 @@ in type = types.bool; }; + banner = mkOption { + type = types.nullOr types.lines; + default = null; + example = '' + foo + bar + baz + ''; + description = lib.mdDoc '' + Optional message to display on the login screen. + ''; + }; + settings = mkOption { type = settingsFormat.type; default = { }; @@ -238,6 +251,11 @@ in sleep-inactive-ac-timeout = lib.gvariant.mkInt32 0; sleep-inactive-battery-timeout = lib.gvariant.mkInt32 0; }; + }] ++ lib.optionals (cfg.gdm.banner != null) [{ + settings."org/gnome/login-screen" = { + banner-message-enable = true; + banner-message-text = cfg.gdm.banner; + }; }] ++ [ "${gdm}/share/gdm/greeter-dconf-defaults" ]; # Use AutomaticLogin if delay is zero, because it's immediate. |