diff options
Diffstat (limited to 'nixos/modules/services')
25 files changed, 420 insertions, 224 deletions
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix index 393fe83f493f..6f4455d3be60 100644 --- a/nixos/modules/services/backup/borgbackup.nix +++ b/nixos/modules/services/backup/borgbackup.nix @@ -143,20 +143,15 @@ let }; # Paths listed in ReadWritePaths must exist before service is started - mkActivationScript = name: cfg: + mkTmpfiles = name: cfg: let - install = "install -o ${cfg.user} -g ${cfg.group}"; - in - nameValuePair "borgbackup-job-${name}" (stringAfter [ "users" ] ('' - # Ensure that the home directory already exists - # We can't assert createHome == true because that's not the case for root - cd "${config.users.users.${cfg.user}.home}" - # Create each directory separately to prevent root owned parent dirs - ${install} -d .config .config/borg - ${install} -d .cache .cache/borg - '' + optionalString (isLocalPath cfg.repo && !cfg.removableDevice) '' - ${install} -d ${escapeShellArg cfg.repo} - '')); + settings = { inherit (cfg) user group; }; + in lib.nameValuePair "borgbackup-job-${name}" ({ + "${config.users.users."${cfg.user}".home}/.config/borg".d = settings; + "${config.users.users."${cfg.user}".home}/.cache/borg".d = settings; + } // optionalAttrs (isLocalPath cfg.repo && !cfg.removableDevice) { + "${cfg.repo}".d = settings; + }); mkPassAssertion = name: cfg: { assertion = with cfg.encryption; @@ -760,7 +755,7 @@ in { ++ mapAttrsToList mkSourceAssertions jobs ++ mapAttrsToList mkRemovableDeviceAssertions jobs; - system.activationScripts = mapAttrs' mkActivationScript jobs; + systemd.tmpfiles.settings = mapAttrs' mkTmpfiles jobs; systemd.services = # A job named "foo" is mapped to systemd.services.borgbackup-job-foo diff --git a/nixos/modules/services/databases/aerospike.nix b/nixos/modules/services/databases/aerospike.nix index 373c8f4bffb0..4923c0f00ddb 100644 --- a/nixos/modules/services/databases/aerospike.nix +++ b/nixos/modules/services/databases/aerospike.nix @@ -108,6 +108,11 @@ in }; users.groups.aerospike.gid = config.ids.gids.aerospike; + boot.kernel.sysctl = { + "net.core.rmem_max" = mkDefault 15728640; + "net.core.wmem_max" = mkDefault 5242880; + }; + systemd.services.aerospike = rec { description = "Aerospike server"; @@ -131,14 +136,6 @@ in echo "kernel.shmmax too low, setting to 1GB" ${pkgs.procps}/bin/sysctl -w kernel.shmmax=1073741824 fi - if [ $(echo "$(cat /proc/sys/net/core/rmem_max) < 15728640" | ${pkgs.bc}/bin/bc) == "1" ]; then - echo "increasing socket buffer limit (/proc/sys/net/core/rmem_max): $(cat /proc/sys/net/core/rmem_max) -> 15728640" - echo 15728640 > /proc/sys/net/core/rmem_max - fi - if [ $(echo "$(cat /proc/sys/net/core/wmem_max) < 5242880" | ${pkgs.bc}/bin/bc) == "1" ]; then - echo "increasing socket buffer limit (/proc/sys/net/core/wmem_max): $(cat /proc/sys/net/core/wmem_max) -> 5242880" - echo 5242880 > /proc/sys/net/core/wmem_max - fi install -d -m0700 -o ${serviceConfig.User} -g ${serviceConfig.Group} "${cfg.workDir}" install -d -m0700 -o ${serviceConfig.User} -g ${serviceConfig.Group} "${cfg.workDir}/smd" install -d -m0700 -o ${serviceConfig.User} -g ${serviceConfig.Group} "${cfg.workDir}/udf" diff --git a/nixos/modules/services/hardware/kanata.nix b/nixos/modules/services/hardware/kanata.nix index 0b77bfbc33b3..05e76d843215 100644 --- a/nixos/modules/services/hardware/kanata.nix +++ b/nixos/modules/services/hardware/kanata.nix @@ -78,7 +78,13 @@ let mkName = name: "kanata-${name}"; mkDevices = devices: - optionalString ((length devices) > 0) "linux-dev ${concatStringsSep ":" devices}"; + let + devicesString = pipe devices [ + (map (device: "\"" + device + "\"")) + (concatStringsSep " ") + ]; + in + optionalString ((length devices) > 0) "linux-dev (${devicesString})"; mkConfig = name: keyboard: pkgs.writeText "${mkName name}-config.kdb" '' (defcfg diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix index 8408844c4f94..8f64afe60734 100644 --- a/nixos/modules/services/hardware/sane.nix +++ b/nixos/modules/services/hardware/sane.nix @@ -4,7 +4,7 @@ with lib; let - pkg = pkgs.sane-backends.override { + pkg = config.hardware.sane.backends-package.override { scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable; scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package; }; @@ -57,6 +57,13 @@ in ''; }; + hardware.sane.backends-package = mkOption { + type = types.package; + default = pkgs.sane-backends; + defaultText = literalExpression "pkgs.sane-backends"; + description = lib.mdDoc "Backends driver package to use."; + }; + hardware.sane.snapshot = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/services/hardware/thermald.nix b/nixos/modules/services/hardware/thermald.nix index 7ae602823cd6..a4839f326cc4 100644 --- a/nixos/modules/services/hardware/thermald.nix +++ b/nixos/modules/services/hardware/thermald.nix @@ -19,6 +19,12 @@ in ''; }; + ignoreCpuidCheck = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to ignore the cpuid check to allow running on unsupported platforms"; + }; + configFile = mkOption { type = types.nullOr types.path; default = null; @@ -42,6 +48,7 @@ in ${cfg.package}/sbin/thermald \ --no-daemon \ ${optionalString cfg.debug "--loglevel=debug"} \ + ${optionalString cfg.ignoreCpuidCheck "--ignore-cpuid-check"} \ ${optionalString (cfg.configFile != null) "--config-file ${cfg.configFile}"} \ --dbus-enable \ --adaptive diff --git a/nixos/modules/services/hardware/vdr.nix b/nixos/modules/services/hardware/vdr.nix index afa64fa16c4a..5feb379b50d1 100644 --- a/nixos/modules/services/hardware/vdr.nix +++ b/nixos/modules/services/hardware/vdr.nix @@ -45,6 +45,7 @@ in { systemd.services.vdr = { description = "VDR"; wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; serviceConfig = { ExecStart = '' ${cfg.package}/bin/vdr \ diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix index 8a277cea6e46..5d87fc87d416 100644 --- a/nixos/modules/services/logging/logcheck.nix +++ b/nixos/modules/services/logging/logcheck.nix @@ -220,10 +220,16 @@ in logcheck = {}; }; - system.activationScripts.logcheck = '' - mkdir -m 700 -p /var/{lib,lock}/logcheck - chown ${cfg.user} /var/{lib,lock}/logcheck - ''; + systemd.tmpfiles.settings.logcheck = { + "/var/lib/logcheck".d = { + mode = "700"; + inherit (cfg) user; + }; + "/var/lock/logcheck".d = { + mode = "700"; + inherit (cfg) user; + }; + }; services.cron.systemCronJobs = let withTime = name: {timeArgs, ...}: timeArgs != null; diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix index e8b5f832e66e..209e066a19ef 100644 --- a/nixos/modules/services/mail/postfix.nix +++ b/nixos/modules/services/mail/postfix.nix @@ -747,7 +747,7 @@ in ${concatStringsSep "\n" (mapAttrsToList (to: from: '' ln -sf ${from} /var/lib/postfix/conf/${to} - ${pkgs.postfix}/bin/postalias /var/lib/postfix/conf/${to} + ${pkgs.postfix}/bin/postalias -o -p /var/lib/postfix/conf/${to} '') cfg.aliasFiles)} ${concatStringsSep "\n" (mapAttrsToList (to: from: '' ln -sf ${from} /var/lib/postfix/conf/${to} diff --git a/nixos/modules/services/matrix/matrix-sliding-sync.nix b/nixos/modules/services/matrix/matrix-sliding-sync.nix index 295be0c6bf16..8b22cd7dba80 100644 --- a/nixos/modules/services/matrix/matrix-sliding-sync.nix +++ b/nixos/modules/services/matrix/matrix-sliding-sync.nix @@ -1,10 +1,14 @@ { config, lib, pkgs, ... }: let - cfg = config.services.matrix-synapse.sliding-sync; + cfg = config.services.matrix-sliding-sync; in { - options.services.matrix-synapse.sliding-sync = { + imports = [ + (lib.mkRenamedOptionModule [ "services" "matrix-synapse" "sliding-sync" ] [ "services" "matrix-sliding-sync" ]) + ]; + + options.services.matrix-sliding-sync = { enable = lib.mkEnableOption (lib.mdDoc "sliding sync"); package = lib.mkPackageOption pkgs "matrix-sliding-sync" { }; @@ -83,6 +87,7 @@ in systemd.services.matrix-sliding-sync = rec { after = lib.optional cfg.createDatabase "postgresql.service" + ++ lib.optional config.services.dendrite.enable "dendrite.service" ++ lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit; wants = after; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/misc/guix/default.nix b/nixos/modules/services/misc/guix/default.nix index 2bfa3b77971f..7174ff36b709 100644 --- a/nixos/modules/services/misc/guix/default.nix +++ b/nixos/modules/services/misc/guix/default.nix @@ -22,11 +22,19 @@ let }) (builtins.genList guixBuildUser numberOfUsers)); - # A set of Guix user profiles to be linked at activation. + # A set of Guix user profiles to be linked at activation. All of these should + # be default profiles managed by Guix CLI and the profiles are located in + # `${cfg.stateDir}/profiles/per-user/$USER/$PROFILE`. guixUserProfiles = { - # The current Guix profile that is created through `guix pull`. + # The default Guix profile managed by `guix pull`. Take note this should be + # the profile with the most precedence in `PATH` env to let users use their + # updated versions of `guix` CLI. "current-guix" = "\${XDG_CONFIG_HOME}/guix/current"; + # The default Guix home profile. This profile contains more than exports + # such as an activation script at `$GUIX_HOME_PROFILE/activate`. + "guix-home" = "$HOME/.guix-home/profile"; + # The default Guix profile similar to $HOME/.nix-profile from Nix. "guix-profile" = "$HOME/.guix-profile"; }; @@ -256,20 +264,31 @@ in # ephemeral setups where only certain part of the filesystem is # persistent (e.g., "Erase my darlings"-type of setup). system.userActivationScripts.guix-activate-user-profiles.text = let + guixProfile = profile: "${cfg.stateDir}/guix/profiles/per-user/\${USER}/${profile}"; + linkProfile = profile: location: let + userProfile = guixProfile profile; + in '' + [ -d "${userProfile}" ] && ln -sfn "${userProfile}" "${location}" + ''; linkProfileToPath = acc: profile: location: let - guixProfile = "${cfg.stateDir}/guix/profiles/per-user/\${USER}/${profile}"; - in acc + '' - [ -d "${guixProfile}" ] && [ -L "${location}" ] || ln -sf "${guixProfile}" "${location}" - ''; + in acc + (linkProfile profile location); - activationScript = lib.foldlAttrs linkProfileToPath "" guixUserProfiles; + # This should contain export-only Guix user profiles. The rest of it is + # handled manually in the activation script. + guixUserProfiles' = lib.attrsets.removeAttrs guixUserProfiles [ "guix-home" ]; + + linkExportsScript = lib.foldlAttrs linkProfileToPath "" guixUserProfiles'; in '' # Don't export this please! It is only expected to be used for this # activation script and nothing else. XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config} # Linking the usual Guix profiles into the home directory. - ${activationScript} + ${linkExportsScript} + + # Activate all of the default Guix non-exports profiles manually. + ${linkProfile "guix-home" "$HOME/.guix-home"} + [ -L "$HOME/.guix-home" ] && "$HOME/.guix-home/activate" ''; # GUIX_LOCPATH is basically LOCPATH but for Guix libc which in turn used by diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix new file mode 100644 index 000000000000..9794bbbec464 --- /dev/null +++ b/nixos/modules/services/misc/ollama.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: let + + cfg = config.services.ollama; + +in { + + options = { + services.ollama = { + enable = lib.mkEnableOption ( + lib.mdDoc "Server for local large language models" + ); + package = lib.mkPackageOption pkgs "ollama" { }; + }; + }; + + config = lib.mkIf cfg.enable { + + systemd = { + services.ollama = { + wantedBy = [ "multi-user.target" ]; + description = "Server for local large language models"; + after = [ "network.target" ]; + environment = { + HOME = "%S/ollama"; + OLLAMA_MODELS = "%S/ollama/models"; + }; + serviceConfig = { + ExecStart = "${lib.getExe cfg.package} serve"; + WorkingDirectory = "/var/lib/ollama"; + StateDirectory = [ "ollama" ]; + DynamicUser = true; + }; + }; + }; + + environment.systemPackages = [ cfg.package ]; + + }; + + meta.maintainers = with lib.maintainers; [ onny ]; + +} diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix index 3299b6404c2b..7036a372d1ea 100644 --- a/nixos/modules/services/misc/portunus.nix +++ b/nixos/modules/services/misc/portunus.nix @@ -102,7 +102,9 @@ in ldap = { package = mkOption { type = types.package; - # needs openldap built with a libxcrypt that support crypt sha256 until https://github.com/majewsky/portunus/issues/2 is solved + # needs openldap built with a libxcrypt that support crypt sha256 until users have had time to migrate to newer hashes + # Ref: <https://github.com/majewsky/portunus/issues/2> + # TODO: remove in NixOS 24.11 (cf. same note on pkgs/servers/portunus/default.nix) default = pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }; defaultText = lib.literalExpression "pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }"; description = lib.mdDoc "The OpenLDAP package to use."; @@ -247,6 +249,7 @@ in acmeDirectory = config.security.acme.certs."${cfg.domain}".directory; in { + PORTUNUS_SERVER_HTTP_SECURE = "true"; PORTUNUS_SLAPD_TLS_CA_CERTIFICATE = "/etc/ssl/certs/ca-certificates.crt"; PORTUNUS_SLAPD_TLS_CERTIFICATE = "${acmeDirectory}/cert.pem"; PORTUNUS_SLAPD_TLS_DOMAIN_NAME = cfg.domain; diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix index b517170cda21..c1209e34a92b 100644 --- a/nixos/modules/services/misc/redmine.nix +++ b/nixos/modules/services/misc/redmine.nix @@ -53,7 +53,7 @@ in enable = mkEnableOption (lib.mdDoc "Redmine"); package = mkPackageOption pkgs "redmine" { - example = "redmine.override { ruby = pkgs.ruby_2_7; }"; + example = "redmine.override { ruby = pkgs.ruby_3_2; }"; }; user = mkOption { diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index 39abd293b2d1..35db8a7376b1 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -64,6 +64,7 @@ let "pgbouncer" "php-fpm" "pihole" + "ping" "postfix" "postgres" "process" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/ping.nix b/nixos/modules/services/monitoring/prometheus/exporters/ping.nix new file mode 100644 index 000000000000..af78b6bef625 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/ping.nix @@ -0,0 +1,48 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.ping; + + settingsFormat = pkgs.formats.yaml {}; + configFile = settingsFormat.generate "config.yml" cfg.settings; +in +{ + port = 9427; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + + settings = mkOption { + type = settingsFormat.type; + default = {}; + + description = lib.mdDoc '' + Configuration for ping_exporter, see + <https://github.com/czerwonk/ping_exporter> + for supported values. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + # ping-exporter needs `CAP_NET_RAW` to run as non root https://github.com/czerwonk/ping_exporter#running-as-non-root-user + CapabilityBoundingSet = [ "CAP_NET_RAW" ]; + AmbientCapabilities = [ "CAP_NET_RAW" ]; + ExecStart = '' + ${pkgs.prometheus-ping-exporter}/bin/ping_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --config.path="${configFile}" \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/thanos.nix b/nixos/modules/services/monitoring/thanos.nix index 5baa0d8446e5..02502816ef5d 100644 --- a/nixos/modules/services/monitoring/thanos.nix +++ b/nixos/modules/services/monitoring/thanos.nix @@ -394,9 +394,8 @@ let Maximum number of queries processed concurrently by query node. ''; - query.replica-labels = mkAttrsParam "query.replica-label" '' + query.replica-labels = mkListParam "query.replica-label" '' Labels to treat as a replica indicator along which data is - deduplicated. Still you will be able to query without deduplication using diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index f54ce5917438..39793922ab51 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -674,7 +674,11 @@ in (lport: "sshd -G -T -C lport=${toString lport} -f ${sshconf} > /dev/null") cfg.ports} ${concatMapStringsSep "\n" - (la: "sshd -G -T -C ${escapeShellArg "laddr=${la.addr},lport=${toString la.port}"} -f ${sshconf} > /dev/null") + (la: + concatMapStringsSep "\n" + (port: "sshd -G -T -C ${escapeShellArg "laddr=${la.addr},lport=${toString port}"} -f ${sshconf} > /dev/null") + (if la.port != null then [ la.port ] else cfg.ports) + ) cfg.listenAddresses} touch $out '') diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix index 3822df81063d..1070e4e25296 100644 --- a/nixos/modules/services/networking/tailscale.nix +++ b/nixos/modules/services/networking/tailscale.nix @@ -100,8 +100,8 @@ in { }; systemd.services.tailscaled-autoconnect = mkIf (cfg.authKeyFile != null) { - after = ["tailscale.service"]; - wants = ["tailscale.service"]; + after = ["tailscaled.service"]; + wants = ["tailscaled.service"]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; diff --git a/nixos/modules/services/networking/yggdrasil.nix b/nixos/modules/services/networking/yggdrasil.nix index 514753687d69..9173e7eb3457 100644 --- a/nixos/modules/services/networking/yggdrasil.nix +++ b/nixos/modules/services/networking/yggdrasil.nix @@ -137,16 +137,24 @@ in message = "networking.enableIPv6 must be true for yggdrasil to work"; }]; - system.activationScripts.yggdrasil = mkIf cfg.persistentKeys '' - if [ ! -e ${keysPath} ] - then - mkdir --mode=700 -p ${builtins.dirOf keysPath} - ${binYggdrasil} -genconf -json \ - | ${pkgs.jq}/bin/jq \ - 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ - > ${keysPath} - fi - ''; + # This needs to be a separate service. The yggdrasil service fails if + # this is put into its preStart. + systemd.services.yggdrasil-persistent-keys = lib.mkIf cfg.persistentKeys { + wantedBy = [ "multi-user.target" ]; + before = [ "yggdrasil.service" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = '' + if [ ! -e ${keysPath} ] + then + mkdir --mode=700 -p ${builtins.dirOf keysPath} + ${binYggdrasil} -genconf -json \ + | ${pkgs.jq}/bin/jq \ + 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ + > ${keysPath} + fi + ''; + }; systemd.services.yggdrasil = { description = "Yggdrasil Network Service"; diff --git a/nixos/modules/services/security/munge.nix b/nixos/modules/services/security/munge.nix index 4d6fe33f697b..9d306c205f94 100644 --- a/nixos/modules/services/security/munge.nix +++ b/nixos/modules/services/security/munge.nix @@ -45,19 +45,25 @@ in systemd.services.munged = { wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + wants = [ + "network-online.target" + "time-sync.target" + ]; + after = [ + "network-online.target" + "time-sync.target" + ]; path = [ pkgs.munge pkgs.coreutils ]; serviceConfig = { ExecStartPre = "+${pkgs.coreutils}/bin/chmod 0400 ${cfg.password}"; - ExecStart = "${pkgs.munge}/bin/munged --syslog --key-file ${cfg.password}"; - PIDFile = "/run/munge/munged.pid"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStart = "${pkgs.munge}/bin/munged --foreground --key-file ${cfg.password}"; User = "munge"; Group = "munge"; StateDirectory = "munge"; StateDirectoryMode = "0711"; + Restart = "on-failure"; RuntimeDirectory = "munge"; }; diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix index 4ff941251c99..dea20dec1ab4 100644 --- a/nixos/modules/services/security/tor.nix +++ b/nixos/modules/services/security/tor.nix @@ -854,7 +854,7 @@ in BridgeRelay = true; ExtORPort.port = mkDefault "auto"; ServerTransportPlugin.transports = mkDefault ["obfs4"]; - ServerTransportPlugin.exec = mkDefault "${pkgs.obfs4}/bin/obfs4proxy managed"; + ServerTransportPlugin.exec = mkDefault "${lib.getExe pkgs.obfs4} managed"; } // optionalAttrs (cfg.relay.role == "private-bridge") { ExtraInfoStatistics = false; PublishServerDescriptor = false; diff --git a/nixos/modules/services/system/cachix-watch-store.nix b/nixos/modules/services/system/cachix-watch-store.nix index 992a59cbc075..8aa5f0358fa9 100644 --- a/nixos/modules/services/system/cachix-watch-store.nix +++ b/nixos/modules/services/system/cachix-watch-store.nix @@ -23,6 +23,14 @@ in ''; }; + signingKeyFile = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc '' + Optional file containing a self-managed signing key to sign uploaded store paths. + ''; + default = null; + }; + compressionLevel = mkOption { type = types.nullOr types.int; description = lib.mdDoc "The compression level for ZSTD compression (between 0 and 16)"; @@ -69,7 +77,8 @@ in DynamicUser = true; LoadCredential = [ "cachix-token:${toString cfg.cachixTokenFile}" - ]; + ] + ++ lib.optional (cfg.signingKeyFile != null) "signing-key:${toString cfg.signingKeyFile}"; }; script = let @@ -80,6 +89,7 @@ in in '' export CACHIX_AUTH_TOKEN="$(<"$CREDENTIALS_DIRECTORY/cachix-token")" + ${lib.optionalString (cfg.signingKeyFile != null) ''export CACHIX_SIGNING_KEY="$(<"$CREDENTIALS_DIRECTORY/signing-key")"''} ${lib.escapeShellArgs command} ''; }; diff --git a/nixos/modules/services/web-apps/nextcloud.md b/nixos/modules/services/web-apps/nextcloud.md index b10fd566abb3..ce8f96a6a389 100644 --- a/nixos/modules/services/web-apps/nextcloud.md +++ b/nixos/modules/services/web-apps/nextcloud.md @@ -51,7 +51,7 @@ to ensure that changes can be applied by changing the module's options. In case the application serves multiple domains (those are checked with [`$_SERVER['HTTP_HOST']`](https://www.php.net/manual/en/reserved.variables.server.php)) it's needed to add them to -[`services.nextcloud.config.extraTrustedDomains`](#opt-services.nextcloud.config.extraTrustedDomains). +[`services.nextcloud.extraOptions.trusted_domains`](#opt-services.nextcloud.extraOptions.trusted_domains). Auto updates for Nextcloud apps can be enabled using [`services.nextcloud.autoUpdateApps`](#opt-services.nextcloud.autoUpdateApps.enable). diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 501df47942e5..50d4d7ce4a1a 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -9,6 +9,7 @@ let jsonFormat = pkgs.formats.json {}; defaultPHPSettings = { + output_buffering = "0"; short_open_tag = "Off"; expose_php = "Off"; error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT"; @@ -23,6 +24,43 @@ let catch_workers_output = "yes"; }; + appStores = { + # default apps bundled with pkgs.nextcloudXX, e.g. files, contacts + apps = { + enabled = true; + writable = false; + }; + # apps installed via cfg.extraApps + nix-apps = { + enabled = cfg.extraApps != { }; + linkTarget = pkgs.linkFarm "nix-apps" + (mapAttrsToList (name: path: { inherit name path; }) cfg.extraApps); + writable = false; + }; + # apps installed via the app store. + store-apps = { + enabled = cfg.appstoreEnable == null || cfg.appstoreEnable; + linkTarget = "${cfg.home}/store-apps"; + writable = true; + }; + }; + + webroot = pkgs.runCommand + "${cfg.package.name or "nextcloud"}-with-apps" + { } + '' + mkdir $out + ln -sfv "${cfg.package}"/* "$out" + ${concatStrings + (mapAttrsToList (name: store: optionalString (store.enabled && store?linkTarget) '' + if [ -e "$out"/${name} ]; then + echo "Didn't expect ${name} already in $out!" + exit 1 + fi + ln -sfTv ${store.linkTarget} "$out"/${name} + '') appStores)} + ''; + inherit (cfg) datadir; phpPackage = cfg.phpPackage.buildEnv { @@ -45,7 +83,7 @@ let occ = pkgs.writeScriptBin "nextcloud-occ" '' #! ${pkgs.runtimeShell} - cd ${cfg.package} + cd ${webroot} sudo=exec if [[ "$USER" != nextcloud ]]; then sudo='exec /run/wrappers/bin/sudo -u nextcloud --preserve-env=NEXTCLOUD_CONFIG_DIR --preserve-env=OC_PASS' @@ -94,6 +132,22 @@ in { (mkRemovedOptionModule [ "services" "nextcloud" "disableImagemagick" ] '' Use services.nextcloud.enableImagemagick instead. '') + (mkRenamedOptionModule + [ "services" "nextcloud" "logLevel" ] [ "services" "nextcloud" "extraOptions" "loglevel" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "logType" ] [ "services" "nextcloud" "extraOptions" "log_type" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "defaultPhoneRegion" ] [ "services" "nextcloud" "extraOptions" "default_phone_region" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "overwriteProtocol" ] [ "services" "nextcloud" "extraOptions" "overwriteprotocol" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "skeletonDirectory" ] [ "services" "nextcloud" "extraOptions" "skeletondirectory" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "globalProfiles" ] [ "services" "nextcloud" "extraOptions" "profile.enabled" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "extraTrustedDomains" ] [ "services" "nextcloud" "extraOptions" "trusted_domains" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "trustedProxies" ] [ "services" "nextcloud" "extraOptions" "trusted_proxies" ]) ]; options.services.nextcloud = { @@ -157,32 +211,6 @@ in { Set this to false to disable the installation of apps from the global appstore. App management is always enabled regardless of this setting. ''; }; - logLevel = mkOption { - type = types.ints.between 0 4; - default = 2; - description = lib.mdDoc '' - Log level value between 0 (DEBUG) and 4 (FATAL). - - - 0 (debug): Log all activity. - - - 1 (info): Log activity such as user logins and file activities, plus warnings, errors, and fatal errors. - - - 2 (warn): Log successful operations, as well as warnings of potential problems, errors and fatal errors. - - - 3 (error): Log failed operations and fatal errors. - - - 4 (fatal): Log only fatal errors that cause the server to stop. - ''; - }; - logType = mkOption { - type = types.enum [ "errorlog" "file" "syslog" "systemd" ]; - default = "syslog"; - description = lib.mdDoc '' - Logging backend to use. - systemd requires the php-systemd package to be added to services.nextcloud.phpExtraExtensions. - See the [nextcloud documentation](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/logging_configuration.html) for details. - ''; - }; https = mkOption { type = types.bool; default = false; @@ -206,16 +234,6 @@ in { ''; }; - skeletonDirectory = mkOption { - default = ""; - type = types.str; - description = lib.mdDoc '' - The directory where the skeleton files are located. These files will be - copied to the data directory of new users. Leave empty to not copy any - skeleton files. - ''; - }; - webfinger = mkOption { type = types.bool; default = false; @@ -315,7 +333,6 @@ in { }; - config = { dbtype = mkOption { type = types.enum [ "sqlite" "pgsql" "mysql" ]; @@ -380,53 +397,6 @@ in { setup of Nextcloud by the systemd service `nextcloud-setup.service`. ''; }; - - extraTrustedDomains = mkOption { - type = types.listOf types.str; - default = []; - description = lib.mdDoc '' - Trusted domains from which the Nextcloud installation will be - accessible. You don't need to add - `services.nextcloud.hostname` here. - ''; - }; - - trustedProxies = mkOption { - type = types.listOf types.str; - default = []; - description = lib.mdDoc '' - Trusted proxies to provide if the Nextcloud installation is being - proxied to secure against, e.g. spoofing. - ''; - }; - - overwriteProtocol = mkOption { - type = types.nullOr (types.enum [ "http" "https" ]); - default = null; - example = "https"; - - description = lib.mdDoc '' - Force Nextcloud to always use HTTP or HTTPS i.e. for link generation. - Nextcloud uses the currently used protocol by default, but when - behind a reverse-proxy, it may use `http` for everything although - Nextcloud may be served via HTTPS. - ''; - }; - - defaultPhoneRegion = mkOption { - default = null; - type = types.nullOr types.str; - example = "DE"; - description = lib.mdDoc '' - An [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) - country code which replaces automatic phone-number detection - without a country code. - - As an example, with `DE` set as the default phone region, - the `+49` prefix can be omitted for phone numbers. - ''; - }; - objectstore = { s3 = { enable = mkEnableOption (lib.mdDoc '' @@ -609,30 +579,109 @@ in { The nextcloud-occ program preconfigured to target this Nextcloud instance. ''; }; - globalProfiles = mkEnableOption (lib.mdDoc "global profiles") // { - description = lib.mdDoc '' - Makes user-profiles globally available under `nextcloud.tld/u/user.name`. - Even though it's enabled by default in Nextcloud, it must be explicitly enabled - here because it has the side-effect that personal information is even accessible to - unauthenticated users by default. - - By default, the following properties are set to “Show to everyone” - if this flag is enabled: - - About - - Full name - - Headline - - Organisation - - Profile picture - - Role - - Twitter - - Website - - Only has an effect in Nextcloud 23 and later. - ''; - }; extraOptions = mkOption { - type = jsonFormat.type; + type = types.submodule { + freeformType = jsonFormat.type; + options = { + + loglevel = mkOption { + type = types.ints.between 0 4; + default = 2; + description = lib.mdDoc '' + Log level value between 0 (DEBUG) and 4 (FATAL). + + - 0 (debug): Log all activity. + + - 1 (info): Log activity such as user logins and file activities, plus warnings, errors, and fatal errors. + + - 2 (warn): Log successful operations, as well as warnings of potential problems, errors and fatal errors. + + - 3 (error): Log failed operations and fatal errors. + + - 4 (fatal): Log only fatal errors that cause the server to stop. + ''; + }; + log_type = mkOption { + type = types.enum [ "errorlog" "file" "syslog" "systemd" ]; + default = "syslog"; + description = lib.mdDoc '' + Logging backend to use. + systemd requires the php-systemd package to be added to services.nextcloud.phpExtraExtensions. + See the [nextcloud documentation](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/logging_configuration.html) for details. + ''; + }; + skeletondirectory = mkOption { + default = ""; + type = types.str; + description = lib.mdDoc '' + The directory where the skeleton files are located. These files will be + copied to the data directory of new users. Leave empty to not copy any + skeleton files. + ''; + }; + trusted_domains = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Trusted domains, from which the nextcloud installation will be + accessible. You don't need to add + `services.nextcloud.hostname` here. + ''; + }; + trusted_proxies = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Trusted proxies, to provide if the nextcloud installation is being + proxied to secure against e.g. spoofing. + ''; + }; + overwriteprotocol = mkOption { + type = types.enum [ "" "http" "https" ]; + default = ""; + example = "https"; + description = lib.mdDoc '' + Force Nextcloud to always use HTTP or HTTPS i.e. for link generation. + Nextcloud uses the currently used protocol by default, but when + behind a reverse-proxy, it may use `http` for everything although + Nextcloud may be served via HTTPS. + ''; + }; + default_phone_region = mkOption { + default = ""; + type = types.str; + example = "DE"; + description = lib.mdDoc '' + An [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) + country code which replaces automatic phone-number detection + without a country code. + + As an example, with `DE` set as the default phone region, + the `+49` prefix can be omitted for phone numbers. + ''; + }; + "profile.enabled" = mkEnableOption (lib.mdDoc "global profiles") // { + description = lib.mdDoc '' + Makes user-profiles globally available under `nextcloud.tld/u/user.name`. + Even though it's enabled by default in Nextcloud, it must be explicitly enabled + here because it has the side-effect that personal information is even accessible to + unauthenticated users by default. + By default, the following properties are set to “Show to everyone” + if this flag is enabled: + - About + - Full name + - Headline + - Organisation + - Profile picture + - Role + - Twitter + - Website + Only has an effect in Nextcloud 23 and later. + ''; + }; + }; + }; default = {}; description = lib.mdDoc '' Extra options which should be appended to Nextcloud's config.php file. @@ -766,11 +815,10 @@ in { # When upgrading the Nextcloud package, Nextcloud can report errors such as # "The files of the app [all apps in /var/lib/nextcloud/apps] were not replaced correctly" # Restarting phpfpm on Nextcloud package update fixes these issues (but this is a workaround). - phpfpm-nextcloud.restartTriggers = [ cfg.package ]; + phpfpm-nextcloud.restartTriggers = [ webroot ]; nextcloud-setup = let c = cfg.config; - writePhpArray = a: "[${concatMapStringsSep "," (val: ''"${toString val}"'') a}]"; requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable; objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable '' 'objectstore' => [ @@ -800,6 +848,10 @@ in { nextcloudGreaterOrEqualThan = req: versionAtLeast cfg.package.version req; + mkAppStoreConfig = name: { enabled, writable, ... }: optionalString enabled '' + [ 'path' => '${webroot}/${name}', 'url' => '/${name}', 'writable' => ${boolToString writable} ], + ''; + overrideConfig = pkgs.writeText "nextcloud-config.php" '' <?php ${optionalString requiresReadSecretFunction '' @@ -828,17 +880,10 @@ in { } $CONFIG = [ 'apps_paths' => [ - ${optionalString (cfg.extraApps != { }) "[ 'path' => '${cfg.home}/nix-apps', 'url' => '/nix-apps', 'writable' => false ],"} - [ 'path' => '${cfg.home}/apps', 'url' => '/apps', 'writable' => false ], - [ 'path' => '${cfg.home}/store-apps', 'url' => '/store-apps', 'writable' => true ], + ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)} ], ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"} - 'datadirectory' => '${datadir}/data', - 'skeletondirectory' => '${cfg.skeletonDirectory}', ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} - 'log_type' => '${cfg.logType}', - 'loglevel' => '${builtins.toString cfg.logLevel}', - ${optionalString (c.overwriteProtocol != null) "'overwriteprotocol' => '${c.overwriteProtocol}',"} ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} ${optionalString (c.dbport != null) "'dbport' => '${toString c.dbport}',"} @@ -851,10 +896,6 @@ in { '' } 'dbtype' => '${c.dbtype}', - 'trusted_domains' => ${writePhpArray ([ cfg.hostName ] ++ c.extraTrustedDomains)}, - 'trusted_proxies' => ${writePhpArray (c.trustedProxies)}, - ${optionalString (c.defaultPhoneRegion != null) "'default_phone_region' => '${c.defaultPhoneRegion}',"} - ${optionalString (nextcloudGreaterOrEqualThan "23") "'profile.enabled' => ${boolToString cfg.globalProfiles},"} ${objectstoreConfig} ]; @@ -907,7 +948,7 @@ in { (i: v: '' ${occ}/bin/nextcloud-occ config:system:set trusted_domains \ ${toString i} --value="${toString v}" - '') ([ cfg.hostName ] ++ cfg.config.extraTrustedDomains)); + '') ([ cfg.hostName ] ++ cfg.extraOptions.trusted_domains)); in { wantedBy = [ "multi-user.target" ]; @@ -935,17 +976,16 @@ in { exit 1 fi - ln -sf ${cfg.package}/apps ${cfg.home}/ - - # Install extra apps - ln -sfT \ - ${pkgs.linkFarm "nix-apps" - (mapAttrsToList (name: path: { inherit name path; }) cfg.extraApps)} \ - ${cfg.home}/nix-apps + ${concatMapStrings (name: '' + if [ -d "${cfg.home}"/${name} ]; then + echo "Cleaning up ${name}; these are now bundled in the webroot store-path!" + rm -r "${cfg.home}"/${name} + fi + '') [ "nix-apps" "apps" ]} # create nextcloud directories. # if the directories exist already with wrong permissions, we fix that - for dir in ${datadir}/config ${datadir}/data ${cfg.home}/store-apps ${cfg.home}/nix-apps; do + for dir in ${datadir}/config ${datadir}/data ${cfg.home}/store-apps; do if [ ! -e $dir ]; then install -o nextcloud -g nextcloud -d $dir elif [ $(stat -c "%G" $dir) != "nextcloud" ]; then @@ -982,7 +1022,7 @@ in { environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config"; serviceConfig.Type = "oneshot"; serviceConfig.User = "nextcloud"; - serviceConfig.ExecStart = "${phpPackage}/bin/php -f ${cfg.package}/cron.php"; + serviceConfig.ExecStart = "${phpPackage}/bin/php -f ${webroot}/cron.php"; }; nextcloud-update-plugins = mkIf cfg.autoUpdateApps.enable { after = [ "nextcloud-setup.service" ]; @@ -1043,22 +1083,25 @@ in { user = "nextcloud"; }; - services.nextcloud = lib.mkIf cfg.configureRedis { - caching.redis = true; - extraOptions = { + services.nextcloud = { + caching.redis = lib.mkIf cfg.configureRedis true; + extraOptions = mkMerge [({ + datadirectory = lib.mkDefault "${datadir}/data"; + trusted_domains = [ cfg.hostName ]; + }) (lib.mkIf cfg.configureRedis { "memcache.distributed" = ''\OC\Memcache\Redis''; "memcache.locking" = ''\OC\Memcache\Redis''; redis = { host = config.services.redis.servers.nextcloud.unixSocket; port = 0; }; - }; + })]; }; services.nginx.enable = mkDefault true; services.nginx.virtualHosts.${cfg.hostName} = { - root = cfg.package; + root = webroot; locations = { "= /robots.txt" = { priority = 100; @@ -1075,14 +1118,6 @@ in { } ''; }; - "~ ^/store-apps" = { - priority = 201; - extraConfig = "root ${cfg.home};"; - }; - "~ ^/nix-apps" = { - priority = 201; - extraConfig = "root ${cfg.home};"; - }; "^~ /.well-known" = { priority = 210; extraConfig = '' diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 6c08d0aee3d7..1285c2bbb916 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -352,10 +352,11 @@ let # The acme-challenge location doesn't need to be added if we are not using any automated # certificate provisioning and can also be omitted when we use a certificate obtained via a DNS-01 challenge - acmeLocation = optionalString (vhost.enableACME || (vhost.useACMEHost != null && config.security.acme.certs.${vhost.useACMEHost}.dnsProvider == null)) '' + acmeLocation = optionalString (vhost.enableACME || (vhost.useACMEHost != null && config.security.acme.certs.${vhost.useACMEHost}.dnsProvider == null)) # Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx) # We use ^~ here, so that we don't check any regexes (which could # otherwise easily override this intended match accidentally). + '' location ^~ /.well-known/acme-challenge/ { ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"} ${optionalString (vhost.acmeRoot != null) "root ${vhost.acmeRoot};"} @@ -375,10 +376,11 @@ let ${concatMapStringsSep "\n" listenString redirectListen} server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases}; - ${acmeLocation} + location / { return ${toString vhost.redirectCode} https://$host$request_uri; } + ${acmeLocation} } ''} @@ -392,13 +394,6 @@ let http3 ${if vhost.http3 then "on" else "off"}; http3_hq ${if vhost.http3_hq then "on" else "off"}; ''} - ${acmeLocation} - ${optionalString (vhost.root != null) "root ${vhost.root};"} - ${optionalString (vhost.globalRedirect != null) '' - location / { - return ${toString vhost.redirectCode} http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri; - } - ''} ${optionalString hasSSL '' ssl_certificate ${vhost.sslCertificate}; ssl_certificate_key ${vhost.sslCertificateKey}; @@ -421,6 +416,14 @@ let ${mkBasicAuth vhostName vhost} + ${optionalString (vhost.root != null) "root ${vhost.root};"} + + ${optionalString (vhost.globalRedirect != null) '' + location / { + return ${toString vhost.redirectCode} http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri; + } + ''} + ${acmeLocation} ${mkLocations vhost.locations} ${vhost.extraConfig} @@ -1130,14 +1133,6 @@ in } { - assertion = any (host: host.kTLS) (attrValues virtualHosts) -> versionAtLeast cfg.package.version "1.21.4"; - message = '' - services.nginx.virtualHosts.<name>.kTLS requires nginx version - 1.21.4 or above; see the documentation for services.nginx.package. - ''; - } - - { assertion = all (host: !(host.enableACME && host.useACMEHost != null)) (attrValues virtualHosts); message = '' Options services.nginx.service.virtualHosts.<name>.enableACME and @@ -1345,6 +1340,8 @@ in nginx.gid = config.ids.gids.nginx; }; + boot.kernelModules = optional (versionAtLeast config.boot.kernelPackages.kernel.version "4.17") "tls"; + # do not delete the default temp directories created upon nginx startup systemd.tmpfiles.rules = [ "X /tmp/systemd-private-%b-nginx.service-*/tmp/nginx_*" |