diff options
Diffstat (limited to 'nixos/modules')
99 files changed, 1028 insertions, 233 deletions
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix index bc6583442edf..a8476bd2aaed 100644 --- a/nixos/modules/config/shells-environment.nix +++ b/nixos/modules/config/shells-environment.nix @@ -214,7 +214,8 @@ in '' # Create the required /bin/sh symlink; otherwise lots of things # (notably the system() function) won't work. - mkdir -m 0755 -p /bin + mkdir -p /bin + chmod 0755 /bin ln -sfn "${cfg.binsh}" /bin/.sh.tmp mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh ''; diff --git a/nixos/modules/image/repart-image.nix b/nixos/modules/image/repart-image.nix index b4a1dfe51ff3..a12b4fb14fb1 100644 --- a/nixos/modules/image/repart-image.nix +++ b/nixos/modules/image/repart-image.nix @@ -10,6 +10,8 @@ , systemd , fakeroot , util-linux + + # filesystem tools , dosfstools , mtools , e2fsprogs @@ -18,8 +20,13 @@ , btrfs-progs , xfsprogs + # compression tools +, zstd +, xz + # arguments -, name +, imageFileBasename +, compression , fileSystems , partitions , split @@ -52,14 +59,25 @@ let }; fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems; + + compressionPkg = { + "zstd" = zstd; + "xz" = xz; + }."${compression.algorithm}"; + + compressionCommand = { + "zstd" = "zstd --no-progress --threads=0 -${toString compression.level}"; + "xz" = "xz --keep --verbose --threads=0 -${toString compression.level}"; + }."${compression.algorithm}"; in -runCommand name +runCommand imageFileBasename { nativeBuildInputs = [ systemd fakeroot util-linux + compressionPkg ] ++ fileSystemTools; } '' amendedRepartDefinitions=$(${amendRepartDefinitions} ${partitions} ${definitionsDirectory}) @@ -67,6 +85,7 @@ runCommand name mkdir -p $out cd $out + echo "Building image with systemd-repart..." unshare --map-root-user fakeroot systemd-repart \ --dry-run=no \ --empty=create \ @@ -75,6 +94,17 @@ runCommand name --definitions="$amendedRepartDefinitions" \ --split="${lib.boolToString split}" \ --json=pretty \ - image.raw \ + ${imageFileBasename}.raw \ | tee repart-output.json + + # Compression is implemented in the same derivation as opposed to in a + # separate derivation to allow users to save disk space. Disk images are + # already very space intensive so we want to allow users to mitigate this. + if ${lib.boolToString compression.enable}; then + for f in ${imageFileBasename}*; do + echo "Compressing $f with ${compression.algorithm}..." + # Keep the original file when compressing and only delete it afterwards + ${compressionCommand} $f && rm $f + done + fi '' diff --git a/nixos/modules/image/repart.nix b/nixos/modules/image/repart.nix index da4f45d9a639..ed584d9bf997 100644 --- a/nixos/modules/image/repart.nix +++ b/nixos/modules/image/repart.nix @@ -66,7 +66,53 @@ in name = lib.mkOption { type = lib.types.str; - description = lib.mdDoc "The name of the image."; + description = lib.mdDoc '' + Name of the image. + + If this option is unset but config.system.image.id is set, + config.system.image.id is used as the default value. + ''; + }; + + version = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = config.system.image.version; + defaultText = lib.literalExpression "config.system.image.version"; + description = lib.mdDoc "Version of the image"; + }; + + imageFileBasename = lib.mkOption { + type = lib.types.str; + readOnly = true; + description = lib.mdDoc '' + Basename of the image filename without any extension (e.g. `image_1`). + ''; + }; + + imageFile = lib.mkOption { + type = lib.types.str; + readOnly = true; + description = lib.mdDoc '' + Filename of the image including all extensions (e.g `image_1.raw` or + `image_1.raw.zst`). + ''; + }; + + compression = { + enable = lib.mkEnableOption (lib.mdDoc "Image compression"); + + algorithm = lib.mkOption { + type = lib.types.enum [ "zstd" "xz" ]; + default = "zstd"; + description = lib.mdDoc "Compression algorithm"; + }; + + level = lib.mkOption { + type = lib.types.int; + description = lib.mdDoc '' + Compression level. The available range depends on the used algorithm. + ''; + }; }; seed = lib.mkOption { @@ -131,6 +177,32 @@ in config = { + image.repart = + let + version = config.image.repart.version; + versionInfix = if version != null then "_${version}" else ""; + compressionSuffix = lib.optionalString cfg.compression.enable + { + "zstd" = ".zst"; + "xz" = ".xz"; + }."${cfg.compression.algorithm}"; + in + { + name = lib.mkIf (config.system.image.id != null) (lib.mkOptionDefault config.system.image.id); + imageFileBasename = cfg.name + versionInfix; + imageFile = cfg.imageFileBasename + ".raw" + compressionSuffix; + + compression = { + # Generally default to slightly faster than default compression + # levels under the assumption that most of the building will be done + # for development and release builds will be customized. + level = lib.mkOptionDefault { + "zstd" = 3; + "xz" = 3; + }."${cfg.compression.algorithm}"; + }; + }; + system.build.image = let fileSystems = lib.filter @@ -160,7 +232,7 @@ in in pkgs.callPackage ./repart-image.nix { systemd = cfg.package; - inherit (cfg) name split seed; + inherit (cfg) imageFileBasename compression split seed; inherit fileSystems definitionsDirectory partitions; }; diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index 45dbf45b3ae7..c929c3b37285 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -28,6 +28,8 @@ let DOCUMENTATION_URL = lib.optionalString (cfg.distroId == "nixos") "https://nixos.org/learn.html"; SUPPORT_URL = lib.optionalString (cfg.distroId == "nixos") "https://nixos.org/community.html"; BUG_REPORT_URL = lib.optionalString (cfg.distroId == "nixos") "https://github.com/NixOS/nixpkgs/issues"; + IMAGE_ID = lib.optionalString (config.system.image.id != null) config.system.image.id; + IMAGE_VERSION = lib.optionalString (config.system.image.version != null) config.system.image.version; } // lib.optionalAttrs (cfg.variant_id != null) { VARIANT_ID = cfg.variant_id; }; @@ -110,6 +112,38 @@ in example = "installer"; }; + image = { + + id = lib.mkOption { + type = types.nullOr (types.strMatching "^[a-z0-9._-]+$"); + default = null; + description = lib.mdDoc '' + Image identifier. + + This corresponds to the IMAGE_ID field in os-release. See the + upstream docs for more details on valid characters for this field: + https://www.freedesktop.org/software/systemd/man/latest/os-release.html#IMAGE_ID= + + You would only want to set this option if you're build NixOS appliance images. + ''; + }; + + version = lib.mkOption { + type = types.nullOr (types.strMatching "^[a-z0-9._-]+$"); + default = null; + description = lib.mdDoc '' + Image version. + + This corresponds to the IMAGE_VERSION field in os-release. See the + upstream docs for more details on valid characters for this field: + https://www.freedesktop.org/software/systemd/man/latest/os-release.html#IMAGE_VERSION= + + You would only want to set this option if you're build NixOS appliance images. + ''; + }; + + }; + stateVersion = mkOption { type = types.str; # TODO Remove this and drop the default of the option so people are forced to set it. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 698514410560..b31154b9624e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -849,6 +849,7 @@ ./services/monitoring/vmagent.nix ./services/monitoring/vmalert.nix ./services/monitoring/vnstat.nix + ./services/monitoring/watchdogd.nix ./services/monitoring/zabbix-agent.nix ./services/monitoring/zabbix-proxy.nix ./services/monitoring/zabbix-server.nix @@ -1339,6 +1340,7 @@ ./services/web-apps/restya-board.nix ./services/web-apps/rimgo.nix ./services/web-apps/sftpgo.nix + ./services/web-apps/suwayomi-server.nix ./services/web-apps/rss-bridge.nix ./services/web-apps/selfoss.nix ./services/web-apps/shiori.nix diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix index c39a3c8d509b..0c1461709c22 100644 --- a/nixos/modules/programs/ssh.nix +++ b/nixos/modules/programs/ssh.nix @@ -12,6 +12,7 @@ let '' #! ${pkgs.runtimeShell} -e export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')" + export XAUTHORITY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^XAUTHORITY=\(.*\)/\1/; t; d')" export WAYLAND_DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^WAYLAND_DISPLAY=\(.*\)/\1/; t; d')" exec ${cfg.askPassword} "$@" ''; diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix index 7cc302969fb6..40d9c487996b 100644 --- a/nixos/modules/security/acme/default.nix +++ b/nixos/modules/security/acme/default.nix @@ -897,10 +897,10 @@ in { certs = attrValues cfg.certs; in [ { - assertion = cfg.email != null || all (certOpts: certOpts.email != null) certs; + assertion = cfg.defaults.email != null || all (certOpts: certOpts.email != null) certs; message = '' You must define `security.acme.certs.<name>.email` or - `security.acme.email` to register with the CA. Note that using + `security.acme.defaults.email` to register with the CA. Note that using many different addresses for certs may trigger account rate limits. ''; } diff --git a/nixos/modules/services/audio/gmediarender.nix b/nixos/modules/services/audio/gmediarender.nix index 545f2b1a2b60..a4cb89098db7 100644 --- a/nixos/modules/services/audio/gmediarender.nix +++ b/nixos/modules/services/audio/gmediarender.nix @@ -64,6 +64,7 @@ in config = mkIf cfg.enable { systemd = { services.gmediarender = { + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; description = "gmediarender server daemon"; diff --git a/nixos/modules/services/audio/jmusicbot.nix b/nixos/modules/services/audio/jmusicbot.nix index fd1d4da19284..e7803677d0fd 100644 --- a/nixos/modules/services/audio/jmusicbot.nix +++ b/nixos/modules/services/audio/jmusicbot.nix @@ -26,6 +26,7 @@ in config = mkIf cfg.enable { systemd.services.jmusicbot = { wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; description = "Discord music bot that's easy to set up and run yourself!"; serviceConfig = mkMerge [{ diff --git a/nixos/modules/services/audio/spotifyd.nix b/nixos/modules/services/audio/spotifyd.nix index 975be5a87cba..1194b6f200d7 100644 --- a/nixos/modules/services/audio/spotifyd.nix +++ b/nixos/modules/services/audio/spotifyd.nix @@ -50,6 +50,7 @@ in systemd.services.spotifyd = { wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" "sound.target" ]; description = "spotifyd, a Spotify playing daemon"; environment.SHELL = "/bin/sh"; diff --git a/nixos/modules/services/audio/ympd.nix b/nixos/modules/services/audio/ympd.nix index b74cc3f9c0b4..6e8d22dab3c8 100644 --- a/nixos/modules/services/audio/ympd.nix +++ b/nixos/modules/services/audio/ympd.nix @@ -50,6 +50,7 @@ in { description = "Standalone MPD Web GUI written in C"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix index 446d19b8fd5a..c86cb81e5df4 100644 --- a/nixos/modules/services/continuous-integration/buildbot/master.nix +++ b/nixos/modules/services/continuous-integration/buildbot/master.nix @@ -268,6 +268,7 @@ in { systemd.services.buildbot-master = { description = "Buildbot Continuous Integration Server."; after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; path = cfg.packages ++ cfg.pythonPackages python.pkgs; environment.PYTHONPATH = "${python.withPackages (self: cfg.pythonPackages self ++ [ package ])}/${python.sitePackages}"; diff --git a/nixos/modules/services/continuous-integration/gitea-actions-runner.nix b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix index 3f2be9464849..06f0da3451a6 100644 --- a/nixos/modules/services/continuous-integration/gitea-actions-runner.nix +++ b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix @@ -188,6 +188,7 @@ in nameValuePair "gitea-runner-${escapeSystemdPath name}" { inherit (instance) enable; description = "Gitea Actions Runner"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ] ++ optionals (wantsDocker) [ diff --git a/nixos/modules/services/continuous-integration/github-runner/options.nix b/nixos/modules/services/continuous-integration/github-runner/options.nix index 2335826e8b66..b9b1ea05e967 100644 --- a/nixos/modules/services/continuous-integration/github-runner/options.nix +++ b/nixos/modules/services/continuous-integration/github-runner/options.nix @@ -153,6 +153,7 @@ with lib; type = types.attrs; description = lib.mdDoc '' Modify the systemd service. Can be used to, e.g., adjust the sandboxing options. + See {manpage}`systemd.exec(5)` for more options. ''; example = { ProtectHome = false; diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix index 46b03bba37be..54bbe69703f9 100644 --- a/nixos/modules/services/continuous-integration/hydra/default.nix +++ b/nixos/modules/services/continuous-integration/hydra/default.nix @@ -393,6 +393,7 @@ in systemd.services.hydra-evaluator = { wantedBy = [ "multi-user.target" ]; requires = [ "hydra-init.service" ]; + wants = [ "network-online.target" ]; after = [ "hydra-init.service" "network.target" "network-online.target" ]; path = with pkgs; [ hydra-package nettools jq ]; restartTriggers = [ hydraConf ]; diff --git a/nixos/modules/services/databases/firebird.nix b/nixos/modules/services/databases/firebird.nix index 36c12eaaf5f1..431233ce5ed4 100644 --- a/nixos/modules/services/databases/firebird.nix +++ b/nixos/modules/services/databases/firebird.nix @@ -143,7 +143,7 @@ in # ConnectionTimeout = 180 #RemoteServiceName = gds_db - RemoteServicePort = ${cfg.port} + RemoteServicePort = ${toString cfg.port} # randomly choose port for server Event Notification #RemoteAuxPort = 0 diff --git a/nixos/modules/services/databases/lldap.nix b/nixos/modules/services/databases/lldap.nix index d1574c98fe67..e821da8e58aa 100644 --- a/nixos/modules/services/databases/lldap.nix +++ b/nixos/modules/services/databases/lldap.nix @@ -104,6 +104,7 @@ in config = lib.mkIf cfg.enable { systemd.services.lldap = { description = "Lightweight LDAP server (lldap)"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix index a7a0909f55e1..df36e37976a4 100644 --- a/nixos/modules/services/databases/openldap.nix +++ b/nixos/modules/services/databases/openldap.nix @@ -294,6 +294,7 @@ in { "man:slapd-mdb" ]; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { User = cfg.user; diff --git a/nixos/modules/services/desktops/geoclue2.nix b/nixos/modules/services/desktops/geoclue2.nix index b04f46c26a56..2a68bb0b55f3 100644 --- a/nixos/modules/services/desktops/geoclue2.nix +++ b/nixos/modules/services/desktops/geoclue2.nix @@ -200,6 +200,7 @@ in }; systemd.services.geoclue = { + wants = lib.optionals cfg.enableWifi [ "network-online.target" ]; after = lib.optionals cfg.enableWifi [ "network-online.target" ]; # restart geoclue service when the configuration changes restartTriggers = [ @@ -217,6 +218,7 @@ in # we can't be part of a system service, and the agent should # be okay with the main service coming and going wantedBy = [ "default.target" ]; + wants = lib.optionals cfg.enableWifi [ "network-online.target" ]; after = lib.optionals cfg.enableWifi [ "network-online.target" ]; unitConfig.ConditionUser = "!@system"; serviceConfig = { diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix index 6f45be6640bc..ff6fd85d8a9b 100644 --- a/nixos/modules/services/editors/emacs.nix +++ b/nixos/modules/services/editors/emacs.nix @@ -15,25 +15,6 @@ let fi ''; - desktopApplicationFile = pkgs.writeTextFile { - name = "emacsclient.desktop"; - destination = "/share/applications/emacsclient.desktop"; - text = '' - [Desktop Entry] - Name=Emacsclient - GenericName=Text Editor - Comment=Edit text - MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++; - Exec=emacseditor %F - Icon=emacs - Type=Application - Terminal=false - Categories=Development;TextEditor; - StartupWMClass=Emacs - Keywords=Text;Editor; - ''; - }; - in { @@ -102,7 +83,7 @@ in wantedBy = if cfg.startWithGraphical then [ "graphical-session.target" ] else [ "default.target" ]; }; - environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ]; + environment.systemPackages = [ cfg.package editorScript ]; environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "emacseditor"); }; diff --git a/nixos/modules/services/home-automation/evcc.nix b/nixos/modules/services/home-automation/evcc.nix index d0ce3fb4a1ce..f360f525b04b 100644 --- a/nixos/modules/services/home-automation/evcc.nix +++ b/nixos/modules/services/home-automation/evcc.nix @@ -41,6 +41,7 @@ in config = mkIf cfg.enable { systemd.services.evcc = { + wants = [ "network-online.target" ]; after = [ "network-online.target" "mosquitto.target" diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix index bc470576b759..a01628968966 100644 --- a/nixos/modules/services/home-automation/home-assistant.nix +++ b/nixos/modules/services/home-automation/home-assistant.nix @@ -435,6 +435,7 @@ in { systemd.services.home-assistant = { description = "Home Assistant"; + wants = [ "network-online.target" ]; after = [ "network-online.target" diff --git a/nixos/modules/services/logging/journaldriver.nix b/nixos/modules/services/logging/journaldriver.nix index 59eedff90d60..4d21464018aa 100644 --- a/nixos/modules/services/logging/journaldriver.nix +++ b/nixos/modules/services/logging/journaldriver.nix @@ -84,6 +84,7 @@ in { systemd.services.journaldriver = { description = "Stackdriver Logging journal forwarder"; script = "${pkgs.journaldriver}/bin/journaldriver"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index 25c7017a1d25..79c8fec75252 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -119,9 +119,10 @@ let '' plugin { sieve_plugins = ${concatStringsSep " " cfg.sieve.plugins} - sieve_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions)} - sieve_global_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions)} '' + (optionalString (cfg.sieve.extensions != []) ''sieve_extensions = ${concatMapStringsSep " " (el: "+${el}") cfg.sieve.extensions}'') + (optionalString (cfg.sieve.globalExtensions != []) ''sieve_global_extensions = ${concatMapStringsSep " " (el: "+${el}") cfg.sieve.globalExtensions}'') + (optionalString (cfg.imapsieve.mailbox != []) '' ${ concatStringsSep "\n" (flatten (imap1 ( diff --git a/nixos/modules/services/mail/roundcube.nix b/nixos/modules/services/mail/roundcube.nix index c883c143e523..3f1a695ab91a 100644 --- a/nixos/modules/services/mail/roundcube.nix +++ b/nixos/modules/services/mail/roundcube.nix @@ -250,6 +250,7 @@ in path = [ config.services.postgresql.package ]; }) { + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; script = let diff --git a/nixos/modules/services/mail/sympa.nix b/nixos/modules/services/mail/sympa.nix index 04ae46f66eea..13fc8656a2b5 100644 --- a/nixos/modules/services/mail/sympa.nix +++ b/nixos/modules/services/mail/sympa.nix @@ -435,7 +435,7 @@ in wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; - wants = sympaSubServices; + wants = sympaSubServices ++ [ "network-online.target" ]; before = sympaSubServices; serviceConfig = sympaServiceConfig "sympa_msg"; diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix index 50019d2a25cb..4c1c396eac05 100644 --- a/nixos/modules/services/matrix/synapse.nix +++ b/nixos/modules/services/matrix/synapse.nix @@ -1056,6 +1056,7 @@ in { systemd.targets.matrix-synapse = lib.mkIf hasWorkers { description = "Synapse Matrix parent target"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service"; wantedBy = [ "multi-user.target" ]; }; @@ -1071,6 +1072,7 @@ in { requires = optional hasLocalPostgresDB "postgresql.service"; } else { + wants = [ "network-online.target" ]; after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service"; requires = optional hasLocalPostgresDB "postgresql.service"; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/misc/amazon-ssm-agent.nix b/nixos/modules/services/misc/amazon-ssm-agent.nix index 20b836abe164..89a1c0766510 100644 --- a/nixos/modules/services/misc/amazon-ssm-agent.nix +++ b/nixos/modules/services/misc/amazon-ssm-agent.nix @@ -41,6 +41,7 @@ in { # See https://github.com/aws/amazon-ssm-agent/blob/mainline/packaging/linux/amazon-ssm-agent.service systemd.services.amazon-ssm-agent = { inherit (cfg.package.meta) description; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/misc/bcg.nix b/nixos/modules/services/misc/bcg.nix index 9da4a879cdd0..ad0b9c871342 100644 --- a/nixos/modules/services/misc/bcg.nix +++ b/nixos/modules/services/misc/bcg.nix @@ -154,7 +154,7 @@ in in { description = "BigClown Gateway"; wantedBy = [ "multi-user.target" ]; - wants = mkIf config.services.mosquitto.enable [ "mosquitto.service" ]; + wants = [ "network-online.target" ] ++ lib.optional config.services.mosquitto.enable "mosquitto.service"; after = [ "network-online.target" ]; preStart = '' umask 077 diff --git a/nixos/modules/services/misc/domoticz.nix b/nixos/modules/services/misc/domoticz.nix index fd9fcf0b78eb..315092f93351 100644 --- a/nixos/modules/services/misc/domoticz.nix +++ b/nixos/modules/services/misc/domoticz.nix @@ -35,6 +35,7 @@ in { systemd.services."domoticz" = { description = pkgDesc; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { DynamicUser = true; diff --git a/nixos/modules/services/misc/etesync-dav.nix b/nixos/modules/services/misc/etesync-dav.nix index 9d99d548d95b..ae2b5ad04343 100644 --- a/nixos/modules/services/misc/etesync-dav.nix +++ b/nixos/modules/services/misc/etesync-dav.nix @@ -59,6 +59,7 @@ in systemd.services.etesync-dav = { description = "etesync-dav - A CalDAV and CardDAV adapter for EteSync"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; path = [ pkgs.etesync-dav ]; diff --git a/nixos/modules/services/misc/mediatomb.nix b/nixos/modules/services/misc/mediatomb.nix index d421d74c53ad..03235e9a1265 100644 --- a/nixos/modules/services/misc/mediatomb.nix +++ b/nixos/modules/services/misc/mediatomb.nix @@ -357,6 +357,7 @@ in { description = "${cfg.serverName} media Server"; # Gerbera might fail if the network interface is not available on startup # https://github.com/gerbera/gerbera/issues/1324 + wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${binaryCommand} --port ${toString cfg.port} ${interfaceFlag} ${configFlag} --home ${cfg.dataDir}"; diff --git a/nixos/modules/services/misc/metabase.nix b/nixos/modules/services/misc/metabase.nix index 883fa0b95911..5fc18e27eaae 100644 --- a/nixos/modules/services/misc/metabase.nix +++ b/nixos/modules/services/misc/metabase.nix @@ -77,6 +77,7 @@ in { systemd.services.metabase = { description = "Metabase server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; environment = { MB_PLUGINS_DIR = "${dataDir}/plugins"; diff --git a/nixos/modules/services/misc/moonraker.nix b/nixos/modules/services/misc/moonraker.nix index 750dca9d0373..4e419aafa990 100644 --- a/nixos/modules/services/misc/moonraker.nix +++ b/nixos/modules/services/misc/moonraker.nix @@ -103,7 +103,7 @@ in { config = mkIf cfg.enable { warnings = [] - ++ optional (cfg.settings ? update_manager) + ++ optional (cfg.settings.update_manager.enable_system_updates or false) ''Enabling update_manager is not supported on NixOS and will lead to non-removable warnings in some clients.'' ++ optional (cfg.configDir != null) '' diff --git a/nixos/modules/services/misc/nix-ssh-serve.nix b/nixos/modules/services/misc/nix-ssh-serve.nix index b656692ca01c..cf9d6339c69b 100644 --- a/nixos/modules/services/misc/nix-ssh-serve.nix +++ b/nixos/modules/services/misc/nix-ssh-serve.nix @@ -1,4 +1,4 @@ -{ config, lib, ... }: +{ config, lib, pkgs, ... }: with lib; let cfg = config.nix.sshServe; @@ -46,7 +46,7 @@ in { description = "Nix SSH store user"; isSystemUser = true; group = "nix-ssh"; - useDefaultShell = true; + shell = pkgs.bashInteractive; }; users.groups.nix-ssh = {}; diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix index 9794bbbec464..d9359d2b5cd4 100644 --- a/nixos/modules/services/misc/ollama.nix +++ b/nixos/modules/services/misc/ollama.nix @@ -9,6 +9,13 @@ in { enable = lib.mkEnableOption ( lib.mdDoc "Server for local large language models" ); + listenAddress = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1:11434"; + description = lib.mdDoc '' + Specifies the bind address on which the ollama server HTTP interface listens. + ''; + }; package = lib.mkPackageOption pkgs "ollama" { }; }; }; @@ -23,6 +30,7 @@ in { environment = { HOME = "%S/ollama"; OLLAMA_MODELS = "%S/ollama/models"; + OLLAMA_HOST = cfg.listenAddress; }; serviceConfig = { ExecStart = "${lib.getExe cfg.package} serve"; diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix index 3c6832958f59..ca34a327dbdf 100644 --- a/nixos/modules/services/misc/paperless.nix +++ b/nixos/modules/services/misc/paperless.nix @@ -297,6 +297,7 @@ in wantedBy = [ "paperless-scheduler.service" ]; before = [ "paperless-scheduler.service" ]; after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; serviceConfig = defaultServiceConfig // { User = cfg.user; Type = "oneshot"; diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix index 7036a372d1ea..47af24f024cd 100644 --- a/nixos/modules/services/misc/portunus.nix +++ b/nixos/modules/services/misc/portunus.nix @@ -230,7 +230,10 @@ in description = "Self-contained authentication service"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - serviceConfig.ExecStart = "${cfg.package.out}/bin/portunus-orchestrator"; + serviceConfig = { + ExecStart = "${cfg.package}/bin/portunus-orchestrator"; + Restart = "on-failure"; + }; environment = { PORTUNUS_LDAP_SUFFIX = cfg.ldap.suffix; PORTUNUS_SERVER_BINARY = "${cfg.package}/bin/portunus-server"; diff --git a/nixos/modules/services/misc/taskserver/helper-tool.py b/nixos/modules/services/misc/taskserver/helper-tool.py index fec05728b2b6..b1eebb07686b 100644 --- a/nixos/modules/services/misc/taskserver/helper-tool.py +++ b/nixos/modules/services/misc/taskserver/helper-tool.py @@ -61,6 +61,10 @@ def run_as_taskd_user(): os.setuid(uid) +def run_as_taskd_group(): + gid = grp.getgrnam(TASKD_GROUP).gr_gid + os.setgid(gid) + def taskd_cmd(cmd, *args, **kwargs): """ Invoke taskd with the specified command with the privileges of the 'taskd' @@ -90,7 +94,7 @@ def certtool_cmd(*args, **kwargs): """ return subprocess.check_output( [CERTTOOL_COMMAND] + list(args), - preexec_fn=lambda: os.umask(0o077), + preexec_fn=run_as_taskd_group, stderr=subprocess.STDOUT, **kwargs ) @@ -156,17 +160,33 @@ def generate_key(org, user): sys.stderr.write(msg.format(user)) return - basedir = os.path.join(TASKD_DATA_DIR, "keys", org, user) - if os.path.exists(basedir): + keysdir = os.path.join(TASKD_DATA_DIR, "keys" ) + orgdir = os.path.join(keysdir , org ) + userdir = os.path.join(orgdir , user ) + if os.path.exists(userdir): raise OSError("Keyfile directory for {} already exists.".format(user)) - privkey = os.path.join(basedir, "private.key") - pubcert = os.path.join(basedir, "public.cert") + privkey = os.path.join(userdir, "private.key") + pubcert = os.path.join(userdir, "public.cert") try: - os.makedirs(basedir, mode=0o700) + # We change the permissions and the owner ship of the base directories + # so that cfg.group and cfg.user could read the directories' contents. + # See also: https://bugs.python.org/issue42367 + for bd in [keysdir, orgdir, userdir]: + # Allow cfg.group, but not others to read the contents of this group + os.makedirs(bd, exist_ok=True) + # not using mode= argument to makedirs intentionally - forcing the + # permissions we want + os.chmod(bd, mode=0o750) + os.chown( + bd, + uid=pwd.getpwnam(TASKD_USER).pw_uid, + gid=grp.getgrnam(TASKD_GROUP).gr_gid, + ) certtool_cmd("-p", "--bits", CERT_BITS, "--outfile", privkey) + os.chmod(privkey, 0o640) template_data = [ "organization = {0}".format(org), @@ -187,7 +207,7 @@ def generate_key(org, user): "--outfile", pubcert ) except: - rmtree(basedir) + rmtree(userdir) raise diff --git a/nixos/modules/services/monitoring/mackerel-agent.nix b/nixos/modules/services/monitoring/mackerel-agent.nix index 62a7858500f2..5915634ed26f 100644 --- a/nixos/modules/services/monitoring/mackerel-agent.nix +++ b/nixos/modules/services/monitoring/mackerel-agent.nix @@ -84,6 +84,7 @@ in { # upstream service file in https://git.io/JUt4Q systemd.services.mackerel-agent = { description = "mackerel.io agent"; + wants = [ "network-online.target" ]; after = [ "network-online.target" "nss-lookup.target" ]; wantedBy = [ "multi-user.target" ]; environment = { diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix index 4fd630015f35..bb426d8b7beb 100644 --- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix +++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix @@ -174,6 +174,7 @@ in { systemd.services.alertmanager = { wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; preStart = '' ${lib.getBin pkgs.envsubst}/bin/envsubst -o "/tmp/alert-manager-substituted.yaml" \ diff --git a/nixos/modules/services/monitoring/teamviewer.nix b/nixos/modules/services/monitoring/teamviewer.nix index 9b1278317943..7c45247aa6d5 100644 --- a/nixos/modules/services/monitoring/teamviewer.nix +++ b/nixos/modules/services/monitoring/teamviewer.nix @@ -30,6 +30,7 @@ in description = "TeamViewer remote control daemon"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" "network.target" "dbus.service" ]; requires = [ "dbus.service" ]; preStart = "mkdir -pv /var/lib/teamviewer /var/log/teamviewer"; diff --git a/nixos/modules/services/monitoring/telegraf.nix b/nixos/modules/services/monitoring/telegraf.nix index ee28ee03adf3..3bab8aba7bd6 100644 --- a/nixos/modules/services/monitoring/telegraf.nix +++ b/nixos/modules/services/monitoring/telegraf.nix @@ -59,6 +59,7 @@ in { in { description = "Telegraf Agent"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = lib.optional (config.services.telegraf.extraConfig.inputs ? procstat) pkgs.procps; serviceConfig = { diff --git a/nixos/modules/services/monitoring/watchdogd.nix b/nixos/modules/services/monitoring/watchdogd.nix new file mode 100644 index 000000000000..e8d104651c6a --- /dev/null +++ b/nixos/modules/services/monitoring/watchdogd.nix @@ -0,0 +1,131 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.watchdogd; + + mkPluginOpts = plugin: defWarn: defCrit: { + enabled = mkEnableOption "watchdogd plugin ${plugin}"; + interval = mkOption { + type = types.ints.unsigned; + default = 300; + description = '' + Amount of seconds between every poll. + ''; + }; + logmark = mkOption { + type = types.bool; + default = false; + description = '' + Whether to log current stats every poll interval. + ''; + }; + warning = mkOption { + type = types.numbers.nonnegative; + default = defWarn; + description = '' + The high watermark level. Alert sent to log. + ''; + }; + critical = mkOption { + type = types.numbers.nonnegative; + default = defCrit; + description = '' + The critical watermark level. Alert sent to log, followed by reboot or script action. + ''; + }; + }; +in { + options.services.watchdogd = { + enable = mkEnableOption "watchdogd, an advanced system & process supervisor"; + package = mkPackageOption pkgs "watchdogd" { }; + + settings = mkOption { + type = with types; submodule { + freeformType = let + valueType = oneOf [ + bool + int + float + str + ]; + in attrsOf (either valueType (attrsOf valueType)); + + options = { + timeout = mkOption { + type = types.ints.unsigned; + default = 15; + description = '' + The WDT timeout before reset. + ''; + }; + interval = mkOption { + type = types.ints.unsigned; + default = 5; + description = '' + The kick interval, i.e. how often {manpage}`watchdogd(8)` should reset the WDT timer. + ''; + }; + + safe-exit = mkOption { + type = types.bool; + default = true; + description = '' + With {var}`safeExit` enabled, the daemon will ask the driver to disable the WDT before exiting. + However, some WDT drivers (or hardware) may not support this. + ''; + }; + + filenr = mkPluginOpts "filenr" 0.9 1.0; + + loadavg = mkPluginOpts "loadavg" 1.0 2.0; + + meminfo = mkPluginOpts "meminfo" 0.9 0.95; + }; + }; + default = { }; + description = '' + Configuration to put in {file}`watchdogd.conf`. + See {manpage}`watchdogd.conf(5)` for more details. + ''; + }; + }; + + config = let + toConfig = attrs: concatStringsSep "\n" (mapAttrsToList toValue attrs); + + toValue = name: value: + if isAttrs value + then pipe value [ + (mapAttrsToList toValue) + (map (s: " ${s}")) + (concatStringsSep "\n") + (s: "${name} {\n${s}\n}") + ] + else if isBool value + then "${name} = ${boolToString value}" + else if any (f: f value) [isString isInt isFloat] + then "${name} = ${toString value}" + else throw '' + Found invalid type in `services.watchdogd.settings`: '${typeOf value}' + ''; + + watchdogdConf = pkgs.writeText "watchdogd.conf" (toConfig cfg.settings); + in mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + systemd.services.watchdogd = { + documentation = [ + "man:watchdogd(8)" + "man:watchdogd.conf(5)" + ]; + wantedBy = [ "multi-user.target" ]; + description = "Advanced system & process supervisor"; + serviceConfig = { + Type = "simple"; + ExecStart = "${cfg.package}/bin/watchdogd -n -f ${watchdogdConf}"; + }; + }; + }; + + meta.maintainers = with maintainers; [ vifino ]; +} diff --git a/nixos/modules/services/network-filesystems/openafs/client.nix b/nixos/modules/services/network-filesystems/openafs/client.nix index bb0fee087e62..02c3482ec657 100644 --- a/nixos/modules/services/network-filesystems/openafs/client.nix +++ b/nixos/modules/services/network-filesystems/openafs/client.nix @@ -215,6 +215,7 @@ in systemd.services.afsd = { description = "AFS client"; wantedBy = [ "multi-user.target" ]; + wants = lib.optional (!cfg.startDisconnected) "network-online.target"; after = singleton (if cfg.startDisconnected then "network.target" else "network-online.target"); serviceConfig = { RemainAfterExit = true; }; restartIfChanged = false; diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix index 5d02eac8e9f1..ef368ddbeefd 100644 --- a/nixos/modules/services/network-filesystems/samba.nix +++ b/nixos/modules/services/network-filesystems/samba.nix @@ -154,7 +154,7 @@ in }; securityType = mkOption { - type = types.str; + type = types.enum [ "auto" "user" "domain" "ads" ]; default = "user"; description = lib.mdDoc "Samba security type"; }; diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix index 9deeb7694d2a..e25f5c7b0379 100644 --- a/nixos/modules/services/networking/bird.nix +++ b/nixos/modules/services/networking/bird.nix @@ -18,6 +18,13 @@ in <http://bird.network.cz/> ''; }; + autoReload = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether bird2 should be automatically reloaded when the configuration changes. + ''; + }; checkConfig = mkOption { type = types.bool; default = true; @@ -68,7 +75,7 @@ in systemd.services.bird2 = { description = "BIRD Internet Routing Daemon"; wantedBy = [ "multi-user.target" ]; - reloadTriggers = [ config.environment.etc."bird/bird2.conf".source ]; + reloadTriggers = lib.optional cfg.autoReload config.environment.etc."bird/bird2.conf".source; serviceConfig = { Type = "forking"; Restart = "on-failure"; diff --git a/nixos/modules/services/networking/bitcoind.nix b/nixos/modules/services/networking/bitcoind.nix index 4512e666ba5b..59722e31c62a 100644 --- a/nixos/modules/services/networking/bitcoind.nix +++ b/nixos/modules/services/networking/bitcoind.nix @@ -198,6 +198,7 @@ in ''; in { description = "Bitcoin daemon"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { diff --git a/nixos/modules/services/networking/dante.nix b/nixos/modules/services/networking/dante.nix index 605f2d74f827..f0d1d6305c54 100644 --- a/nixos/modules/services/networking/dante.nix +++ b/nixos/modules/services/networking/dante.nix @@ -47,6 +47,7 @@ in systemd.services.dante = { description = "Dante SOCKS v4 and v5 compatible proxy server"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/ergo.nix b/nixos/modules/services/networking/ergo.nix index 033d4d9caf8a..1bee0f43f988 100644 --- a/nixos/modules/services/networking/ergo.nix +++ b/nixos/modules/services/networking/ergo.nix @@ -114,6 +114,7 @@ in { systemd.services.ergo = { description = "ergo server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { User = cfg.user; diff --git a/nixos/modules/services/networking/expressvpn.nix b/nixos/modules/services/networking/expressvpn.nix index 30de6987d31f..05c24d8bccff 100644 --- a/nixos/modules/services/networking/expressvpn.nix +++ b/nixos/modules/services/networking/expressvpn.nix @@ -21,6 +21,7 @@ with lib; RestartSec = 5; }; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; }; }; diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix index 4224a0578cc3..95b5fcf6ebde 100644 --- a/nixos/modules/services/networking/headscale.nix +++ b/nixos/modules/services/networking/headscale.nix @@ -460,6 +460,7 @@ in { systemd.services.headscale = { description = "headscale coordination server for Tailscale"; + wants = [ "network-online.target" ]; after = ["network-online.target"]; wantedBy = ["multi-user.target"]; restartTriggers = [configFile]; diff --git a/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixos/modules/services/networking/ircd-hybrid/default.nix index 554b0f7bb8b4..64a34cc52d25 100644 --- a/nixos/modules/services/networking/ircd-hybrid/default.nix +++ b/nixos/modules/services/networking/ircd-hybrid/default.nix @@ -125,7 +125,8 @@ in systemd.services.ircd-hybrid = { description = "IRCD Hybrid server"; - after = [ "started networking" ]; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; script = "${ircdService}/bin/control start"; }; diff --git a/nixos/modules/services/networking/ivpn.nix b/nixos/modules/services/networking/ivpn.nix index 6df630c1f194..6c9ae599e670 100644 --- a/nixos/modules/services/networking/ivpn.nix +++ b/nixos/modules/services/networking/ivpn.nix @@ -27,7 +27,7 @@ with lib; systemd.services.ivpn-service = { description = "iVPN daemon"; wantedBy = [ "multi-user.target" ]; - wants = [ "network.target" ]; + wants = [ "network.target" "network-online.target" ]; after = [ "network-online.target" "NetworkManager.service" diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix index 5ca705976c41..656ddd41fd12 100644 --- a/nixos/modules/services/networking/kea.nix +++ b/nixos/modules/services/networking/kea.nix @@ -325,6 +325,9 @@ in "network-online.target" "time-sync.target" ]; + wants = [ + "network-online.target" + ]; wantedBy = [ "multi-user.target" ]; @@ -372,6 +375,9 @@ in "network-online.target" "time-sync.target" ]; + wants = [ + "network-online.target" + ]; wantedBy = [ "multi-user.target" ]; @@ -413,6 +419,7 @@ in "https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" "time-sync.target" diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index f2b158b98942..ad9eefb42252 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -596,6 +596,7 @@ in systemd.services.mosquitto = { description = "Mosquitto MQTT Broker Daemon"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { Type = "notify"; diff --git a/nixos/modules/services/networking/mullvad-vpn.nix b/nixos/modules/services/networking/mullvad-vpn.nix index 446c71f40764..5da4ca1d1d80 100644 --- a/nixos/modules/services/networking/mullvad-vpn.nix +++ b/nixos/modules/services/networking/mullvad-vpn.nix @@ -53,7 +53,7 @@ with lib; systemd.services.mullvad-daemon = { description = "Mullvad VPN daemon"; wantedBy = [ "multi-user.target" ]; - wants = [ "network.target" ]; + wants = [ "network.target" "network-online.target" ]; after = [ "network-online.target" "NetworkManager.service" diff --git a/nixos/modules/services/networking/nbd.nix b/nixos/modules/services/networking/nbd.nix index 454380aa3154..b4bf7ede8463 100644 --- a/nixos/modules/services/networking/nbd.nix +++ b/nixos/modules/services/networking/nbd.nix @@ -117,6 +117,7 @@ in boot.kernelModules = [ "nbd" ]; systemd.services.nbd-server = { + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; before = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/ocserv.nix b/nixos/modules/services/networking/ocserv.nix index 9548fd92dbda..3c61d56b893e 100644 --- a/nixos/modules/services/networking/ocserv.nix +++ b/nixos/modules/services/networking/ocserv.nix @@ -85,6 +85,7 @@ in systemd.services.ocserv = { description = "OpenConnect SSL VPN server"; documentation = [ "man:ocserv(8)" ]; + wants = [ "network-online.target" ]; after = [ "dbus.service" "network-online.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/pleroma.nix b/nixos/modules/services/networking/pleroma.nix index db0a61b83469..8470f5e9cbc0 100644 --- a/nixos/modules/services/networking/pleroma.nix +++ b/nixos/modules/services/networking/pleroma.nix @@ -92,6 +92,7 @@ in { systemd.services.pleroma = { description = "Pleroma social network"; + wants = [ "network-online.target" ]; after = [ "network-online.target" "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; restartTriggers = [ config.environment.etc."/pleroma/config.exs".source ]; diff --git a/nixos/modules/services/networking/rosenpass.nix b/nixos/modules/services/networking/rosenpass.nix index d2a264b83d67..487cb6f60142 100644 --- a/nixos/modules/services/networking/rosenpass.nix +++ b/nixos/modules/services/networking/rosenpass.nix @@ -208,6 +208,7 @@ in in rec { wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = [ cfg.package pkgs.wireguard-tools ]; diff --git a/nixos/modules/services/networking/rxe.nix b/nixos/modules/services/networking/rxe.nix index 7dbb4823b4bc..07437ed71195 100644 --- a/nixos/modules/services/networking/rxe.nix +++ b/nixos/modules/services/networking/rxe.nix @@ -33,7 +33,7 @@ in { wantedBy = [ "multi-user.target" ]; after = [ "systemd-modules-load.service" "network-online.target" ]; - wants = [ "network-pre.target" ]; + wants = [ "network-pre.target" "network-online.target" ]; serviceConfig = { Type = "oneshot"; diff --git a/nixos/modules/services/networking/soju.nix b/nixos/modules/services/networking/soju.nix index 7f0ac3e3b8e6..d69ec08ca13a 100644 --- a/nixos/modules/services/networking/soju.nix +++ b/nixos/modules/services/networking/soju.nix @@ -110,6 +110,7 @@ in systemd.services.soju = { description = "soju IRC bouncer"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { DynamicUser = true; diff --git a/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixos/modules/services/networking/strongswan-swanctl/module.nix index c8832ed4defb..a98850923955 100644 --- a/nixos/modules/services/networking/strongswan-swanctl/module.nix +++ b/nixos/modules/services/networking/strongswan-swanctl/module.nix @@ -55,6 +55,7 @@ in { systemd.services.strongswan-swanctl = { description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = with pkgs; [ kmod iproute2 iptables util-linux ]; environment = { diff --git a/nixos/modules/services/networking/strongswan.nix b/nixos/modules/services/networking/strongswan.nix index e58526814d1a..dcf04d2a1917 100644 --- a/nixos/modules/services/networking/strongswan.nix +++ b/nixos/modules/services/networking/strongswan.nix @@ -153,6 +153,7 @@ in description = "strongSwan IPSec Service"; wantedBy = [ "multi-user.target" ]; path = with pkgs; [ kmod iproute2 iptables util-linux ]; # XXX Linux + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; environment = { STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; }; diff --git a/nixos/modules/services/networking/syncplay.nix b/nixos/modules/services/networking/syncplay.nix index 0a66d93bf153..151259b6d4ad 100644 --- a/nixos/modules/services/networking/syncplay.nix +++ b/nixos/modules/services/networking/syncplay.nix @@ -107,6 +107,7 @@ in systemd.services.syncplay = { description = "Syncplay Service"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { diff --git a/nixos/modules/services/networking/wasabibackend.nix b/nixos/modules/services/networking/wasabibackend.nix index 938145b35ee8..e3a48afd2a2c 100644 --- a/nixos/modules/services/networking/wasabibackend.nix +++ b/nixos/modules/services/networking/wasabibackend.nix @@ -119,6 +119,7 @@ in { systemd.services.wasabibackend = { description = "wasabibackend server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; environment = { DOTNET_PRINT_TELEMETRY_MESSAGE = "false"; diff --git a/nixos/modules/services/networking/znc/default.nix b/nixos/modules/services/networking/znc/default.nix index d3ba4a524197..e15233293cf2 100644 --- a/nixos/modules/services/networking/znc/default.nix +++ b/nixos/modules/services/networking/znc/default.nix @@ -243,6 +243,7 @@ in systemd.services.znc = { description = "ZNC Server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { User = cfg.user; diff --git a/nixos/modules/services/security/certmgr.nix b/nixos/modules/services/security/certmgr.nix index db80e943973d..02cb7afe87ba 100644 --- a/nixos/modules/services/security/certmgr.nix +++ b/nixos/modules/services/security/certmgr.nix @@ -182,6 +182,7 @@ in systemd.services.certmgr = { description = "certmgr"; path = mkIf (cfg.svcManager == "command") [ pkgs.bash ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; inherit preStart; diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix index d3164373ec01..4480c0cae60c 100644 --- a/nixos/modules/services/security/clamav.nix +++ b/nixos/modules/services/security/clamav.nix @@ -196,6 +196,7 @@ in systemd.services.clamav-freshclam = mkIf cfg.updater.enable { description = "ClamAV virus database updater (freshclam)"; restartTriggers = [ freshclamConfigFile ]; + requires = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { @@ -243,6 +244,7 @@ in systemd.services.clamav-fangfrisch = mkIf cfg.fangfrisch.enable { description = "ClamAV virus database updater (fangfrisch)"; restartTriggers = [ fangfrischConfigFile ]; + requires = [ "network-online.target" ]; after = [ "network-online.target" "clamav-fangfrisch-init.service" ]; serviceConfig = { diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix index 78916c907279..d1dc37d549d2 100644 --- a/nixos/modules/services/security/oauth2_proxy.nix +++ b/nixos/modules/services/security/oauth2_proxy.nix @@ -572,6 +572,7 @@ in description = "OAuth2 Proxy"; path = [ cfg.package ]; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { diff --git a/nixos/modules/services/system/cachix-agent/default.nix b/nixos/modules/services/system/cachix-agent/default.nix index 196d3291d555..f8020fe970f1 100644 --- a/nixos/modules/services/system/cachix-agent/default.nix +++ b/nixos/modules/services/system/cachix-agent/default.nix @@ -49,6 +49,7 @@ in { config = mkIf cfg.enable { systemd.services.cachix-agent = { description = "Cachix Deploy Agent"; + wants = [ "network-online.target" ]; after = ["network-online.target"]; path = [ config.nix.package ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/system/cachix-watch-store.nix b/nixos/modules/services/system/cachix-watch-store.nix index 8aa5f0358fa9..d48af29465aa 100644 --- a/nixos/modules/services/system/cachix-watch-store.nix +++ b/nixos/modules/services/system/cachix-watch-store.nix @@ -61,6 +61,7 @@ in config = mkIf cfg.enable { systemd.services.cachix-watch-store-agent = { description = "Cachix watch store Agent"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = [ config.nix.package ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/system/cloud-init.nix b/nixos/modules/services/system/cloud-init.nix index d782bb1a3666..00ae77be4271 100644 --- a/nixos/modules/services/system/cloud-init.nix +++ b/nixos/modules/services/system/cloud-init.nix @@ -164,7 +164,10 @@ in systemd.services.cloud-init-local = { description = "Initial cloud-init job (pre-networking)"; wantedBy = [ "multi-user.target" ]; - before = [ "systemd-networkd.service" ]; + # In certain environments (AWS for example), cloud-init-local will + # first configure an IP through DHCP, and later delete it. + # This can cause race conditions with anything else trying to set IP through DHCP. + before = [ "systemd-networkd.service" "dhcpcd.service" ]; path = path; serviceConfig = { Type = "oneshot"; diff --git a/nixos/modules/services/video/go2rtc/default.nix b/nixos/modules/services/video/go2rtc/default.nix index 13851fa0306f..9dddbb60baa8 100644 --- a/nixos/modules/services/video/go2rtc/default.nix +++ b/nixos/modules/services/video/go2rtc/default.nix @@ -94,6 +94,7 @@ in config = lib.mkIf cfg.enable { systemd.services.go2rtc = { + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; diff --git a/nixos/modules/services/web-apps/akkoma.nix b/nixos/modules/services/web-apps/akkoma.nix index 8980556ab014..4cd9e2664378 100644 --- a/nixos/modules/services/web-apps/akkoma.nix +++ b/nixos/modules/services/web-apps/akkoma.nix @@ -974,7 +974,7 @@ in { # This service depends on network-online.target and is sequenced after # it because it requires access to the Internet to function properly. bindsTo = [ "akkoma-config.service" ]; - wants = [ "network-online.service" ]; + wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; after = [ "akkoma-config.target" diff --git a/nixos/modules/services/web-apps/alps.nix b/nixos/modules/services/web-apps/alps.nix index 05fb676102df..81c6b8ad30b5 100644 --- a/nixos/modules/services/web-apps/alps.nix +++ b/nixos/modules/services/web-apps/alps.nix @@ -94,6 +94,7 @@ in { description = "alps is a simple and extensible webmail."; documentation = [ "https://git.sr.ht/~migadu/alps" ]; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; serviceConfig = { diff --git a/nixos/modules/services/web-apps/c2fmzq-server.nix b/nixos/modules/services/web-apps/c2fmzq-server.nix index 87938fe160e1..dee131182de1 100644 --- a/nixos/modules/services/web-apps/c2fmzq-server.nix +++ b/nixos/modules/services/web-apps/c2fmzq-server.nix @@ -80,6 +80,7 @@ in { description = "c2FmZQ-server"; documentation = [ "https://github.com/c2FmZQ/c2FmZQ/blob/main/README.md" ]; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; serviceConfig = { diff --git a/nixos/modules/services/web-apps/code-server.nix b/nixos/modules/services/web-apps/code-server.nix index 11601f6c3044..d087deb7848d 100644 --- a/nixos/modules/services/web-apps/code-server.nix +++ b/nixos/modules/services/web-apps/code-server.nix @@ -205,6 +205,7 @@ in { systemd.services.code-server = { description = "Code server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = cfg.extraPackages; environment = { diff --git a/nixos/modules/services/web-apps/healthchecks.nix b/nixos/modules/services/web-apps/healthchecks.nix index e5e425a29d54..1d439f162313 100644 --- a/nixos/modules/services/web-apps/healthchecks.nix +++ b/nixos/modules/services/web-apps/healthchecks.nix @@ -176,6 +176,7 @@ in systemd.targets.healthchecks = { description = "Target for all Healthchecks services"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; }; diff --git a/nixos/modules/services/web-apps/netbox.nix b/nixos/modules/services/web-apps/netbox.nix index 88d40b3abc52..72ec578146a7 100644 --- a/nixos/modules/services/web-apps/netbox.nix +++ b/nixos/modules/services/web-apps/netbox.nix @@ -267,6 +267,7 @@ in { systemd.targets.netbox = { description = "Target for all NetBox services"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" "redis-netbox.service" ]; }; diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 38c51251aac1..0b19265942c0 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -99,11 +99,101 @@ let mysqlLocal = cfg.database.createLocally && cfg.config.dbtype == "mysql"; pgsqlLocal = cfg.database.createLocally && cfg.config.dbtype == "pgsql"; + nextcloudGreaterOrEqualThan = versionAtLeast cfg.package.version; + nextcloudOlderThan = versionOlder cfg.package.version; + # https://github.com/nextcloud/documentation/pull/11179 - ocmProviderIsNotAStaticDirAnymore = versionAtLeast cfg.package.version "27.1.2" - || (versionOlder cfg.package.version "27.0.0" - && versionAtLeast cfg.package.version "26.0.8"); + ocmProviderIsNotAStaticDirAnymore = nextcloudGreaterOrEqualThan "27.1.2" + || (nextcloudOlderThan "27.0.0" && nextcloudGreaterOrEqualThan "26.0.8"); + + overrideConfig = let + c = cfg.config; + requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable; + objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable '' + 'objectstore' => [ + 'class' => '\\OC\\Files\\ObjectStore\\S3', + 'arguments' => [ + 'bucket' => '${s3.bucket}', + 'autocreate' => ${boolToString s3.autocreate}, + 'key' => '${s3.key}', + 'secret' => nix_read_secret('${s3.secretFile}'), + ${optionalString (s3.hostname != null) "'hostname' => '${s3.hostname}',"} + ${optionalString (s3.port != null) "'port' => ${toString s3.port},"} + 'use_ssl' => ${boolToString s3.useSsl}, + ${optionalString (s3.region != null) "'region' => '${s3.region}',"} + 'use_path_style' => ${boolToString s3.usePathStyle}, + ${optionalString (s3.sseCKeyFile != null) "'sse_c_key' => nix_read_secret('${s3.sseCKeyFile}'),"} + ], + ] + ''; + showAppStoreSetting = cfg.appstoreEnable != null || cfg.extraApps != {}; + renderedAppStoreSetting = + let + x = cfg.appstoreEnable; + in + if x == null then "false" + else boolToString x; + mkAppStoreConfig = name: { enabled, writable, ... }: optionalString enabled '' + [ 'path' => '${webroot}/${name}', 'url' => '/${name}', 'writable' => ${boolToString writable} ], + ''; + in pkgs.writeText "nextcloud-config.php" '' + <?php + ${optionalString requiresReadSecretFunction '' + function nix_read_secret($file) { + if (!file_exists($file)) { + throw new \RuntimeException(sprintf( + "Cannot start Nextcloud, secret file %s set by NixOS doesn't seem to " + . "exist! Please make sure that the file exists and has appropriate " + . "permissions for user & group 'nextcloud'!", + $file + )); + } + return trim(file_get_contents($file)); + }''} + function nix_decode_json_file($file, $error) { + if (!file_exists($file)) { + throw new \RuntimeException(sprintf($error, $file)); + } + $decoded = json_decode(file_get_contents($file), true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \RuntimeException(sprintf("Cannot decode %s, because: %s", $file, json_last_error_msg())); + } + return $decoded; + } + $CONFIG = [ + 'apps_paths' => [ + ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)} + ], + ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"} + ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} + ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} + ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} + ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"} + ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"} + ${optionalString (c.dbpassFile != null) '' + 'dbpassword' => nix_read_secret( + "${c.dbpassFile}" + ), + '' + } + 'dbtype' => '${c.dbtype}', + ${objectstoreConfig} + ]; + + $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( + "${jsonFormat.generate "nextcloud-extraOptions.json" cfg.extraOptions}", + "impossible: this should never happen (decoding generated extraOptions file %s failed)" + )); + + ${optionalString (cfg.secretFile != null) '' + $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( + "${cfg.secretFile}", + "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!" + )); + ''} + ''; in { imports = [ @@ -787,107 +877,23 @@ in { timerConfig.Unit = "nextcloud-cron.service"; }; - systemd.tmpfiles.rules = ["d ${cfg.home} 0750 nextcloud nextcloud"]; + systemd.tmpfiles.rules = map (dir: "d ${dir} 0750 nextcloud nextcloud - -") [ + "${cfg.home}" + "${datadir}/config" + "${datadir}/data" + "${cfg.home}/store-apps" + ] ++ [ + "L+ ${datadir}/config/override.config.php - - - - ${overrideConfig}" + ]; systemd.services = { # 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 = [ webroot ]; + phpfpm-nextcloud.restartTriggers = [ webroot overrideConfig ]; nextcloud-setup = let c = cfg.config; - requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable; - objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable '' - 'objectstore' => [ - 'class' => '\\OC\\Files\\ObjectStore\\S3', - 'arguments' => [ - 'bucket' => '${s3.bucket}', - 'autocreate' => ${boolToString s3.autocreate}, - 'key' => '${s3.key}', - 'secret' => nix_read_secret('${s3.secretFile}'), - ${optionalString (s3.hostname != null) "'hostname' => '${s3.hostname}',"} - ${optionalString (s3.port != null) "'port' => ${toString s3.port},"} - 'use_ssl' => ${boolToString s3.useSsl}, - ${optionalString (s3.region != null) "'region' => '${s3.region}',"} - 'use_path_style' => ${boolToString s3.usePathStyle}, - ${optionalString (s3.sseCKeyFile != null) "'sse_c_key' => nix_read_secret('${s3.sseCKeyFile}'),"} - ], - ] - ''; - - showAppStoreSetting = cfg.appstoreEnable != null || cfg.extraApps != {}; - renderedAppStoreSetting = - let - x = cfg.appstoreEnable; - in - if x == null then "false" - else boolToString x; - - 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 '' - function nix_read_secret($file) { - if (!file_exists($file)) { - throw new \RuntimeException(sprintf( - "Cannot start Nextcloud, secret file %s set by NixOS doesn't seem to " - . "exist! Please make sure that the file exists and has appropriate " - . "permissions for user & group 'nextcloud'!", - $file - )); - } - return trim(file_get_contents($file)); - }''} - function nix_decode_json_file($file, $error) { - if (!file_exists($file)) { - throw new \RuntimeException(sprintf($error, $file)); - } - $decoded = json_decode(file_get_contents($file), true); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw new \RuntimeException(sprintf("Cannot decode %s, because: %s", $file, json_last_error_msg())); - } - - return $decoded; - } - $CONFIG = [ - 'apps_paths' => [ - ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)} - ], - ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"} - ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} - ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} - ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} - ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"} - ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"} - ${optionalString (c.dbpassFile != null) '' - 'dbpassword' => nix_read_secret( - "${c.dbpassFile}" - ), - '' - } - 'dbtype' => '${c.dbtype}', - ${objectstoreConfig} - ]; - - $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( - "${jsonFormat.generate "nextcloud-extraOptions.json" cfg.extraOptions}", - "impossible: this should never happen (decoding generated extraOptions file %s failed)" - )); - - ${optionalString (cfg.secretFile != null) '' - $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file( - "${cfg.secretFile}", - "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!" - )); - ''} - ''; occInstallCmd = let mkExport = { arg, value }: "export ${arg}=${value}"; dbpass = { @@ -932,6 +938,7 @@ in { after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; path = [ occ ]; + restartTriggers = [ overrideConfig ]; script = '' ${optionalString (c.dbpassFile != null) '' if [ ! -r "${c.dbpassFile}" ]; then @@ -959,18 +966,6 @@ in { 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; do - if [ ! -e $dir ]; then - install -o nextcloud -g nextcloud -d $dir - elif [ $(stat -c "%G" $dir) != "nextcloud" ]; then - chgrp -R nextcloud $dir - fi - done - - ln -sf ${overrideConfig} ${datadir}/config/override.config.php - # Do not install if already installed if [[ ! -e ${datadir}/config/config.php ]]; then ${occInstallCmd} diff --git a/nixos/modules/services/web-apps/openvscode-server.nix b/nixos/modules/services/web-apps/openvscode-server.nix index 76a19dccae16..81b9d1f3b4c8 100644 --- a/nixos/modules/services/web-apps/openvscode-server.nix +++ b/nixos/modules/services/web-apps/openvscode-server.nix @@ -159,6 +159,7 @@ in systemd.services.openvscode-server = { description = "OpenVSCode server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = cfg.extraPackages; environment = cfg.extraEnvironment; diff --git a/nixos/modules/services/web-apps/peering-manager.nix b/nixos/modules/services/web-apps/peering-manager.nix index d6f6077268d4..0382ce717473 100644 --- a/nixos/modules/services/web-apps/peering-manager.nix +++ b/nixos/modules/services/web-apps/peering-manager.nix @@ -196,6 +196,7 @@ in { systemd.targets.peering-manager = { description = "Target for all Peering Manager services"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" "redis-peering-manager.service" ]; }; diff --git a/nixos/modules/services/web-apps/suwayomi-server.md b/nixos/modules/services/web-apps/suwayomi-server.md new file mode 100644 index 000000000000..ff1e06c8a53a --- /dev/null +++ b/nixos/modules/services/web-apps/suwayomi-server.md @@ -0,0 +1,108 @@ +# Suwayomi-Server {#module-services-suwayomi-server} + +A free and open source manga reader server that runs extensions built for Tachiyomi. + +## Basic usage {#module-services-suwayomi-server-basic-usage} + +By default, the module will execute Suwayomi-Server backend and web UI: + +```nix +{ ... }: + +{ + services.suwayomi-server = { + enable = true; + }; +} +``` + +It runs in the systemd service named `suwayomi-server` in the data directory `/var/lib/suwayomi-server`. + +You can change the default parameters with some other parameters: +```nix +{ ... }: + +{ + services.suwayomi-server = { + enable = true; + + dataDir = "/var/lib/suwayomi"; # Default is "/var/lib/suwayomi-server" + openFirewall = true; + + settings = { + server.port = 4567; + }; + }; +} +``` + +If you want to create a desktop icon, you can activate the system tray option: + +```nix +{ ... }: + +{ + services.suwayomi-server = { + enable = true; + + dataDir = "/var/lib/suwayomi"; # Default is "/var/lib/suwayomi-server" + openFirewall = true; + + settings = { + server.port = 4567; + server.enableSystemTray = true; + }; + }; +} +``` + +## Basic authentication {#module-services-suwayomi-server-basic-auth} + +You can configure a basic authentication to the web interface with: + +```nix +{ ... }: + +{ + services.suwayomi-server = { + enable = true; + + openFirewall = true; + + settings = { + server.port = 4567; + server = { + basicAuthEnabled = true; + basicAuthUsername = "username"; + + # NOTE: this is not a real upstream option + basicAuthPasswordFile = ./path/to/the/password/file; + }; + }; + }; +} +``` + +## Extra configuration {#module-services-suwayomi-server-extra-config} + +Not all the configuration options are available directly in this module, but you can add the other options of suwayomi-server with: + +```nix +{ ... }: + +{ + services.suwayomi-server = { + enable = true; + + openFirewall = true; + + settings = { + server = { + port = 4567; + autoDownloadNewChapters = false; + maxSourcesInParallel" = 6; + }; + }; + }; +} +``` diff --git a/nixos/modules/services/web-apps/suwayomi-server.nix b/nixos/modules/services/web-apps/suwayomi-server.nix new file mode 100644 index 000000000000..c4c1540edbee --- /dev/null +++ b/nixos/modules/services/web-apps/suwayomi-server.nix @@ -0,0 +1,260 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.services.suwayomi-server; + inherit (lib) mkOption mdDoc mkEnableOption mkIf types; +in +{ + options = { + services.suwayomi-server = { + enable = mkEnableOption (mdDoc "Suwayomi, a free and open source manga reader server that runs extensions built for Tachiyomi."); + + package = lib.mkPackageOptionMD pkgs "suwayomi-server" { }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/suwayomi-server"; + example = "/var/data/mangas"; + description = mdDoc '' + The path to the data directory in which Suwayomi-Server will download scans. + ''; + }; + + user = mkOption { + type = types.str; + default = "suwayomi"; + example = "root"; + description = mdDoc '' + User account under which Suwayomi-Server runs. + ''; + }; + + group = mkOption { + type = types.str; + default = "suwayomi"; + example = "medias"; + description = mdDoc '' + Group under which Suwayomi-Server runs. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to open the firewall for the port in {option}`services.suwayomi-server.settings.server.port`. + ''; + }; + + settings = mkOption { + type = types.submodule { + freeformType = + let + recursiveAttrsType = with types; attrsOf (nullOr (oneOf [ + str + path + int + float + bool + (listOf str) + (recursiveAttrsType // { description = "instances of this type recursively"; }) + ])); + in + recursiveAttrsType; + options = { + server = { + ip = mkOption { + type = types.str; + default = "0.0.0.0"; + example = "127.0.0.1"; + description = mdDoc '' + The ip that Suwayomi will bind to. + ''; + }; + + port = mkOption { + type = types.port; + default = 8080; + example = 4567; + description = mdDoc '' + The port that Suwayomi will listen to. + ''; + }; + + basicAuthEnabled = mkEnableOption (mdDoc '' + Add basic access authentication to Suwayomi-Server. + Enabling this option is useful when hosting on a public network/the Internet + ''); + + basicAuthUsername = mkOption { + type = types.nullOr types.str; + default = null; + description = mdDoc '' + The username value that you have to provide when authenticating. + ''; + }; + + # NOTE: this is not a real upstream option + basicAuthPasswordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/secrets/suwayomi-server-password"; + description = mdDoc '' + The password file containing the value that you have to provide when authenticating. + ''; + }; + + downloadAsCbz = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Download chapters as `.cbz` files. + ''; + }; + + localSourcePath = mkOption { + type = types.path; + default = cfg.dataDir; + defaultText = lib.literalExpression "suwayomi-server.dataDir"; + example = "/var/data/local_mangas"; + description = mdDoc '' + Path to the local source folder. + ''; + }; + + systemTrayEnabled = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to enable a system tray icon, if possible. + ''; + }; + }; + }; + }; + description = mdDoc '' + Configuration to write to {file}`server.conf`. + See <https://github.com/Suwayomi/Suwayomi-Server/wiki/Configuring-Suwayomi-Server> for more information. + ''; + default = { }; + example = { + server.socksProxyEnabled = true; + server.socksProxyHost = "yourproxyhost.com"; + server.socksProxyPort = "8080"; + }; + }; + }; + }; + + config = mkIf cfg.enable { + + assertions = [{ + assertion = with cfg.settings.server; basicAuthEnabled -> (basicAuthUsername != null && basicAuthPasswordFile != null); + message = '' + [suwayomi-server]: the username and the password file cannot be null when the basic auth is enabled + ''; + }]; + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.server.port ]; + + users.groups = mkIf (cfg.group == "suwayomi") { + suwayomi = { }; + }; + + users.users = mkIf (cfg.user == "suwayomi") { + suwayomi = { + group = cfg.group; + # Need to set the user home because the package writes to ~/.local/Tachidesk + home = cfg.dataDir; + description = "Suwayomi Daemon user"; + isSystemUser = true; + }; + }; + + systemd.tmpfiles.settings."10-suwayomi-server" = { + "${cfg.dataDir}/.local/share/Tachidesk".d = { + mode = "0700"; + inherit (cfg) user group; + }; + }; + + systemd.services.suwayomi-server = + let + flattenConfig = prefix: config: + lib.foldl' + lib.mergeAttrs + { } + (lib.attrValues + (lib.mapAttrs + (k: v: + if !(lib.isAttrs v) + then { "${prefix}${k}" = v; } + else flattenConfig "${prefix}${k}." v + ) + config + ) + ); + + # HOCON is a JSON superset that suwayomi-server use for configuration + toHOCON = attr: + let + attrType = builtins.typeOf attr; + in + if builtins.elem attrType [ "string" "path" "int" "float" ] + then ''"${toString attr}"'' + else if attrType == "bool" + then lib.boolToString attr + else if attrType == "list" + then "[\n${lib.concatMapStringsSep ",\n" toHOCON attr}\n]" + else # attrs, lambda, null + throw '' + [suwayomi-server]: invalid config value type '${attrType}'. + ''; + + configFile = pkgs.writeText "server.conf" (lib.pipe cfg.settings [ + (settings: lib.recursiveUpdate settings { + server.basicAuthPasswordFile = null; + server.basicAuthPassword = + if settings.server.basicAuthEnabled + then "$TACHIDESK_SERVER_BASIC_AUTH_PASSWORD" + else null; + }) + (flattenConfig "") + (lib.filterAttrs (_: x: x != null)) + (lib.mapAttrsToList (name: value: ''${name} = ${toHOCON value}'')) + lib.concatLines + ]); + + in + { + description = "A free and open source manga reader server that runs extensions built for Tachiyomi."; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + + script = '' + ${lib.optionalString cfg.settings.server.basicAuthEnabled '' + export TACHIDESK_SERVER_BASIC_AUTH_PASSWORD="$(<${cfg.settings.server.basicAuthPasswordFile})" + ''} + ${lib.getExe pkgs.envsubst} -i ${configFile} -o ${cfg.dataDir}/.local/share/Tachidesk/server.conf + ${lib.getExe cfg.package} -Dsuwayomi.tachidesk.config.server.rootDir=${cfg.dataDir} + ''; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + + Type = "simple"; + Restart = "on-failure"; + + StateDirectory = mkIf (cfg.dataDir == "/var/lib/suwayomi-server") "suwayomi-server"; + }; + }; + }; + + meta = { + maintainers = with lib.maintainers; [ ratcornu ]; + doc = ./suwayomi-server.md; + }; +} diff --git a/nixos/modules/services/web-apps/wordpress.nix b/nixos/modules/services/web-apps/wordpress.nix index 002d6683b2ed..2f7306309d69 100644 --- a/nixos/modules/services/web-apps/wordpress.nix +++ b/nixos/modules/services/web-apps/wordpress.nix @@ -174,22 +174,22 @@ let List of path(s) to respective language(s) which are copied from the 'languages' directory. ''; example = literalExpression '' - [( + [ # Let's package the German language. # For other languages try to replace language and country code in the download URL with your desired one. # Reference https://translate.wordpress.org for available translations and # codes. - language-de = pkgs.stdenv.mkDerivation { + (pkgs.stdenv.mkDerivation { name = "language-de"; src = pkgs.fetchurl { url = "https://de.wordpress.org/wordpress-''${pkgs.wordpress.version}-de_DE.tar.gz"; # Name is required to invalidate the hash when wordpress is updated - name = "wordpress-''${pkgs.wordpress.version}-language-de" + name = "wordpress-''${pkgs.wordpress.version}-language-de"; sha256 = "sha256-dlas0rXTSV4JAl8f/UyMbig57yURRYRhTMtJwF9g8h0="; }; installPhase = "mkdir -p $out; cp -r ./wp-content/languages/* $out/"; - }; - )]; + }) + ]; ''; }; diff --git a/nixos/modules/services/web-servers/agate.nix b/nixos/modules/services/web-servers/agate.nix index dce425035ff7..e03174c87945 100644 --- a/nixos/modules/services/web-servers/agate.nix +++ b/nixos/modules/services/web-servers/agate.nix @@ -71,6 +71,7 @@ in systemd.services.agate = { description = "Agate"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; script = diff --git a/nixos/modules/services/web-servers/mighttpd2.nix b/nixos/modules/services/web-servers/mighttpd2.nix index bdd6d8b62aa3..bb75dc4f2ff4 100644 --- a/nixos/modules/services/web-servers/mighttpd2.nix +++ b/nixos/modules/services/web-servers/mighttpd2.nix @@ -101,6 +101,7 @@ in { ]; systemd.services.mighttpd2 = { description = "Mighttpd2 web server"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { diff --git a/nixos/modules/services/web-servers/minio.nix b/nixos/modules/services/web-servers/minio.nix index 6431db250476..be6946657e23 100644 --- a/nixos/modules/services/web-servers/minio.nix +++ b/nixos/modules/services/web-servers/minio.nix @@ -98,6 +98,7 @@ in services.minio = { description = "Minio Object Storage"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix index cc2c680b3342..fc9eb504ebf8 100644 --- a/nixos/modules/services/web-servers/traefik.nix +++ b/nixos/modules/services/web-servers/traefik.nix @@ -144,6 +144,7 @@ in { systemd.services.traefik = { description = "Traefik web server"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; startLimitIntervalSec = 86400; diff --git a/nixos/modules/services/web-servers/ttyd.nix b/nixos/modules/services/web-servers/ttyd.nix index 3b1d87ccb483..e545869ca432 100644 --- a/nixos/modules/services/web-servers/ttyd.nix +++ b/nixos/modules/services/web-servers/ttyd.nix @@ -180,10 +180,11 @@ in # Runs login which needs to be run as root # login: Cannot possibly work without effective root User = "root"; + LoadCredential = lib.optionalString (cfg.passwordFile != null) "TTYD_PASSWORD_FILE:${cfg.passwordFile}"; }; script = if cfg.passwordFile != null then '' - PASSWORD=$(cat ${escapeShellArg cfg.passwordFile}) + PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/TTYD_PASSWORD_FILE") ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \ --credential ${escapeShellArg cfg.username}:"$PASSWORD" \ ${pkgs.shadow}/bin/login diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index 6cd46f30373b..055afe95df60 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py @@ -15,6 +15,19 @@ import json from typing import NamedTuple, Dict, List from dataclasses import dataclass +# These values will be replaced with actual values during the package build +EFI_SYS_MOUNT_POINT = "@efiSysMountPoint@" +TIMEOUT = "@timeout@" +EDITOR = bool("@editor@") +CONSOLE_MODE = "@consoleMode@" +BOOTSPEC_TOOLS = "@bootspecTools@" +DISTRO_NAME = "@distroName@" +NIX = "@nix@" +SYSTEMD = "@systemd@" +CONFIGURATION_LIMIT = int("@configurationLimit@") +CAN_TOUCH_EFI_VARIABLES = "@canTouchEfiVariables@" +GRACEFUL = "@graceful@" +COPY_EXTRA_FILES = "@copyExtraFiles@" @dataclass class BootSpec: @@ -29,7 +42,6 @@ class BootSpec: initrdSecrets: str | None = None - libc = ctypes.CDLL("libc.so.6") class SystemIdentifier(NamedTuple): @@ -75,16 +87,16 @@ def generation_conf_filename(profile: str | None, generation: int, specialisatio def write_loader_conf(profile: str | None, generation: int, specialisation: str | None) -> None: - with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f: - if "@timeout@" != "": - f.write("timeout @timeout@\n") + with open(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", 'w') as f: + if TIMEOUT != "": + f.write(f"timeout {TIMEOUT}\n") f.write("default %s\n" % generation_conf_filename(profile, generation, specialisation)) - if not @editor@: + if not EDITOR: f.write("editor 0\n") - f.write("console-mode @consoleMode@\n") + f.write(f"console-mode {CONSOLE_MODE}\n") f.flush() os.fsync(f.fileno()) - os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf") + os.rename(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf") def get_bootspec(profile: str | None, generation: int) -> BootSpec: @@ -95,7 +107,7 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec: bootspec_json = json.load(boot_json_f) else: boot_json_str = subprocess.check_output([ - "@bootspecTools@/bin/synthesize", + f"{BOOTSPEC_TOOLS}/bin/synthesize", "--version", "1", system_directory, @@ -116,7 +128,7 @@ def copy_from_file(file: str, dry_run: bool = False) -> str: store_dir = os.path.basename(os.path.dirname(store_file_path)) efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix) if not dry_run: - copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path)) + copy_if_not_exists(store_file_path, f"{EFI_SYS_MOUNT_POINT}%s" % (efi_file_path)) return efi_file_path def write_entry(profile: str | None, generation: int, specialisation: str | None, @@ -126,13 +138,14 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None kernel = copy_from_file(bootspec.kernel) initrd = copy_from_file(bootspec.initrd) - title = "@distroName@{profile}{specialisation}".format( + title = "{name}{profile}{specialisation}".format( + name=DISTRO_NAME, profile=" [" + profile + "]" if profile else "", specialisation=" (%s)" % specialisation if specialisation else "") try: if bootspec.initrdSecrets is not None: - subprocess.check_call([bootspec.initrdSecrets, "@efiSysMountPoint@%s" % (initrd)]) + subprocess.check_call([bootspec.initrdSecrets, f"{EFI_SYS_MOUNT_POINT}%s" % (initrd)]) except subprocess.CalledProcessError: if current: print("failed to create initrd secrets!", file=sys.stderr) @@ -142,7 +155,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None f'for "{title} - Configuration {generation}", an older generation', file=sys.stderr) print("note: this is normal after having removed " "or renamed a file in `boot.initrd.secrets`", file=sys.stderr) - entry_file = "@efiSysMountPoint@/loader/entries/%s" % ( + entry_file = f"{EFI_SYS_MOUNT_POINT}/loader/entries/%s" % ( generation_conf_filename(profile, generation, specialisation)) tmp_path = "%s.tmp" % (entry_file) kernel_params = "init=%s " % bootspec.init @@ -167,7 +180,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None def get_generations(profile: str | None = None) -> list[SystemIdentifier]: gen_list = subprocess.check_output([ - "@nix@/bin/nix-env", + f"{NIX}/bin/nix-env", "--list-generations", "-p", "/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system"), @@ -176,7 +189,7 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]: gen_lines = gen_list.split('\n') gen_lines.pop() - configurationLimit = @configurationLimit@ + configurationLimit = CONFIGURATION_LIMIT configurations = [ SystemIdentifier( profile=profile, @@ -189,14 +202,14 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]: def remove_old_entries(gens: list[SystemIdentifier]) -> None: - rex_profile = re.compile(r"^@efiSysMountPoint@/loader/entries/nixos-(.*)-generation-.*\.conf$") - rex_generation = re.compile(r"^@efiSysMountPoint@/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$") + rex_profile = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$") + rex_generation = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$") known_paths = [] for gen in gens: bootspec = get_bootspec(gen.profile, gen.generation) known_paths.append(copy_from_file(bootspec.kernel, True)) known_paths.append(copy_from_file(bootspec.initrd, True)) - for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos*-generation-[1-9]*.conf"): + for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"): if rex_profile.match(path): prof = rex_profile.sub(r"\1", path) else: @@ -207,7 +220,7 @@ def remove_old_entries(gens: list[SystemIdentifier]) -> None: continue if not (prof, gen_number, None) in gens: os.unlink(path) - for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"): + for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/*"): if not path in known_paths and not os.path.isdir(path): os.unlink(path) @@ -230,7 +243,7 @@ def install_bootloader(args: argparse.Namespace) -> None: # Since systemd version 232 a machine ID is required and it might not # be there on newly installed systems, so let's generate one so that # bootctl can find it and we can also pass it to write_entry() later. - cmd = ["@systemd@/bin/systemd-machine-id-setup", "--print"] + cmd = [f"{SYSTEMD}/bin/systemd-machine-id-setup", "--print"] machine_id = subprocess.run( cmd, text=True, check=True, stdout=subprocess.PIPE ).stdout.rstrip() @@ -242,22 +255,22 @@ def install_bootloader(args: argparse.Namespace) -> None: # flags to pass to bootctl install/update bootctl_flags = [] - if "@canTouchEfiVariables@" != "1": + if CAN_TOUCH_EFI_VARIABLES != "1": bootctl_flags.append("--no-variables") - if "@graceful@" == "1": + if GRACEFUL == "1": bootctl_flags.append("--graceful") if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1": # bootctl uses fopen() with modes "wxe" and fails if the file exists. - if os.path.exists("@efiSysMountPoint@/loader/loader.conf"): - os.unlink("@efiSysMountPoint@/loader/loader.conf") + if os.path.exists(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf"): + os.unlink(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf") - subprocess.check_call(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@"] + bootctl_flags + ["install"]) + subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["install"]) else: # Update bootloader to latest if needed - available_out = subprocess.check_output(["@systemd@/bin/bootctl", "--version"], universal_newlines=True).split()[2] - installed_out = subprocess.check_output(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@", "status"], universal_newlines=True) + available_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", "--version"], universal_newlines=True).split()[2] + installed_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}", "status"], universal_newlines=True) # See status_binaries() in systemd bootctl.c for code which generates this installed_match = re.search(r"^\W+File:.*/EFI/(?:BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$", @@ -276,10 +289,10 @@ def install_bootloader(args: argparse.Namespace) -> None: if installed_version < available_version: print("updating systemd-boot from %s to %s" % (installed_version, available_version)) - subprocess.check_call(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@"] + bootctl_flags + ["update"]) + subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["update"]) - os.makedirs("@efiSysMountPoint@/efi/nixos", exist_ok=True) - os.makedirs("@efiSysMountPoint@/loader/entries", exist_ok=True) + os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos", exist_ok=True) + os.makedirs(f"{EFI_SYS_MOUNT_POINT}/loader/entries", exist_ok=True) gens = get_generations() for profile in get_profiles(): @@ -302,9 +315,9 @@ def install_bootloader(args: argparse.Namespace) -> None: else: raise e - for root, _, files in os.walk('@efiSysMountPoint@/efi/nixos/.extra-files', topdown=False): - relative_root = root.removeprefix("@efiSysMountPoint@/efi/nixos/.extra-files").removeprefix("/") - actual_root = os.path.join("@efiSysMountPoint@", relative_root) + for root, _, files in os.walk(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", topdown=False): + relative_root = root.removeprefix(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files").removeprefix("/") + actual_root = os.path.join(f"{EFI_SYS_MOUNT_POINT}", relative_root) for file in files: actual_file = os.path.join(actual_root, file) @@ -317,14 +330,14 @@ def install_bootloader(args: argparse.Namespace) -> None: os.rmdir(actual_root) os.rmdir(root) - os.makedirs("@efiSysMountPoint@/efi/nixos/.extra-files", exist_ok=True) + os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", exist_ok=True) - subprocess.check_call("@copyExtraFiles@") + subprocess.check_call(COPY_EXTRA_FILES) def main() -> None: - parser = argparse.ArgumentParser(description='Update @distroName@-related systemd-boot files') - parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default @distroName@ config to boot') + parser = argparse.ArgumentParser(description=f"Update {DISTRO_NAME}-related systemd-boot files") + parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot") args = parser.parse_args() try: @@ -334,9 +347,9 @@ def main() -> None: # it can leave the system in an unbootable state, when a crash/outage # happens shortly after an update. To decrease the likelihood of this # event sync the efi filesystem after each update. - rc = libc.syncfs(os.open("@efiSysMountPoint@", os.O_RDONLY)) + rc = libc.syncfs(os.open(f"{EFI_SYS_MOUNT_POINT}", os.O_RDONLY)) if rc != 0: - print("could not sync @efiSysMountPoint@: {}".format(os.strerror(rc)), file=sys.stderr) + print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr) if __name__ == '__main__': diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix index 9d55c21077d1..3b140726c2d6 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix @@ -81,7 +81,11 @@ in { type = types.bool; - description = lib.mdDoc "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager"; + description = lib.mdDoc '' + Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager. + For more information about systemd-boot: + https://www.freedesktop.org/wiki/Software/systemd/systemd-boot/ + ''; }; editor = mkOption { diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix index 538f71cc0b9a..c42c88163c56 100644 --- a/nixos/modules/system/boot/resolved.nix +++ b/nixos/modules/system/boot/resolved.nix @@ -95,6 +95,29 @@ in ''; }; + services.resolved.dnsovertls = mkOption { + default = "false"; + example = "true"; + type = types.enum [ "true" "opportunistic" "false" ]; + description = lib.mdDoc '' + If set to + - `"true"`: + all DNS lookups will be encrypted. This requires + that the DNS server supports DNS-over-TLS and + has a valid certificate. If the hostname was specified + via the `address#hostname` format in {option}`services.resolved.domains` + then the specified hostname is used to validate its certificate. + - `"opportunistic"`: + all DNS lookups will attempt to be encrypted, but will fallback + to unecrypted requests if the server does not support DNS-over-TLS. + Note that this mode does allow for a malicious party to conduct a + downgrade attack by immitating the DNS server and pretending to not + support encryption. + - `"false"`: + all DNS lookups are done unencrypted. + ''; + }; + services.resolved.extraConfig = mkOption { default = ""; type = types.lines; @@ -141,6 +164,7 @@ in "Domains=${concatStringsSep " " cfg.domains}"} LLMNR=${cfg.llmnr} DNSSEC=${cfg.dnssec} + DNSOverTLS=${cfg.dnsovertls} ${config.services.resolved.extraConfig} ''; diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 46c3f66f02dc..331ca5103ba6 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -451,20 +451,37 @@ in cfg.services ); - assertions = concatLists ( - mapAttrsToList - (name: service: - map (message: { - assertion = false; - inherit message; - }) (concatLists [ - (optional ((builtins.elem "network-interfaces.target" service.after) || (builtins.elem "network-interfaces.target" service.wants)) - "Service '${name}.service' is using the deprecated target network-interfaces.target, which no longer exists. Using network.target is recommended instead." - ) - ]) - ) - cfg.services - ); + assertions = let + mkOneAssert = typeStr: name: def: { + assertion = lib.elem "network-online.target" def.after -> lib.elem "network-online.target" (def.wants ++ def.requires ++ def.bindsTo); + message = "${name}.${typeStr} is ordered after 'network-online.target' but doesn't depend on it"; + }; + mkAsserts = typeStr: lib.mapAttrsToList (mkOneAssert typeStr); + mkMountAsserts = typeStr: map (m: mkOneAssert typeStr m.what m); + in mkMerge [ + (concatLists ( + mapAttrsToList + (name: service: + map (message: { + assertion = false; + inherit message; + }) (concatLists [ + (optional ((builtins.elem "network-interfaces.target" service.after) || (builtins.elem "network-interfaces.target" service.wants)) + "Service '${name}.service' is using the deprecated target network-interfaces.target, which no longer exists. Using network.target is recommended instead." + ) + ]) + ) + cfg.services + )) + (mkAsserts "target" cfg.targets) + (mkAsserts "service" cfg.services) + (mkAsserts "socket" cfg.sockets) + (mkAsserts "timer" cfg.timers) + (mkAsserts "path" cfg.paths) + (mkMountAsserts "mount" cfg.mounts) + (mkMountAsserts "automount" cfg.automounts) + (mkAsserts "slice" cfg.slices) + ]; system.build.units = cfg.units; @@ -641,7 +658,6 @@ in systemd.services.systemd-udev-settle.restartIfChanged = false; # Causes long delays in nixos-rebuild systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true; systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true; - systemd.targets.network-online.wantedBy = [ "multi-user.target" ]; systemd.services.systemd-importd.environment = proxy_env; systemd.services.systemd-pstore.wantedBy = [ "sysinit.target" ]; # see #81138 diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix index aa44f2642697..f0d9b95f81f6 100644 --- a/nixos/modules/virtualisation/amazon-image.nix +++ b/nixos/modules/virtualisation/amazon-image.nix @@ -71,6 +71,7 @@ in systemd.services.fetch-ec2-metadata = { wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = ["network-online.target"]; path = [ pkgs.curl ]; script = builtins.readFile ./ec2-metadata-fetcher.sh; diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix index a4a40346f093..07ed08ab2f84 100644 --- a/nixos/modules/virtualisation/oci-containers.nix +++ b/nixos/modules/virtualisation/oci-containers.nix @@ -267,6 +267,7 @@ let }; in { wantedBy = [] ++ optional (container.autoStart) "multi-user.target"; + wants = lib.optional (container.imageFile == null) "network-online.target"; after = lib.optionals (cfg.backend == "docker") [ "docker.service" "docker.socket" ] # if imageFile is not set, the service needs the network to download the image from the registry ++ lib.optionals (container.imageFile == null) [ "network-online.target" ] diff --git a/nixos/modules/virtualisation/podman/default.nix b/nixos/modules/virtualisation/podman/default.nix index ec0b713e58b3..47382f9beab0 100644 --- a/nixos/modules/virtualisation/podman/default.nix +++ b/nixos/modules/virtualisation/podman/default.nix @@ -150,26 +150,33 @@ in }; - config = lib.mkIf cfg.enable - { + config = + let + networkConfig = ({ + dns_enabled = false; + driver = "bridge"; + id = "0000000000000000000000000000000000000000000000000000000000000000"; + internal = false; + ipam_options = { driver = "host-local"; }; + ipv6_enabled = false; + name = "podman"; + network_interface = "podman0"; + subnets = [{ gateway = "10.88.0.1"; subnet = "10.88.0.0/16"; }]; + } // cfg.defaultNetwork.settings); + inherit (networkConfig) dns_enabled network_interface; + in + lib.mkIf cfg.enable { environment.systemPackages = [ cfg.package ] ++ lib.optional cfg.dockerCompat dockerCompat; # https://github.com/containers/podman/blob/097cc6eb6dd8e598c0e8676d21267b4edb11e144/docs/tutorials/basic_networking.md#default-network environment.etc."containers/networks/podman.json" = lib.mkIf (cfg.defaultNetwork.settings != { }) { - source = json.generate "podman.json" ({ - dns_enabled = false; - driver = "bridge"; - id = "0000000000000000000000000000000000000000000000000000000000000000"; - internal = false; - ipam_options = { driver = "host-local"; }; - ipv6_enabled = false; - name = "podman"; - network_interface = "podman0"; - subnets = [{ gateway = "10.88.0.1"; subnet = "10.88.0.0/16"; }]; - } // cfg.defaultNetwork.settings); + source = json.generate "podman.json" networkConfig; }; + # containers cannot reach aardvark-dns otherwise + networking.firewall.interfaces.${network_interface}.allowedUDPPorts = lib.mkIf dns_enabled [ 53 ]; + virtualisation.containers = { enable = true; # Enable common /etc/containers configuration containersConf.settings = { |