diff options
Diffstat (limited to 'nixos/modules')
116 files changed, 2757 insertions, 761 deletions
diff --git a/nixos/modules/config/ldap.nix b/nixos/modules/config/ldap.nix index d2f01fb87d32d..e374e4a7a27e9 100644 --- a/nixos/modules/config/ldap.nix +++ b/nixos/modules/config/ldap.nix @@ -226,18 +226,6 @@ in "ldap.conf" = ldapConfig; }; - system.activationScripts = mkIf (!cfg.daemon.enable) { - ldap = stringAfter [ "etc" "groups" "users" ] '' - if test -f "${cfg.bind.passwordFile}" ; then - umask 0077 - conf="$(mktemp)" - printf 'bindpw %s\n' "$(cat ${cfg.bind.passwordFile})" | - cat ${ldapConfig.source} - >"$conf" - mv -fT "$conf" /etc/ldap.conf - fi - ''; - }; - system.nssModules = mkIf cfg.nsswitch (singleton ( if cfg.daemon.enable then nss_pam_ldapd else nss_ldap )); @@ -258,42 +246,63 @@ in }; }; - systemd.services = mkIf cfg.daemon.enable { - nslcd = { - wantedBy = [ "multi-user.target" ]; - - preStart = '' - umask 0077 - conf="$(mktemp)" - { - cat ${nslcdConfig} - test -z '${cfg.bind.distinguishedName}' -o ! -f '${cfg.bind.passwordFile}' || - printf 'bindpw %s\n' "$(cat '${cfg.bind.passwordFile}')" - test -z '${cfg.daemon.rootpwmoddn}' -o ! -f '${cfg.daemon.rootpwmodpwFile}' || - printf 'rootpwmodpw %s\n' "$(cat '${cfg.daemon.rootpwmodpwFile}')" - } >"$conf" - mv -fT "$conf" /run/nslcd/nslcd.conf - ''; - - restartTriggers = [ - nslcdConfig - cfg.bind.passwordFile - cfg.daemon.rootpwmodpwFile - ]; - - serviceConfig = { - ExecStart = "${nslcdWrapped}/bin/nslcd"; - Type = "forking"; - Restart = "always"; - User = "nslcd"; - Group = "nslcd"; - RuntimeDirectory = [ "nslcd" ]; - PIDFile = "/run/nslcd/nslcd.pid"; - AmbientCapabilities = "CAP_SYS_RESOURCE"; + systemd.services = mkMerge [ + (mkIf (!cfg.daemon.enable) { + ldap-password = { + wantedBy = [ "sysinit.target" ]; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; + unitConfig.DefaultDependencies = false; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = '' + if test -f "${cfg.bind.passwordFile}" ; then + umask 0077 + conf="$(mktemp)" + printf 'bindpw %s\n' "$(cat ${cfg.bind.passwordFile})" | + cat ${ldapConfig.source} - >"$conf" + mv -fT "$conf" /etc/ldap.conf + fi + ''; }; - }; + }) + + (mkIf cfg.daemon.enable { + nslcd = { + wantedBy = [ "multi-user.target" ]; + + preStart = '' + umask 0077 + conf="$(mktemp)" + { + cat ${nslcdConfig} + test -z '${cfg.bind.distinguishedName}' -o ! -f '${cfg.bind.passwordFile}' || + printf 'bindpw %s\n' "$(cat '${cfg.bind.passwordFile}')" + test -z '${cfg.daemon.rootpwmoddn}' -o ! -f '${cfg.daemon.rootpwmodpwFile}' || + printf 'rootpwmodpw %s\n' "$(cat '${cfg.daemon.rootpwmodpwFile}')" + } >"$conf" + mv -fT "$conf" /run/nslcd/nslcd.conf + ''; - }; + restartTriggers = [ + nslcdConfig + cfg.bind.passwordFile + cfg.daemon.rootpwmodpwFile + ]; + + serviceConfig = { + ExecStart = "${nslcdWrapped}/bin/nslcd"; + Type = "forking"; + Restart = "always"; + User = "nslcd"; + Group = "nslcd"; + RuntimeDirectory = [ "nslcd" ]; + PIDFile = "/run/nslcd/nslcd.pid"; + AmbientCapabilities = "CAP_SYS_RESOURCE"; + }; + }; + }) + ]; }; diff --git a/nixos/modules/config/nix-channel.nix b/nixos/modules/config/nix-channel.nix index a7ca7a5c74a40..dd97cb730ae41 100644 --- a/nixos/modules/config/nix-channel.nix +++ b/nixos/modules/config/nix-channel.nix @@ -12,7 +12,6 @@ let mkDefault mkIf mkOption - stringAfter types ; diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix index ec26d4b12eff0..4727e5b85ef22 100644 --- a/nixos/modules/config/no-x-libs.nix +++ b/nixos/modules/config/no-x-libs.nix @@ -34,6 +34,8 @@ with lib; ffmpeg_5 = super.ffmpeg_5.override { ffmpegVariant = "headless"; }; # dep of graphviz, libXpm is optional for Xpm support gd = super.gd.override { withXorg = false; }; + ghostscript = super.ghostscript.override { cupsSupport = false; x11Support = false; }; + gjs = super.gjs.overrideAttrs { doCheck = false; installTests = false; }; # avoid test dependency on gtk3 gobject-introspection = super.gobject-introspection.override { x11Support = false; }; gpsd = super.gpsd.override { guiSupport = false; }; graphviz = super.graphviz-nox; @@ -44,6 +46,7 @@ with lib; }; imagemagick = super.imagemagick.override { libX11Support = false; libXtSupport = false; }; imagemagickBig = super.imagemagickBig.override { libX11Support = false; libXtSupport = false; }; + intel-vaapi-driver = super.intel-vaapi-driver.override { enableGui = false; }; libdevil = super.libdevil-nox; libextractor = super.libextractor.override { gtkSupport = false; }; libva = super.libva-minimal; @@ -72,7 +75,7 @@ with lib; qemu = super.qemu.override { gtkSupport = false; spiceSupport = false; sdlSupport = false; }; qrencode = super.qrencode.overrideAttrs (_: { doCheck = false; }); qt5 = super.qt5.overrideScope (const (super': { - qtbase = super'.qtbase.override { withGtk3 = false; }; + qtbase = super'.qtbase.override { withGtk3 = false; withQttranslation = false; }; })); stoken = super.stoken.override { withGTK3 = false; }; # translateManpages -> perlPackages.po4a -> texlive-combined-basic -> texlive-core-big -> libX11 diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix index 8989a64082643..21046d6f1697b 100644 --- a/nixos/modules/config/swap.nix +++ b/nixos/modules/config/swap.nix @@ -258,7 +258,8 @@ in # avoid this race condition. after = [ "systemd-modules-load.service" ]; wantedBy = [ "${realDevice'}.swap" ]; - before = [ "${realDevice'}.swap" ]; + before = [ "${realDevice'}.swap" "shutdown.target"]; + conflicts = [ "shutdown.target" ]; path = [ pkgs.util-linux pkgs.e2fsprogs ] ++ optional sw.randomEncryption.enable pkgs.cryptsetup; diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix index b779f12aca30b..bedba984a3c23 100644 --- a/nixos/modules/config/sysctl.nix +++ b/nixos/modules/config/sysctl.nix @@ -31,16 +31,18 @@ in }; in types.submodule { freeformType = types.attrsOf sysctlOption; - options."net.core.rmem_max" = mkOption { - type = types.nullOr highestValueType; - default = null; - description = lib.mdDoc "The maximum socket receive buffer size. In case of conflicting values, the highest will be used."; - }; + options = { + "net.core.rmem_max" = mkOption { + type = types.nullOr highestValueType; + default = null; + description = lib.mdDoc "The maximum receive socket buffer size in bytes. In case of conflicting values, the highest will be used."; + }; - options."net.core.wmem_max" = mkOption { - type = types.nullOr highestValueType; - default = null; - description = lib.mdDoc "The maximum socket send buffer size. In case of conflicting values, the highest will be used."; + "net.core.wmem_max" = mkOption { + type = types.nullOr highestValueType; + default = null; + description = lib.mdDoc "The maximum send socket buffer size in bytes. In case of conflicting values, the highest will be used."; + }; }; }; default = {}; diff --git a/nixos/modules/hardware/usb-storage.nix b/nixos/modules/hardware/usb-storage.nix index 9c1b7a125fd18..3cb2c60d7ccd5 100644 --- a/nixos/modules/hardware/usb-storage.nix +++ b/nixos/modules/hardware/usb-storage.nix @@ -14,7 +14,7 @@ with lib; config = mkIf config.hardware.usbStorage.manageStartStop { services.udev.extraRules = '' - ACTION=="add|change", SUBSYSTEM=="scsi_disk", DRIVERS=="usb-storage", ATTR{manage_start_stop}="1" + ACTION=="add|change", SUBSYSTEM=="scsi_disk", DRIVERS=="usb-storage", ATTR{manage_system_start_stop}="1" ''; }; } diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix index 605aa6ef8b88a..2a86280eec8cb 100644 --- a/nixos/modules/hardware/video/amdgpu-pro.nix +++ b/nixos/modules/hardware/video/amdgpu-pro.nix @@ -39,9 +39,10 @@ in hardware.firmware = [ package.fw ]; - system.activationScripts.setup-amdgpu-pro = '' - ln -sfn ${package}/opt/amdgpu{,-pro} /run - ''; + systemd.tmpfiles.settings.amdgpu-pro = { + "/run/amdgpu"."L+".argument = "${package}/opt/amdgpu"; + "/run/amdgpu-pro"."L+".argument = "${package}/opt/amdgpu-pro"; + }; system.requiredKernelConfig = with config.lib.kernelConfig; [ (isYes "DEVICE_PRIVATE") diff --git a/nixos/modules/i18n/input-method/fcitx5.nix b/nixos/modules/i18n/input-method/fcitx5.nix index 3d52c08888eae..530727f3f2928 100644 --- a/nixos/modules/i18n/input-method/fcitx5.nix +++ b/nixos/modules/i18n/input-method/fcitx5.nix @@ -19,6 +19,14 @@ in Enabled Fcitx5 addons. ''; }; + waylandFrontend = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Use the Wayland input method frontend. + See [Using Fcitx 5 on Wayland](https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland). + ''; + }; quickPhrase = mkOption { type = with types; attrsOf str; default = { }; @@ -118,10 +126,11 @@ in ]; environment.variables = { - GTK_IM_MODULE = "fcitx"; - QT_IM_MODULE = "fcitx"; XMODIFIERS = "@im=fcitx"; QT_PLUGIN_PATH = [ "${fcitx5Package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ]; + } // lib.optionalAttrs (!cfg.waylandFrontend) { + GTK_IM_MODULE = "fcitx"; + QT_IM_MODULE = "fcitx"; } // lib.optionalAttrs cfg.ignoreUserConfig { SKIP_FCITX_USER_PATH = "1"; }; diff --git a/nixos/modules/image/repart-image.nix b/nixos/modules/image/repart-image.nix new file mode 100644 index 0000000000000..b4a1dfe51ff3a --- /dev/null +++ b/nixos/modules/image/repart-image.nix @@ -0,0 +1,80 @@ +# This is an expression meant to be called from `./repart.nix`, it is NOT a +# NixOS module that can be imported. + +{ lib +, runCommand +, python3 +, black +, ruff +, mypy +, systemd +, fakeroot +, util-linux +, dosfstools +, mtools +, e2fsprogs +, squashfsTools +, erofs-utils +, btrfs-progs +, xfsprogs + + # arguments +, name +, fileSystems +, partitions +, split +, seed +, definitionsDirectory +}: + +let + amendRepartDefinitions = runCommand "amend-repart-definitions.py" + { + # TODO: ruff does not splice properly in nativeBuildInputs + depsBuildBuild = [ ruff ]; + nativeBuildInputs = [ python3 black mypy ]; + } '' + install ${./amend-repart-definitions.py} $out + patchShebangs --build $out + + black --check --diff $out + ruff --line-length 88 $out + mypy --strict $out + ''; + + fileSystemToolMapping = { + "vfat" = [ dosfstools mtools ]; + "ext4" = [ e2fsprogs.bin ]; + "squashfs" = [ squashfsTools ]; + "erofs" = [ erofs-utils ]; + "btrfs" = [ btrfs-progs ]; + "xfs" = [ xfsprogs ]; + }; + + fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems; +in + +runCommand name +{ + nativeBuildInputs = [ + systemd + fakeroot + util-linux + ] ++ fileSystemTools; +} '' + amendedRepartDefinitions=$(${amendRepartDefinitions} ${partitions} ${definitionsDirectory}) + + mkdir -p $out + cd $out + + unshare --map-root-user fakeroot systemd-repart \ + --dry-run=no \ + --empty=create \ + --size=auto \ + --seed="${seed}" \ + --definitions="$amendedRepartDefinitions" \ + --split="${lib.boolToString split}" \ + --json=pretty \ + image.raw \ + | tee repart-output.json +'' diff --git a/nixos/modules/image/repart.nix b/nixos/modules/image/repart.nix index 41e6110885b85..da4f45d9a6392 100644 --- a/nixos/modules/image/repart.nix +++ b/nixos/modules/image/repart.nix @@ -90,8 +90,10 @@ in }; package = lib.mkPackageOption pkgs "systemd-repart" { - default = "systemd"; - example = "pkgs.systemdMinimal.override { withCryptsetup = true; }"; + # We use buildPackages so that repart images are built with the build + # platform's systemd, allowing for cross-compiled systems to work. + default = [ "buildPackages" "systemd" ]; + example = "pkgs.buildPackages.systemdMinimal.override { withCryptsetup = true; }"; }; partitions = lib.mkOption { @@ -131,22 +133,10 @@ in system.build.image = let - fileSystemToolMapping = with pkgs; { - "vfat" = [ dosfstools mtools ]; - "ext4" = [ e2fsprogs.bin ]; - "squashfs" = [ squashfsTools ]; - "erofs" = [ erofs-utils ]; - "btrfs" = [ btrfs-progs ]; - "xfs" = [ xfsprogs ]; - }; - fileSystems = lib.filter (f: f != null) (lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions); - fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems; - - makeClosure = paths: pkgs.closureInfo { rootPaths = paths; }; # Add the closure of the provided Nix store paths to cfg.partitions so @@ -157,23 +147,8 @@ in { closure = "${makeClosure partitionConfig.storePaths}/store-paths"; } ); - finalPartitions = lib.mapAttrs addClosure cfg.partitions; - - amendRepartDefinitions = pkgs.runCommand "amend-repart-definitions.py" - { - nativeBuildInputs = with pkgs; [ black ruff mypy ]; - buildInputs = [ pkgs.python3 ]; - } '' - install ${./amend-repart-definitions.py} $out - patchShebangs --host $out - - black --check --diff $out - ruff --line-length 88 $out - mypy --strict $out - ''; - format = pkgs.formats.ini { }; definitionsDirectory = utils.systemdUtils.lib.definitions @@ -183,30 +158,11 @@ in partitions = pkgs.writeText "partitions.json" (builtins.toJSON finalPartitions); in - pkgs.runCommand cfg.name - { - nativeBuildInputs = [ - cfg.package - pkgs.fakeroot - pkgs.util-linux - ] ++ fileSystemTools; - } '' - amendedRepartDefinitions=$(${amendRepartDefinitions} ${partitions} ${definitionsDirectory}) - - mkdir -p $out - cd $out - - unshare --map-root-user fakeroot systemd-repart \ - --dry-run=no \ - --empty=create \ - --size=auto \ - --seed="${cfg.seed}" \ - --definitions="$amendedRepartDefinitions" \ - --split="${lib.boolToString cfg.split}" \ - --json=pretty \ - image.raw \ - | tee repart-output.json - ''; + pkgs.callPackage ./repart-image.nix { + systemd = cfg.package; + inherit (cfg) name split seed; + inherit fileSystems definitionsDirectory partitions; + }; meta.maintainers = with lib.maintainers; [ nikstur ]; diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix index 9ccc76a82c95a..a7d11370d445e 100644 --- a/nixos/modules/installer/tools/tools.nix +++ b/nixos/modules/installer/tools/tools.nix @@ -231,7 +231,8 @@ in # even if you've upgraded your system to a new NixOS release. # # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, - # so changing it will NOT upgrade your system. + # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how + # to actually do that. # # This value being lower than the current NixOS release does NOT mean your system is # out of date, out of support, or vulnerable. diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix index 46462c5abd435..f3e698468e642 100644 --- a/nixos/modules/misc/documentation.nix +++ b/nixos/modules/misc/documentation.nix @@ -77,7 +77,11 @@ let libPath = filter (pkgs.path + "/lib"); pkgsLibPath = filter (pkgs.path + "/pkgs/pkgs-lib"); nixosPath = filter (pkgs.path + "/nixos"); - modules = map (p: ''"${removePrefix "${modulesPath}/" (toString p)}"'') docModules.lazy; + modules = + "[ " + + concatMapStringsSep " " (p: ''"${removePrefix "${modulesPath}/" (toString p)}"'') docModules.lazy + + " ]"; + passAsFile = [ "modules" ]; } '' export NIX_STORE_DIR=$TMPDIR/store export NIX_STATE_DIR=$TMPDIR/state @@ -87,7 +91,7 @@ let --argstr libPath "$libPath" \ --argstr pkgsLibPath "$pkgsLibPath" \ --argstr nixosPath "$nixosPath" \ - --arg modules "[ $modules ]" \ + --arg modules "import $modulesPath" \ --argstr stateVersion "${options.system.stateVersion.default}" \ --argstr release "${config.system.nixos.release}" \ $nixosPath/lib/eval-cacheable-options.nix > $out \ diff --git a/nixos/modules/misc/mandoc.nix b/nixos/modules/misc/mandoc.nix index 9bcef5b1a09bd..73646a60aabb2 100644 --- a/nixos/modules/misc/mandoc.nix +++ b/nixos/modules/misc/mandoc.nix @@ -5,25 +5,39 @@ let cfg = config.documentation.man.mandoc; -in { + toMandocOutput = output: ( + lib.mapAttrsToList + ( + name: value: + if lib.isString value || lib.isPath value then "output ${name} ${value}" + else if lib.isInt value then "output ${name} ${builtins.toString value}" + else if lib.isBool value then lib.optionalString value "output ${name}" + else if value == null then "" + else throw "Unrecognized value type ${builtins.typeOf value} of key ${name} in mandoc output settings" + ) + output + ); +in +{ meta.maintainers = [ lib.maintainers.sternenseemann ]; options = { documentation.man.mandoc = { - enable = lib.mkEnableOption (lib.mdDoc "mandoc as the default man page viewer"); + enable = lib.mkEnableOption "mandoc as the default man page viewer"; manPath = lib.mkOption { type = with lib.types; listOf str; default = [ "share/man" ]; example = lib.literalExpression "[ \"share/man\" \"share/man/fr\" ]"; - description = lib.mdDoc '' - Change the manpath, i. e. the directories where - {manpage}`man(1)` + description = '' + Change the paths included in the MANPATH environment variable, + i. e. the directories where {manpage}`man(1)` looks for section-specific directories of man pages. You only need to change this setting if you want extra man pages (e. g. in non-english languages). All values must be strings that are a valid path from the target prefix (without including it). - The first value given takes priority. + The first value given takes priority. Note that this will not + add manpath directives to {manpage}`man.conf(5)`. ''; }; @@ -31,11 +45,122 @@ in { type = lib.types.package; default = pkgs.mandoc; defaultText = lib.literalExpression "pkgs.mandoc"; - description = lib.mdDoc '' + description = '' The `mandoc` derivation to use. Useful to override configuration options used for the package. ''; }; + + settings = lib.mkOption { + description = "Configuration for {manpage}`man.conf(5)`"; + default = { }; + type = lib.types.submodule { + options = { + manpath = lib.mkOption { + type = with lib.types; listOf str; + default = [ ]; + example = lib.literalExpression "[ \"/run/current-system/sw/share/man\" ]"; + description = '' + Override the default search path for {manpage}`man(1)`, + {manpage}`apropos(1)`, and {manpage}`makewhatis(8)`. It can be + used multiple times to specify multiple paths, with the order + determining the manual page search order. + This is not recommended in favor of + {option}`documentation.man.mandoc.manPath`, but if it's needed to + specify the manpath in this way, set + {option}`documentation.man.mandoc.manPath` to an empty list (`[]`). + ''; + }; + output.fragment = lib.mkEnableOption '' + Omit the <!DOCTYPE> declaration and the <html>, <head>, and <body> + elements and only emit the subtree below the <body> element in HTML + output of {manpage}`mandoc(1)`. The style argument will be ignored. + This is useful when embedding manual content within existing documents. + ''; + output.includes = lib.mkOption { + type = with lib.types; nullOr str; + default = null; + example = lib.literalExpression "../src/%I.html"; + description = '' + A string of relative path used as a template for the output path of + linked header files (usually via the In macro) in HTML output. + Instances of `%I` are replaced with the include filename. The + default is not to present a hyperlink. + ''; + }; + output.indent = lib.mkOption { + type = with lib.types; nullOr int; + default = null; + description = '' + Number of blank characters at the left margin for normal text, + default of `5` for {manpage}`mdoc(7)` and `7` for + {manpage}`man(7)`. Increasing this is not recommended; it may + result in degraded formatting, for example overfull lines or ugly + line breaks. When output is to a pager on a terminal that is less + than 66 columns wide, the default is reduced to three columns. + ''; + }; + output.man = lib.mkOption { + type = with lib.types; nullOr str; + default = null; + example = lib.literalExpression "../html%S/%N.%S.html"; + description = '' + A template for linked manuals (usually via the Xr macro) in HTML + output. Instances of ‘%N’ and ‘%S’ are replaced with the linked + manual's name and section, respectively. If no section is included, + section 1 is assumed. The default is not to present a hyperlink. + If two formats are given and a file %N.%S exists in the current + directory, the first format is used; otherwise, the second format is used. + ''; + }; + output.paper = lib.mkOption { + type = with lib.types; nullOr str; + default = null; + description = '' + This option is for generating PostScript and PDF output. The paper + size name may be one of `a3`, `a4`, `a5`, `legal`, or `letter`. + You may also manually specify dimensions as `NNxNN`, width by + height in millimetres. If an unknown value is encountered, letter + is used. Output pages default to letter sized and are rendered in + the Times font family, 11-point. Margins are calculated as 1/9 the + page length and width. Line-height is 1.4m. + ''; + }; + output.style = lib.mkOption { + type = with lib.types; nullOr path; + default = null; + description = '' + Path to the file used for an external style-sheet. This must be a + valid absolute or relative URI. + ''; + }; + output.toc = lib.mkEnableOption '' + In HTML output of {manpage}`mandoc(1)`, If an input file contains + at least two non-standard sections, print a table of contents near + the beginning of the output. + ''; + output.width = lib.mkOption { + type = with lib.types; nullOr int; + default = null; + description = '' + The ASCII and UTF-8 output width, default is `78`. When output is a + pager on a terminal that is less than 79 columns wide, the + default is reduced to one less than the terminal width. In any case, + lines that are output in literal mode are never wrapped and may + exceed the output width. + ''; + }; + }; + }; + }; + + extraConfig = lib.mkOption { + type = lib.types.lines; + default = ""; + description = '' + Extra configuration to write to {manpage}`man.conf(5)`. + ''; + }; }; }; @@ -43,21 +168,29 @@ in { environment = { systemPackages = [ cfg.package ]; - # tell mandoc about man pages - etc."man.conf".text = lib.concatMapStrings (path: '' - manpath /run/current-system/sw/${path} - '') cfg.manPath; + etc."man.conf".text = lib.concatStringsSep "\n" ( + (map (path: "manpath ${path}") cfg.settings.manpath) + ++ (toMandocOutput cfg.settings.output) + ++ [ cfg.extraConfig ] + ); # create mandoc.db for whatis(1), apropos(1) and man(1) -k # TODO(@sternenseemman): fix symlinked directories not getting indexed, # see: https://inbox.vuxu.org/mandoc-tech/20210906171231.GF83680@athene.usta.de/T/#e85f773c1781e3fef85562b2794f9cad7b2909a3c extraSetup = lib.mkIf config.documentation.man.generateCaches '' - ${makewhatis} -T utf8 ${ + for man_path in ${ lib.concatMapStringsSep " " (path: "$out/" + lib.escapeShellArg path - ) cfg.manPath - } + ) cfg.manPath} ${lib.concatMapStringsSep " " (path: + lib.escapeShellArg path) cfg.settings.manpath + } + do + [[ -d "$man_path" ]] && ${makewhatis} -T utf8 $man_path + done ''; + + # tell mandoc the paths containing man pages + profileRelativeSessionVariables."MANPATH" = map (path: if builtins.substring 0 1 path != "/" then "/${path}" else path) cfg.manPath; }; }; } diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b9dce250087b1..02832e656dc95 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -272,6 +272,7 @@ ./programs/virt-manager.nix ./programs/wavemon.nix ./programs/wayland/cardboard.nix + ./programs/wayland/labwc.nix ./programs/wayland/river.nix ./programs/wayland/sway.nix ./programs/wayland/waybar.nix @@ -344,6 +345,7 @@ ./services/audio/mopidy.nix ./services/audio/mpd.nix ./services/audio/mpdscribble.nix + ./services/audio/mympd.nix ./services/audio/navidrome.nix ./services/audio/networkaudiod.nix ./services/audio/roon-bridge.nix @@ -496,6 +498,7 @@ ./services/development/jupyterhub/default.nix ./services/development/livebook.nix ./services/development/lorri.nix + ./services/development/nixseparatedebuginfod.nix ./services/development/rstudio-server/default.nix ./services/development/zammad.nix ./services/display-managers/greetd.nix @@ -619,6 +622,7 @@ ./services/mail/public-inbox.nix ./services/mail/roundcube.nix ./services/mail/rspamd.nix + ./services/mail/rspamd-trainer.nix ./services/mail/rss2email.nix ./services/mail/schleuder.nix ./services/mail/spamassassin.nix @@ -721,6 +725,7 @@ ./services/misc/nzbget.nix ./services/misc/nzbhydra2.nix ./services/misc/octoprint.nix + ./services/misc/ollama.nix ./services/misc/ombi.nix ./services/misc/osrm.nix ./services/misc/owncast.nix @@ -829,6 +834,7 @@ ./services/monitoring/riemann.nix ./services/monitoring/scollector.nix ./services/monitoring/smartd.nix + ./services/monitoring/snmpd.nix ./services/monitoring/statsd.nix ./services/monitoring/sysstat.nix ./services/monitoring/teamviewer.nix @@ -946,6 +952,7 @@ ./services/networking/ghostunnel.nix ./services/networking/git-daemon.nix ./services/networking/globalprotect-vpn.nix + ./services/networking/gns3-server.nix ./services/networking/gnunet.nix ./services/networking/go-autoconfig.nix ./services/networking/go-neb.nix @@ -1171,6 +1178,7 @@ ./services/search/typesense.nix ./services/security/aesmd.nix ./services/security/authelia.nix + ./services/security/bitwarden-directory-connector-cli.nix ./services/security/certmgr.nix ./services/security/cfssl.nix ./services/security/clamav.nix @@ -1259,6 +1267,7 @@ ./services/web-apps/changedetection-io.nix ./services/web-apps/chatgpt-retrieval-plugin.nix ./services/web-apps/cloudlog.nix + ./services/web-apps/code-server.nix ./services/web-apps/convos.nix ./services/web-apps/dex.nix ./services/web-apps/discourse.nix diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix index 52750cd472dad..58f07b050b5c4 100644 --- a/nixos/modules/profiles/installation-device.nix +++ b/nixos/modules/profiles/installation-device.nix @@ -105,6 +105,8 @@ with lib; ]; boot.swraid.enable = true; + # remove warning about unset mail + boot.swraid.mdadmConf = "PROGRAM ${pkgs.coreutils}/bin/true"; # Show all debug messages from the kernel but don't log refused packets # because we have the firewall enabled. This makes installs from the diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix index 7d9491d1fc1f3..003cfdbfc8fad 100644 --- a/nixos/modules/programs/atop.nix +++ b/nixos/modules/programs/atop.nix @@ -137,6 +137,7 @@ in atop.preStart = '' set -e -u shopt -s nullglob + rm -f "$LOGPATH"/atop_*.new for logfile in "$LOGPATH"/atop_* do ${atop}/bin/atopconvert "$logfile" "$logfile".new @@ -144,9 +145,9 @@ in # false positives for atop-rotate.service if ! ${pkgs.diffutils}/bin/cmp -s "$logfile" "$logfile".new then - ${pkgs.coreutils}/bin/mv -v -f "$logfile".new "$logfile" + mv -v -f "$logfile".new "$logfile" else - ${pkgs.coreutils}/bin/rm -f "$logfile".new + rm -f "$logfile".new fi done ''; diff --git a/nixos/modules/programs/firefox.nix b/nixos/modules/programs/firefox.nix index 1edf935d1649e..29c567783e27a 100644 --- a/nixos/modules/programs/firefox.nix +++ b/nixos/modules/programs/firefox.nix @@ -284,6 +284,7 @@ in # Preferences are converted into a policy programs.firefox.policies = { + DisableAppUpdate = true; Preferences = (mapAttrs (_: value: { Value = value; Status = cfg.preferencesStatus; }) cfg.preferences); diff --git a/nixos/modules/programs/gamemode.nix b/nixos/modules/programs/gamemode.nix index c43e2c2296f5a..344f392852e2a 100644 --- a/nixos/modules/programs/gamemode.nix +++ b/nixos/modules/programs/gamemode.nix @@ -18,7 +18,7 @@ in settings = mkOption { type = settingsFormat.type; - default = {}; + default = { }; description = lib.mdDoc '' System-wide configuration for GameMode (/etc/gamemode.ini). See gamemoded(8) man page for available settings. diff --git a/nixos/modules/programs/gpaste.nix b/nixos/modules/programs/gpaste.nix index 074b4d59a365a..37172c9583a37 100644 --- a/nixos/modules/programs/gpaste.nix +++ b/nixos/modules/programs/gpaste.nix @@ -32,5 +32,7 @@ with lib; systemd.packages = [ pkgs.gnome.gpaste ]; # gnome-control-center crashes in Keyboard Shortcuts pane without the GSettings schemas. services.xserver.desktopManager.gnome.sessionPath = [ pkgs.gnome.gpaste ]; + # gpaste-reloaded applet doesn't work without the typelib + services.xserver.desktopManager.cinnamon.sessionPath = [ pkgs.gnome.gpaste ]; }; } diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix index 01ffd811e70e2..3568736854d8e 100644 --- a/nixos/modules/programs/mininet.nix +++ b/nixos/modules/programs/mininet.nix @@ -6,39 +6,6 @@ with lib; let cfg = config.programs.mininet; - - telnet = pkgs.runCommand "inetutils-telnet" - { } - '' - mkdir -p $out/bin - ln -s ${pkgs.inetutils}/bin/telnet $out/bin - ''; - - generatedPath = with pkgs; makeSearchPath "bin" [ - iperf - ethtool - iproute2 - socat - # mn errors out without a telnet binary - # pkgs.inetutils brings an undesired ifconfig into PATH see #43105 - nettools - telnet - ]; - - pyEnv = pkgs.python3.withPackages (ps: [ ps.mininet-python ]); - - mnexecWrapped = pkgs.runCommand "mnexec-wrapper" - { nativeBuildInputs = [ pkgs.makeWrapper pkgs.python3Packages.wrapPython ]; } - '' - makeWrapper ${pkgs.mininet}/bin/mnexec \ - $out/bin/mnexec \ - --prefix PATH : "${generatedPath}" - - makeWrapper ${pyEnv}/bin/mn \ - $out/bin/mn \ - --prefix PYTHONPATH : "${pyEnv}/${pyEnv.sitePackages}" \ - --prefix PATH : "${generatedPath}" - ''; in { options.programs.mininet.enable = mkEnableOption (lib.mdDoc "Mininet"); @@ -47,6 +14,6 @@ in virtualisation.vswitch.enable = true; - environment.systemPackages = [ mnexecWrapped ]; + environment.systemPackages = [ pkgs.mininet ]; }; } diff --git a/nixos/modules/programs/partition-manager.nix b/nixos/modules/programs/partition-manager.nix index c18598b7c25d6..cf0491ff028fc 100644 --- a/nixos/modules/programs/partition-manager.nix +++ b/nixos/modules/programs/partition-manager.nix @@ -14,6 +14,6 @@ with lib; config = mkIf config.programs.partition-manager.enable { services.dbus.packages = [ pkgs.libsForQt5.kpmcore ]; # `kpmcore` need to be installed to pull in polkit actions. - environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.partition-manager ]; + environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.libsForQt5.partitionmanager ]; }; } diff --git a/nixos/modules/programs/wayland/labwc.nix b/nixos/modules/programs/wayland/labwc.nix new file mode 100644 index 0000000000000..d0806c3aa5d0e --- /dev/null +++ b/nixos/modules/programs/wayland/labwc.nix @@ -0,0 +1,25 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.labwc; +in +{ + meta.maintainers = with lib.maintainers; [ AndersonTorres ]; + + options.programs.labwc = { + enable = lib.mkEnableOption (lib.mdDoc "labwc"); + package = lib.mkPackageOption pkgs "labwc" { }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + environment.systemPackages = [ cfg.package ]; + + xdg.portal.config.wlroots.default = lib.mkDefault [ "wlr" "gtk" ]; + + # To make a labwc session available for certain DMs like SDDM + services.xserver.displayManager.sessionPackages = [ cfg.package ]; + } + (import ./wayland-session.nix { inherit lib pkgs; }) + ]); +} diff --git a/nixos/modules/programs/wayland/sway.nix b/nixos/modules/programs/wayland/sway.nix index f96c833856dbb..57ee629b28810 100644 --- a/nixos/modules/programs/wayland/sway.nix +++ b/nixos/modules/programs/wayland/sway.nix @@ -26,13 +26,28 @@ let }; }; - defaultSwayPackage = pkgs.sway.override { - extraSessionCommands = cfg.extraSessionCommands; - extraOptions = cfg.extraOptions; - withBaseWrapper = cfg.wrapperFeatures.base; - withGtkWrapper = cfg.wrapperFeatures.gtk; - isNixOS = true; - }; + genFinalPackage = pkg: + let + expectedArgs = lib.naturalSort [ + "extraSessionCommands" + "extraOptions" + "withBaseWrapper" + "withGtkWrapper" + "isNixOS" + ]; + existedArgs = with lib; + naturalSort + (intersectLists expectedArgs (attrNames (functionArgs pkg.override))); + in if existedArgs != expectedArgs then + pkg + else + pkg.override { + extraSessionCommands = cfg.extraSessionCommands; + extraOptions = cfg.extraOptions; + withBaseWrapper = cfg.wrapperFeatures.base; + withGtkWrapper = cfg.wrapperFeatures.gtk; + isNixOS = true; + }; in { options.programs.sway = { enable = mkEnableOption (lib.mdDoc '' @@ -44,14 +59,16 @@ in { package = mkOption { type = with types; nullOr package; - default = defaultSwayPackage; + default = pkgs.sway; + apply = p: if p == null then null else genFinalPackage p; defaultText = literalExpression "pkgs.sway"; description = lib.mdDoc '' - Sway package to use. Will override the options - 'wrapperFeatures', 'extraSessionCommands', and 'extraOptions'. - Set to `null` to not add any Sway package to your - path. This should be done if you want to use the Home Manager Sway - module to install Sway. + Sway package to use. If the package does not contain the override arguments + `extraSessionCommands`, `extraOptions`, `withBaseWrapper`, `withGtkWrapper`, + `isNixOS`, then the module options {option}`wrapperFeatures`, + {option}`wrapperFeatures` and {option}`wrapperFeatures` will have no effect. + Set to `null` to not add any Sway package to your path. This should be done if + you want to use the Home Manager Sway module to install Sway. ''; }; diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix index 24b48338ed772..ea1af6c6e2f29 100644 --- a/nixos/modules/security/apparmor.nix +++ b/nixos/modules/security/apparmor.nix @@ -164,7 +164,8 @@ in "local-fs.target" "systemd-journald-audit.socket" ]; - before = [ "sysinit.target" ]; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; wantedBy = [ "multi-user.target" ]; unitConfig = { Description="Load AppArmor policies"; diff --git a/nixos/modules/security/auditd.nix b/nixos/modules/security/auditd.nix index db4b2701ee2e9..253ee1d4dd0e5 100644 --- a/nixos/modules/security/auditd.nix +++ b/nixos/modules/security/auditd.nix @@ -13,6 +13,8 @@ with lib; systemd.services.auditd = { description = "Linux Audit daemon"; wantedBy = [ "basic.target" ]; + before = [ "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; unitConfig = { ConditionVirtualization = "!container"; @@ -23,7 +25,7 @@ with lib; path = [ pkgs.audit ]; serviceConfig = { - ExecStartPre="${pkgs.coreutils}/bin/mkdir -p /var/log/audit"; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/log/audit"; ExecStart = "${pkgs.audit}/bin/auditd -l -n -s nochange"; }; }; diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix index 2a855a77e3a39..ef76bfeb6d66a 100644 --- a/nixos/modules/security/duosec.nix +++ b/nixos/modules/security/duosec.nix @@ -195,7 +195,8 @@ in systemd.services.login-duo = lib.mkIf cfg.ssh.enable { wantedBy = [ "sysinit.target" ]; - before = [ "sysinit.target" ]; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; unitConfig.DefaultDependencies = false; script = '' if test -f "${cfg.secretKeyFile}"; then @@ -216,7 +217,8 @@ in systemd.services.pam-duo = lib.mkIf cfg.ssh.enable { wantedBy = [ "sysinit.target" ]; - before = [ "sysinit.target" ]; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; unitConfig.DefaultDependencies = false; script = '' if test -f "${cfg.secretKeyFile}"; then diff --git a/nixos/modules/security/ipa.nix b/nixos/modules/security/ipa.nix index df59d1e754140..3bf8b11f86261 100644 --- a/nixos/modules/security/ipa.nix +++ b/nixos/modules/security/ipa.nix @@ -181,25 +181,33 @@ in { ''; }; - system.activationScripts.ipa = stringAfter ["etc"] '' - # libcurl requires a hard copy of the certificate - if ! ${pkgs.diffutils}/bin/diff ${cfg.certificate} /etc/ipa/ca.crt > /dev/null 2>&1; then - rm -f /etc/ipa/ca.crt - cp ${cfg.certificate} /etc/ipa/ca.crt - fi - - if [ ! -f /etc/krb5.keytab ]; then - cat <<EOF - - In order to complete FreeIPA integration, please join the domain by completing the following steps: - 1. Authenticate as an IPA user authorized to join new hosts, e.g. kinit admin@${cfg.realm} - 2. Join the domain and obtain the keytab file: ipa-join - 3. Install the keytab file: sudo install -m 600 krb5.keytab /etc/ - 4. Restart sssd systemd service: sudo systemctl restart sssd - - EOF - fi - ''; + systemd.services."ipa-activation" = { + wantedBy = [ "sysinit.target" ]; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; + unitConfig.DefaultDependencies = false; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = '' + # libcurl requires a hard copy of the certificate + if ! ${pkgs.diffutils}/bin/diff ${cfg.certificate} /etc/ipa/ca.crt > /dev/null 2>&1; then + rm -f /etc/ipa/ca.crt + cp ${cfg.certificate} /etc/ipa/ca.crt + fi + + if [ ! -f /etc/krb5.keytab ]; then + cat <<EOF + + In order to complete FreeIPA integration, please join the domain by completing the following steps: + 1. Authenticate as an IPA user authorized to join new hosts, e.g. kinit admin@${cfg.realm} + 2. Join the domain and obtain the keytab file: ipa-join + 3. Install the keytab file: sudo install -m 600 krb5.keytab /etc/ + 4. Restart sssd systemd service: sudo systemctl restart sssd + + EOF + fi + ''; + }; services.sssd.config = '' [domain/${cfg.domain}] diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index b5e5dcb24426a..111be7057afc0 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -654,8 +654,8 @@ let { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { config_file = "/etc/security/pam_mysql.conf"; }; } - { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; settings = { - file = lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles; + { name = "ssh_agent_auth"; enable = config.security.pam.sshAgentAuth.enable && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; settings = { + file = lib.concatStringsSep ":" config.security.pam.sshAgentAuth.authorizedKeysFiles; }; } (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; modulePath = "${pkgs.pam_p11}/lib/security/pam_p11.so"; args = [ "${pkgs.opensc}/lib/opensc-pkcs11.so" @@ -943,7 +943,7 @@ let value.source = pkgs.writeText "${name}.pam" service.text; }; - optionalSudoConfigForSSHAgentAuth = optionalString config.security.pam.enableSSHAgentAuth '' + optionalSudoConfigForSSHAgentAuth = optionalString config.security.pam.sshAgentAuth.enable '' # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic. Defaults env_keep+=SSH_AUTH_SOCK ''; @@ -956,6 +956,7 @@ in imports = [ (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ]) + (mkRenamedOptionModule [ "security" "pam" "enableSSHAgentAuth" ] [ "security" "pam" "sshAgentAuth" "enable" ]) ]; ###### interface @@ -1025,16 +1026,34 @@ in ''; }; - security.pam.enableSSHAgentAuth = mkOption { - type = types.bool; - default = false; - description = - lib.mdDoc '' - Enable sudo logins if the user's SSH agent provides a key - present in {file}`~/.ssh/authorized_keys`. - This allows machines to exclusively use SSH keys instead of - passwords. + security.pam.sshAgentAuth = { + enable = mkEnableOption '' + authenticating using a signature performed by the ssh-agent. + This allows using SSH keys exclusively, instead of passwords, for instance on remote machines + ''; + + authorizedKeysFiles = mkOption { + type = with types; listOf str; + description = '' + A list of paths to files in OpenSSH's `authorized_keys` format, containing + the keys that will be trusted by the `pam_ssh_agent_auth` module. + + The following patterns are expanded when interpreting the path: + - `%f` and `%H` respectively expand to the fully-qualified and short hostname ; + - `%u` expands to the username ; + - `~` or `%h` expands to the user's home directory. + + ::: {.note} + Specifying user-writeable files here result in an insecure configuration: a malicious process + can then edit such an authorized_keys file and bypass the ssh-agent-based authentication. + + See [issue #31611](https://github.com/NixOS/nixpkgs/issues/31611) + ::: ''; + example = [ "/etc/ssh/authorized_keys.d/%u" ]; + default = config.services.openssh.authorizedKeysFiles; + defaultText = literalExpression "config.services.openssh.authorizedKeysFiles"; + }; }; security.pam.enableOTPW = mkEnableOption (lib.mdDoc "the OTPW (one-time password) PAM module"); @@ -1456,8 +1475,25 @@ in `security.pam.zfs.enable` requires enabling ZFS (`boot.zfs.enabled` or `boot.zfs.enableUnstable`). ''; } + { + assertion = with config.security.pam.sshAgentAuth; enable -> authorizedKeysFiles != []; + message = '' + `security.pam.enableSSHAgentAuth` requires `services.openssh.authorizedKeysFiles` to be a non-empty list. + Did you forget to set `services.openssh.enable` ? + ''; + } ]; + warnings = optional + (with lib; with config.security.pam.sshAgentAuth; + enable && any (s: hasPrefix "%h" s || hasPrefix "~" s) authorizedKeysFiles) + ''config.security.pam.sshAgentAuth.authorizedKeysFiles contains files in the user's home directory. + + Specifying user-writeable files there result in an insecure configuration: + a malicious process can then edit such an authorized_keys file and bypass the ssh-agent-based authentication. + See https://github.com/NixOS/nixpkgs/issues/31611 + ''; + environment.systemPackages = # Include the PAM modules in the system path mostly for the manpages. [ pkgs.pam ] diff --git a/nixos/modules/security/sudo-rs.nix b/nixos/modules/security/sudo-rs.nix index f991675827efb..b4376562c34d7 100644 --- a/nixos/modules/security/sudo-rs.nix +++ b/nixos/modules/security/sudo-rs.nix @@ -6,8 +6,6 @@ let cfg = config.security.sudo-rs; - inherit (config.security.pam) enableSSHAgentAuth; - toUserString = user: if (isInt user) then "#${toString user}" else "${user}"; toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}"; diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix index 3dd5d2e525d91..6aa9445eab65e 100644 --- a/nixos/modules/security/sudo.nix +++ b/nixos/modules/security/sudo.nix @@ -6,8 +6,6 @@ let cfg = config.security.sudo; - inherit (config.security.pam) enableSSHAgentAuth; - toUserString = user: if (isInt user) then "#${toString user}" else "${user}"; toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}"; diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix index 250f9775be14d..a298686b34e97 100644 --- a/nixos/modules/security/wrappers/default.nix +++ b/nixos/modules/security/wrappers/default.nix @@ -278,7 +278,9 @@ in systemd.services.suid-sgid-wrappers = { description = "Create SUID/SGID Wrappers"; wantedBy = [ "sysinit.target" ]; - before = [ "sysinit.target" ]; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; + after = [ "systemd-sysusers.service" ]; unitConfig.DefaultDependencies = false; unitConfig.RequiresMountsFor = [ "/nix/store" "/run/wrappers" ]; serviceConfig.Type = "oneshot"; diff --git a/nixos/modules/security/wrappers/wrapper.nix b/nixos/modules/security/wrappers/wrapper.nix index 27d46c630af54..ca4b27bff1801 100644 --- a/nixos/modules/security/wrappers/wrapper.nix +++ b/nixos/modules/security/wrappers/wrapper.nix @@ -1,8 +1,8 @@ { stdenv, unsecvars, linuxHeaders, sourceProg, debug ? false }: # For testing: -# $ nix-build -E 'with import <nixpkgs> {}; pkgs.callPackage ./wrapper.nix { parentWrapperDir = "/run/wrappers"; debug = true; }' +# $ nix-build -E 'with import <nixpkgs> {}; pkgs.callPackage ./wrapper.nix { sourceProg = "${pkgs.hello}/bin/hello"; debug = true; }' stdenv.mkDerivation { - name = "security-wrapper"; + name = "security-wrapper-${baseNameOf sourceProg}"; buildInputs = [ linuxHeaders ]; dontUnpack = true; CFLAGS = [ diff --git a/nixos/modules/services/admin/pgadmin.nix b/nixos/modules/services/admin/pgadmin.nix index 390c80d1a2d42..5eaa911e37f18 100644 --- a/nixos/modules/services/admin/pgadmin.nix +++ b/nixos/modules/services/admin/pgadmin.nix @@ -3,7 +3,6 @@ with lib; let - pkg = pkgs.pgadmin4; cfg = config.services.pgadmin; _base = with types; [ int bool str ]; @@ -36,6 +35,8 @@ in default = 5050; }; + package = mkPackageOptionMD pkgs "pgadmin4" { }; + initialEmail = mkOption { description = lib.mdDoc "Initial email for the pgAdmin account"; type = types.str; @@ -150,7 +151,7 @@ in echo "$PW" # Retype password: echo "$PW" - ) | ${pkg}/bin/pgadmin4-setup + ) | ${cfg.package}/bin/pgadmin4-setup ''; restartTriggers = [ @@ -162,7 +163,7 @@ in DynamicUser = true; LogsDirectory = "pgadmin"; StateDirectory = "pgadmin"; - ExecStart = "${pkg}/bin/pgadmin4"; + ExecStart = "${cfg.package}/bin/pgadmin4"; }; }; diff --git a/nixos/modules/services/audio/mympd.nix b/nixos/modules/services/audio/mympd.nix new file mode 100644 index 0000000000000..f1c7197085d7d --- /dev/null +++ b/nixos/modules/services/audio/mympd.nix @@ -0,0 +1,129 @@ +{ pkgs, config, lib, ... }: + +let + cfg = config.services.mympd; +in { + options = { + + services.mympd = { + + enable = lib.mkEnableOption (lib.mdDoc "MyMPD server"); + + package = lib.mkPackageOption pkgs "mympd" {}; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc '' + Open ports needed for the functionality of the program. + ''; + }; + + extraGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "music" ]; + description = lib.mdDoc '' + Additional groups for the systemd service. + ''; + }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = with lib.types; attrsOf (nullOr (oneOf [ str bool int ])); + options = { + http_port = lib.mkOption { + type = lib.types.port; + description = lib.mdDoc '' + The HTTP port where mympd's web interface will be available. + + The HTTPS/SSL port can be configured via {option}`config`. + ''; + example = "8080"; + }; + + ssl = lib.mkOption { + type = lib.types.bool; + description = lib.mdDoc '' + Whether to enable listening on the SSL port. + + Refer to <https://jcorporation.github.io/myMPD/configuration/configuration-files#ssl-options> + for more information. + ''; + default = false; + }; + }; + }; + description = lib.mdDoc '' + Manages the configuration files declaratively. For all the configuration + options, see <https://jcorporation.github.io/myMPD/configuration/configuration-files>. + + Each key represents the "File" column from the upstream configuration table, and the + value is the content of that file. + ''; + }; + }; + + }; + + config = lib.mkIf cfg.enable { + systemd.services.mympd = { + # upstream service config: https://github.com/jcorporation/myMPD/blob/master/contrib/initscripts/mympd.service.in + after = [ "mpd.service" ]; + wantedBy = [ "multi-user.target" ]; + preStart = with lib; '' + config_dir="/var/lib/mympd/config" + mkdir -p "$config_dir" + + ${pipe cfg.settings [ + (mapAttrsToList (name: value: '' + echo -n "${if isBool value then boolToString value else toString value}" > "$config_dir/${name}" + '')) + (concatStringsSep "\n") + ]} + ''; + unitConfig = { + Description = "myMPD server daemon"; + Documentation = "man:mympd(1)"; + }; + serviceConfig = { + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + DynamicUser = true; + ExecStart = lib.getExe cfg.package; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictRealtime = true; + StateDirectory = "mympd"; + CacheDirectory = "mympd"; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK AF_UNIX"; + RestrictNamespaces = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + SupplementaryGroups = cfg.extraGroups; + }; + }; + + networking.firewall = lib.mkMerge [ + (lib.mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.settings.http_port ]; + }) + (lib.mkIf (cfg.openFirewall && cfg.settings.ssl && cfg.settings.ssl_port != null) { + allowedTCPPorts = [ cfg.settings.ssl_port ]; + }) + ]; + + }; + + meta.maintainers = [ lib.maintainers.eliandoran ]; + +} diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix index 393fe83f493f5..6f4455d3be605 100644 --- a/nixos/modules/services/backup/borgbackup.nix +++ b/nixos/modules/services/backup/borgbackup.nix @@ -143,20 +143,15 @@ let }; # Paths listed in ReadWritePaths must exist before service is started - mkActivationScript = name: cfg: + mkTmpfiles = name: cfg: let - install = "install -o ${cfg.user} -g ${cfg.group}"; - in - nameValuePair "borgbackup-job-${name}" (stringAfter [ "users" ] ('' - # Ensure that the home directory already exists - # We can't assert createHome == true because that's not the case for root - cd "${config.users.users.${cfg.user}.home}" - # Create each directory separately to prevent root owned parent dirs - ${install} -d .config .config/borg - ${install} -d .cache .cache/borg - '' + optionalString (isLocalPath cfg.repo && !cfg.removableDevice) '' - ${install} -d ${escapeShellArg cfg.repo} - '')); + settings = { inherit (cfg) user group; }; + in lib.nameValuePair "borgbackup-job-${name}" ({ + "${config.users.users."${cfg.user}".home}/.config/borg".d = settings; + "${config.users.users."${cfg.user}".home}/.cache/borg".d = settings; + } // optionalAttrs (isLocalPath cfg.repo && !cfg.removableDevice) { + "${cfg.repo}".d = settings; + }); mkPassAssertion = name: cfg: { assertion = with cfg.encryption; @@ -760,7 +755,7 @@ in { ++ mapAttrsToList mkSourceAssertions jobs ++ mapAttrsToList mkRemovableDeviceAssertions jobs; - system.activationScripts = mapAttrs' mkActivationScript jobs; + systemd.tmpfiles.settings = mapAttrs' mkTmpfiles jobs; systemd.services = # A job named "foo" is mapped to systemd.services.borgbackup-job-foo diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix index 56abeda3a5cdd..446d19b8fd5a0 100644 --- a/nixos/modules/services/continuous-integration/buildbot/master.nix +++ b/nixos/modules/services/continuous-integration/buildbot/master.nix @@ -305,5 +305,5 @@ in { '') ]; - meta.maintainers = with lib.maintainers; [ mic92 lopsided98 ]; + meta.maintainers = lib.teams.buildbot.members; } diff --git a/nixos/modules/services/continuous-integration/buildbot/worker.nix b/nixos/modules/services/continuous-integration/buildbot/worker.nix index 2a836c24dda36..9c7b2bdd06e02 100644 --- a/nixos/modules/services/continuous-integration/buildbot/worker.nix +++ b/nixos/modules/services/continuous-integration/buildbot/worker.nix @@ -188,6 +188,6 @@ in { }; }; - meta.maintainers = with lib.maintainers; [ ]; + meta.maintainers = lib.teams.buildbot.members; } diff --git a/nixos/modules/services/continuous-integration/buildkite-agents.nix b/nixos/modules/services/continuous-integration/buildkite-agents.nix index a35ca4168074f..2e488f83d4c3b 100644 --- a/nixos/modules/services/continuous-integration/buildkite-agents.nix +++ b/nixos/modules/services/continuous-integration/buildkite-agents.nix @@ -35,6 +35,12 @@ let type = lib.types.str; }; + extraGroups = lib.mkOption { + default = [ "keys" ]; + description = lib.mdDoc "Groups the user for this buildkite agent should belong to"; + type = lib.types.listOf lib.types.str; + }; + runtimePackages = lib.mkOption { default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]; defaultText = lib.literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]"; @@ -150,7 +156,7 @@ in home = cfg.dataDir; createHome = true; description = "Buildkite agent user"; - extraGroups = [ "keys" ]; + extraGroups = cfg.extraGroups; isSystemUser = true; group = "buildkite-agent-${name}"; }; diff --git a/nixos/modules/services/databases/aerospike.nix b/nixos/modules/services/databases/aerospike.nix index 373c8f4bffb0d..4923c0f00ddb4 100644 --- a/nixos/modules/services/databases/aerospike.nix +++ b/nixos/modules/services/databases/aerospike.nix @@ -108,6 +108,11 @@ in }; users.groups.aerospike.gid = config.ids.gids.aerospike; + boot.kernel.sysctl = { + "net.core.rmem_max" = mkDefault 15728640; + "net.core.wmem_max" = mkDefault 5242880; + }; + systemd.services.aerospike = rec { description = "Aerospike server"; @@ -131,14 +136,6 @@ in echo "kernel.shmmax too low, setting to 1GB" ${pkgs.procps}/bin/sysctl -w kernel.shmmax=1073741824 fi - if [ $(echo "$(cat /proc/sys/net/core/rmem_max) < 15728640" | ${pkgs.bc}/bin/bc) == "1" ]; then - echo "increasing socket buffer limit (/proc/sys/net/core/rmem_max): $(cat /proc/sys/net/core/rmem_max) -> 15728640" - echo 15728640 > /proc/sys/net/core/rmem_max - fi - if [ $(echo "$(cat /proc/sys/net/core/wmem_max) < 5242880" | ${pkgs.bc}/bin/bc) == "1" ]; then - echo "increasing socket buffer limit (/proc/sys/net/core/wmem_max): $(cat /proc/sys/net/core/wmem_max) -> 5242880" - echo 5242880 > /proc/sys/net/core/wmem_max - fi install -d -m0700 -o ${serviceConfig.User} -g ${serviceConfig.Group} "${cfg.workDir}" install -d -m0700 -o ${serviceConfig.User} -g ${serviceConfig.Group} "${cfg.workDir}/smd" install -d -m0700 -o ${serviceConfig.User} -g ${serviceConfig.Group} "${cfg.workDir}/udf" diff --git a/nixos/modules/services/development/livebook.md b/nixos/modules/services/development/livebook.md index 73ddc57f6179a..5012e977a4f7f 100644 --- a/nixos/modules/services/development/livebook.md +++ b/nixos/modules/services/development/livebook.md @@ -18,7 +18,7 @@ which runs the server. port = 20123; # See note below about security environmentFile = pkgs.writeText "livebook.env" '' - LIVEBOOK_PASSWORD = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + LIVEBOOK_PASSWORD = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ''; }; } @@ -37,3 +37,14 @@ A better approach would be to put the password in some secure user-readable location and set `environmentFile = /home/user/secure/livebook.env`. ::: + +### Extra dependencies {#module-services-livebook-extra-dependencies} + +By default, the Livebook service is run with minimum dependencies, but +some features require additional packages. For example, the machine +learning Kinos require `gcc` and `gnumake`. To add these, use +`extraPackages`: + +``` +services.livebook.extraPackages = with pkgs; [ gcc gnumake ]; +``` diff --git a/nixos/modules/services/development/livebook.nix b/nixos/modules/services/development/livebook.nix index 3991a4125ec39..75729ff28efaf 100644 --- a/nixos/modules/services/development/livebook.nix +++ b/nixos/modules/services/development/livebook.nix @@ -12,6 +12,8 @@ in # future, this can be changed to a system service. enableUserService = mkEnableOption "a user service for Livebook"; + package = mkPackageOption pkgs "livebook" { }; + environmentFile = mkOption { type = types.path; description = lib.mdDoc '' @@ -63,6 +65,15 @@ in } ''; }; + + extraPackages = mkOption { + type = with types; listOf package; + default = [ ]; + description = lib.mdDoc '' + Extra packages to make available to the Livebook service. + ''; + example = literalExpression "with pkgs; [ gcc gnumake ]"; + }; }; config = mkIf cfg.enableUserService { @@ -79,9 +90,9 @@ in sname = cfg.erlang_node_short_name; } // cfg.options); in - "${pkgs.livebook}/bin/livebook server ${args}"; + "${cfg.package}/bin/livebook server ${args}"; }; - path = [ pkgs.bash ]; + path = [ pkgs.bash ] ++ cfg.extraPackages; wantedBy = [ "default.target" ]; }; }; diff --git a/nixos/modules/services/development/nixseparatedebuginfod.nix b/nixos/modules/services/development/nixseparatedebuginfod.nix new file mode 100644 index 0000000000000..daf85153d339f --- /dev/null +++ b/nixos/modules/services/development/nixseparatedebuginfod.nix @@ -0,0 +1,105 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.services.nixseparatedebuginfod; + url = "127.0.0.1:${toString cfg.port}"; +in +{ + options = { + services.nixseparatedebuginfod = { + enable = lib.mkEnableOption "separatedebuginfod, a debuginfod server providing source and debuginfo for nix packages"; + port = lib.mkOption { + description = "port to listen"; + default = 1949; + type = lib.types.port; + }; + nixPackage = lib.mkOption { + type = lib.types.package; + default = pkgs.nix; + defaultText = lib.literalExpression "pkgs.nix"; + description = '' + The version of nix that nixseparatedebuginfod should use as client for the nix daemon. It is strongly advised to use nix version >= 2.18, otherwise some debug info may go missing. + ''; + }; + allowOldNix = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Do not fail evaluation when {option}`services.nixseparatedebuginfod.nixPackage` is older than nix 2.18. + ''; + }; + }; + }; + config = lib.mkIf cfg.enable { + assertions = [ { + assertion = cfg.allowOldNix || (lib.versionAtLeast cfg.nixPackage.version "2.18"); + message = "nixseparatedebuginfod works better when `services.nixseparatedebuginfod.nixPackage` is set to nix >= 2.18 (instead of ${cfg.nixPackage.name}). Set `services.nixseparatedebuginfod.allowOldNix` to bypass."; + } ]; + + systemd.services.nixseparatedebuginfod = { + wantedBy = [ "multi-user.target" ]; + wants = [ "nix-daemon.service" ]; + after = [ "nix-daemon.service" ]; + path = [ cfg.nixPackage ]; + serviceConfig = { + ExecStart = [ "${pkgs.nixseparatedebuginfod}/bin/nixseparatedebuginfod -l ${url}" ]; + Restart = "on-failure"; + CacheDirectory = "nixseparatedebuginfod"; + # nix does not like DynamicUsers in allowed-users + User = "nixseparatedebuginfod"; + Group = "nixseparatedebuginfod"; + + # hardening + # Filesystem stuff + ProtectSystem = "strict"; # Prevent writing to most of / + ProtectHome = true; # Prevent accessing /home and /root + PrivateTmp = true; # Give an own directory under /tmp + PrivateDevices = true; # Deny access to most of /dev + ProtectKernelTunables = true; # Protect some parts of /sys + ProtectControlGroups = true; # Remount cgroups read-only + RestrictSUIDSGID = true; # Prevent creating SETUID/SETGID files + PrivateMounts = true; # Give an own mount namespace + RemoveIPC = true; + UMask = "0077"; + + # Capabilities + CapabilityBoundingSet = ""; # Allow no capabilities at all + NoNewPrivileges = true; # Disallow getting more capabilities. This is also implied by other options. + + # Kernel stuff + ProtectKernelModules = true; # Prevent loading of kernel modules + SystemCallArchitectures = "native"; # Usually no need to disable this + ProtectKernelLogs = true; # Prevent access to kernel logs + ProtectClock = true; # Prevent setting the RTC + + # Networking + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + + # Misc + LockPersonality = true; # Prevent change of the personality + ProtectHostname = true; # Give an own UTS namespace + RestrictRealtime = true; # Prevent switching to RT scheduling + MemoryDenyWriteExecute = true; # Maybe disable this for interpreters like python + RestrictNamespaces = true; + }; + }; + + users.users.nixseparatedebuginfod = { + isSystemUser = true; + group = "nixseparatedebuginfod"; + }; + + users.groups.nixseparatedebuginfod = { }; + + nix.settings.extra-allowed-users = [ "nixseparatedebuginfod" ]; + + environment.variables.DEBUGINFOD_URLS = "http://${url}"; + + environment.systemPackages = [ + # valgrind support requires debuginfod-find on PATH + (lib.getBin pkgs.elfutils) + ]; + + environment.etc."gdb/gdbinit.d/nixseparatedebuginfod.gdb".text = "set debuginfod enabled on"; + + }; +} diff --git a/nixos/modules/services/hardware/kanata.nix b/nixos/modules/services/hardware/kanata.nix index 0b77bfbc33b3f..05e76d8432154 100644 --- a/nixos/modules/services/hardware/kanata.nix +++ b/nixos/modules/services/hardware/kanata.nix @@ -78,7 +78,13 @@ let mkName = name: "kanata-${name}"; mkDevices = devices: - optionalString ((length devices) > 0) "linux-dev ${concatStringsSep ":" devices}"; + let + devicesString = pipe devices [ + (map (device: "\"" + device + "\"")) + (concatStringsSep " ") + ]; + in + optionalString ((length devices) > 0) "linux-dev (${devicesString})"; mkConfig = name: keyboard: pkgs.writeText "${mkName name}-config.kdb" '' (defcfg diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix index 8408844c4f943..8f64afe60734c 100644 --- a/nixos/modules/services/hardware/sane.nix +++ b/nixos/modules/services/hardware/sane.nix @@ -4,7 +4,7 @@ with lib; let - pkg = pkgs.sane-backends.override { + pkg = config.hardware.sane.backends-package.override { scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable; scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package; }; @@ -57,6 +57,13 @@ in ''; }; + hardware.sane.backends-package = mkOption { + type = types.package; + default = pkgs.sane-backends; + defaultText = literalExpression "pkgs.sane-backends"; + description = lib.mdDoc "Backends driver package to use."; + }; + hardware.sane.snapshot = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/services/hardware/thermald.nix b/nixos/modules/services/hardware/thermald.nix index 7ae602823cd65..a4839f326cc45 100644 --- a/nixos/modules/services/hardware/thermald.nix +++ b/nixos/modules/services/hardware/thermald.nix @@ -19,6 +19,12 @@ in ''; }; + ignoreCpuidCheck = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to ignore the cpuid check to allow running on unsupported platforms"; + }; + configFile = mkOption { type = types.nullOr types.path; default = null; @@ -42,6 +48,7 @@ in ${cfg.package}/sbin/thermald \ --no-daemon \ ${optionalString cfg.debug "--loglevel=debug"} \ + ${optionalString cfg.ignoreCpuidCheck "--ignore-cpuid-check"} \ ${optionalString (cfg.configFile != null) "--config-file ${cfg.configFile}"} \ --dbus-enable \ --adaptive diff --git a/nixos/modules/services/hardware/vdr.nix b/nixos/modules/services/hardware/vdr.nix index afa64fa16c4a6..689d83f7eedcd 100644 --- a/nixos/modules/services/hardware/vdr.nix +++ b/nixos/modules/services/hardware/vdr.nix @@ -1,18 +1,15 @@ { config, lib, pkgs, ... }: - -with lib; - let cfg = config.services.vdr; - libDir = "/var/lib/vdr"; -in { - - ###### interface + inherit (lib) + mkEnableOption mkPackageOption mkOption types mkIf optional mdDoc; +in +{ options = { services.vdr = { - enable = mkEnableOption (lib.mdDoc "VDR. Please put config into ${libDir}"); + enable = mkEnableOption (mdDoc "Start VDR"); package = mkPackageOption pkgs "vdr" { example = "wrapVdr.override { plugins = with pkgs.vdrPlugins; [ hello ]; }"; @@ -21,58 +18,84 @@ in { videoDir = mkOption { type = types.path; default = "/srv/vdr/video"; - description = lib.mdDoc "Recording directory"; + description = mdDoc "Recording directory"; }; extraArguments = mkOption { type = types.listOf types.str; - default = []; - description = lib.mdDoc "Additional command line arguments to pass to VDR."; + default = [ ]; + description = mdDoc "Additional command line arguments to pass to VDR."; + }; + + enableLirc = mkEnableOption (mdDoc "LIRC"); + + user = mkOption { + type = types.str; + default = "vdr"; + description = mdDoc '' + User under which the VDR service runs. + ''; }; - enableLirc = mkEnableOption (lib.mdDoc "LIRC"); + group = mkOption { + type = types.str; + default = "vdr"; + description = mdDoc '' + Group under which the VDRvdr service runs. + ''; + }; }; + }; - ###### implementation + config = mkIf cfg.enable { - config = mkIf cfg.enable (mkMerge [{ systemd.tmpfiles.rules = [ - "d ${cfg.videoDir} 0755 vdr vdr -" - "Z ${cfg.videoDir} - vdr vdr -" + "d ${cfg.videoDir} 0755 ${cfg.user} ${cfg.group} -" + "Z ${cfg.videoDir} - ${cfg.user} ${cfg.group} -" ]; systemd.services.vdr = { description = "VDR"; wantedBy = [ "multi-user.target" ]; + wants = optional cfg.enableLirc "lircd.service"; + after = [ "network.target" ] + ++ optional cfg.enableLirc "lircd.service"; serviceConfig = { - ExecStart = '' - ${cfg.package}/bin/vdr \ - --video="${cfg.videoDir}" \ - --config="${libDir}" \ - ${escapeShellArgs cfg.extraArguments} - ''; - User = "vdr"; + ExecStart = + let + args = [ + "--video=${cfg.videoDir}" + ] + ++ optional cfg.enableLirc "--lirc=${config.passthru.lirc.socket}" + ++ cfg.extraArguments; + in + "${cfg.package}/bin/vdr ${lib.escapeShellArgs args}"; + User = cfg.user; + Group = cfg.group; CacheDirectory = "vdr"; StateDirectory = "vdr"; + RuntimeDirectory = "vdr"; Restart = "on-failure"; }; }; - users.users.vdr = { - group = "vdr"; - home = libDir; - isSystemUser = true; + environment.systemPackages = [ cfg.package ]; + + users.users = mkIf (cfg.user == "vdr") { + vdr = { + inherit (cfg) group; + home = "/run/vdr"; + isSystemUser = true; + extraGroups = [ + "video" + "audio" + ] + ++ optional cfg.enableLirc "lirc"; + }; }; - users.groups.vdr = {}; - } + users.groups = mkIf (cfg.group == "vdr") { vdr = { }; }; - (mkIf cfg.enableLirc { - services.lirc.enable = true; - users.users.vdr.extraGroups = [ "lirc" ]; - services.vdr.extraArguments = [ - "--lirc=${config.passthru.lirc.socket}" - ]; - })]); + }; } diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix index 8a277cea6e461..5d87fc87d4161 100644 --- a/nixos/modules/services/logging/logcheck.nix +++ b/nixos/modules/services/logging/logcheck.nix @@ -220,10 +220,16 @@ in logcheck = {}; }; - system.activationScripts.logcheck = '' - mkdir -m 700 -p /var/{lib,lock}/logcheck - chown ${cfg.user} /var/{lib,lock}/logcheck - ''; + systemd.tmpfiles.settings.logcheck = { + "/var/lib/logcheck".d = { + mode = "700"; + inherit (cfg) user; + }; + "/var/lock/logcheck".d = { + mode = "700"; + inherit (cfg) user; + }; + }; services.cron.systemCronJobs = let withTime = name: {timeArgs, ...}: timeArgs != null; diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix index 23c47aaca7e23..209e066a19ef8 100644 --- a/nixos/modules/services/mail/postfix.nix +++ b/nixos/modules/services/mail/postfix.nix @@ -747,7 +747,7 @@ in ${concatStringsSep "\n" (mapAttrsToList (to: from: '' ln -sf ${from} /var/lib/postfix/conf/${to} - ${pkgs.postfix}/bin/postalias /var/lib/postfix/conf/${to} + ${pkgs.postfix}/bin/postalias -o -p /var/lib/postfix/conf/${to} '') cfg.aliasFiles)} ${concatStringsSep "\n" (mapAttrsToList (to: from: '' ln -sf ${from} /var/lib/postfix/conf/${to} @@ -779,6 +779,19 @@ in ExecStart = "${pkgs.postfix}/bin/postfix start"; ExecStop = "${pkgs.postfix}/bin/postfix stop"; ExecReload = "${pkgs.postfix}/bin/postfix reload"; + + # Hardening + PrivateTmp = true; + PrivateDevices = true; + ProtectSystem = "full"; + CapabilityBoundingSet = [ "~CAP_NET_ADMIN CAP_SYS_ADMIN CAP_SYS_BOOT CAP_SYS_MODULE" ]; + MemoryDenyWriteExecute = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_NETLINK" "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; }; }; diff --git a/nixos/modules/services/mail/rspamd-trainer.nix b/nixos/modules/services/mail/rspamd-trainer.nix new file mode 100644 index 0000000000000..bb78ddf9dd471 --- /dev/null +++ b/nixos/modules/services/mail/rspamd-trainer.nix @@ -0,0 +1,76 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.rspamd-trainer; + format = pkgs.formats.toml { }; + +in { + options.services.rspamd-trainer = { + + enable = mkEnableOption (mdDoc "Spam/ham trainer for rspamd"); + + settings = mkOption { + default = { }; + description = mdDoc '' + IMAP authentication configuration for rspamd-trainer. For supplying + the IMAP password, use the `secrets` option. + ''; + type = types.submodule { + freeformType = format.type; + }; + example = literalExpression '' + { + HOST = "localhost"; + USERNAME = "spam@example.com"; + INBOXPREFIX = "INBOX/"; + } + ''; + }; + + secrets = lib.mkOption { + type = with types; listOf path; + description = lib.mdDoc '' + A list of files containing the various secrets. Should be in the + format expected by systemd's `EnvironmentFile` directory. For the + IMAP account password use `PASSWORD = mypassword`. + ''; + default = [ ]; + }; + + }; + + config = mkIf cfg.enable { + + systemd = { + services.rspamd-trainer = { + description = "Spam/ham trainer for rspamd"; + serviceConfig = { + ExecStart = "${pkgs.rspamd-trainer}/bin/rspamd-trainer"; + WorkingDirectory = "/var/lib/rspamd-trainer"; + StateDirectory = [ "rspamd-trainer/log" ]; + Type = "oneshot"; + DynamicUser = true; + EnvironmentFile = [ + ( format.generate "rspamd-trainer-env" cfg.settings ) + cfg.secrets + ]; + }; + }; + timers."rspamd-trainer" = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "10m"; + OnUnitActiveSec = "10m"; + Unit = "rspamd-trainer.service"; + }; + }; + }; + + }; + + meta.maintainers = with lib.maintainers; [ onny ]; + +} diff --git a/nixos/modules/services/matrix/matrix-sliding-sync.nix b/nixos/modules/services/matrix/matrix-sliding-sync.nix index 295be0c6bf167..8b22cd7dba802 100644 --- a/nixos/modules/services/matrix/matrix-sliding-sync.nix +++ b/nixos/modules/services/matrix/matrix-sliding-sync.nix @@ -1,10 +1,14 @@ { config, lib, pkgs, ... }: let - cfg = config.services.matrix-synapse.sliding-sync; + cfg = config.services.matrix-sliding-sync; in { - options.services.matrix-synapse.sliding-sync = { + imports = [ + (lib.mkRenamedOptionModule [ "services" "matrix-synapse" "sliding-sync" ] [ "services" "matrix-sliding-sync" ]) + ]; + + options.services.matrix-sliding-sync = { enable = lib.mkEnableOption (lib.mdDoc "sliding sync"); package = lib.mkPackageOption pkgs "matrix-sliding-sync" { }; @@ -83,6 +87,7 @@ in systemd.services.matrix-sliding-sync = rec { after = lib.optional cfg.createDatabase "postgresql.service" + ++ lib.optional config.services.dendrite.enable "dendrite.service" ++ lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit; wants = after; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 6756d59cf367c..d8e4aab4feea5 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -27,7 +27,13 @@ let encoding = "utf8"; pool = cfg.databasePool; } // cfg.extraDatabaseConfig; - in if lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) "15.0" then { + in if lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) "15.9" then { + production.main = val; + # Starting with GitLab 15.9, single connections were deprecated and will be + # removed in GitLab 17.0. The CI connection however requires database_tasks set + # to false. + production.ci = val // { database_tasks = false; }; + } else if lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) "15.0" then { production.main = val; } else { production = val; @@ -1348,11 +1354,12 @@ in { fi jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ - '.${if lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) "15.0" then "production.main" else "production"}.password = $ENV.db_password' \ + '.${if lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) "15.0" then "production.main" else "production"}.password = $ENV.db_password ${if lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) "15.9" then "| .production.ci.password = $ENV.db_password | .production.main as $main | del(.production.main) | .production |= {main: $main} + ." else ""}' \ >'${cfg.statePath}/config/database.yml' '' else '' jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ + '${if lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) "15.9" then ".production.main as $main | del(.production.main) | .production |= {main: $main} + ." else ""}' \ >'${cfg.statePath}/config/database.yml' '' } diff --git a/nixos/modules/services/misc/guix/default.nix b/nixos/modules/services/misc/guix/default.nix index 3e1a212693b90..7174ff36b7090 100644 --- a/nixos/modules/services/misc/guix/default.nix +++ b/nixos/modules/services/misc/guix/default.nix @@ -22,11 +22,19 @@ let }) (builtins.genList guixBuildUser numberOfUsers)); - # A set of Guix user profiles to be linked at activation. + # A set of Guix user profiles to be linked at activation. All of these should + # be default profiles managed by Guix CLI and the profiles are located in + # `${cfg.stateDir}/profiles/per-user/$USER/$PROFILE`. guixUserProfiles = { - # The current Guix profile that is created through `guix pull`. + # The default Guix profile managed by `guix pull`. Take note this should be + # the profile with the most precedence in `PATH` env to let users use their + # updated versions of `guix` CLI. "current-guix" = "\${XDG_CONFIG_HOME}/guix/current"; + # The default Guix home profile. This profile contains more than exports + # such as an activation script at `$GUIX_HOME_PROFILE/activate`. + "guix-home" = "$HOME/.guix-home/profile"; + # The default Guix profile similar to $HOME/.nix-profile from Nix. "guix-profile" = "$HOME/.guix-profile"; }; @@ -228,14 +236,8 @@ in description = "Guix daemon socket"; before = [ "multi-user.target" ]; listenStreams = [ "${cfg.stateDir}/guix/daemon-socket/socket" ]; - unitConfig = { - RequiresMountsFor = [ - cfg.storeDir - cfg.stateDir - ]; - ConditionPathIsReadWrite = "${cfg.stateDir}/guix/daemon-socket"; - }; - wantedBy = [ "socket.target" ]; + unitConfig.RequiresMountsFor = [ cfg.storeDir cfg.stateDir ]; + wantedBy = [ "sockets.target" ]; }; systemd.mounts = [{ @@ -262,20 +264,31 @@ in # ephemeral setups where only certain part of the filesystem is # persistent (e.g., "Erase my darlings"-type of setup). system.userActivationScripts.guix-activate-user-profiles.text = let + guixProfile = profile: "${cfg.stateDir}/guix/profiles/per-user/\${USER}/${profile}"; + linkProfile = profile: location: let + userProfile = guixProfile profile; + in '' + [ -d "${userProfile}" ] && ln -sfn "${userProfile}" "${location}" + ''; linkProfileToPath = acc: profile: location: let - guixProfile = "${cfg.stateDir}/guix/profiles/per-user/\${USER}/${profile}"; - in acc + '' - [ -d "${guixProfile}" ] && [ -L "${location}" ] || ln -sf "${guixProfile}" "${location}" - ''; + in acc + (linkProfile profile location); + + # This should contain export-only Guix user profiles. The rest of it is + # handled manually in the activation script. + guixUserProfiles' = lib.attrsets.removeAttrs guixUserProfiles [ "guix-home" ]; - activationScript = lib.foldlAttrs linkProfileToPath "" guixUserProfiles; + linkExportsScript = lib.foldlAttrs linkProfileToPath "" guixUserProfiles'; in '' # Don't export this please! It is only expected to be used for this # activation script and nothing else. XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config} # Linking the usual Guix profiles into the home directory. - ${activationScript} + ${linkExportsScript} + + # Activate all of the default Guix non-exports profiles manually. + ${linkProfile "guix-home" "$HOME/.guix-home"} + [ -L "$HOME/.guix-home" ] && "$HOME/.guix-home/activate" ''; # GUIX_LOCPATH is basically LOCPATH but for Guix libc which in turn used by diff --git a/nixos/modules/services/misc/llama-cpp.nix b/nixos/modules/services/misc/llama-cpp.nix new file mode 100644 index 0000000000000..4d76456fb2fd5 --- /dev/null +++ b/nixos/modules/services/misc/llama-cpp.nix @@ -0,0 +1,111 @@ +{ config, lib, pkgs, utils, ... }: + +let + cfg = config.services.llama-cpp; +in { + + options = { + + services.llama-cpp = { + enable = lib.mkEnableOption "LLaMA C++ server"; + + package = lib.mkPackageOption pkgs "llama-cpp" { }; + + model = lib.mkOption { + type = lib.types.path; + example = "/models/mistral-instruct-7b/ggml-model-q4_0.gguf"; + description = "Model path."; + }; + + extraFlags = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "Extra flags passed to llama-cpp-server."; + example = ["-c" "4096" "-ngl" "32" "--numa"]; + default = []; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + example = "0.0.0.0"; + description = "IP address the LLaMA C++ server listens on."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 8080; + description = "Listen port for LLaMA C++ server."; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Open ports in the firewall for LLaMA C++ server."; + }; + }; + + }; + + config = lib.mkIf cfg.enable { + + systemd.services.llama-cpp = { + description = "LLaMA C++ server"; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + + serviceConfig = { + Type = "idle"; + KillSignal = "SIGINT"; + ExecStart = "${cfg.package}/bin/llama-cpp-server --log-disable --host ${cfg.host} --port ${builtins.toString cfg.port} -m ${cfg.model} ${utils.escapeSystemdExecArgs cfg.extraFlags}"; + Restart = "on-failure"; + RestartSec = 300; + + # for GPU acceleration + PrivateDevices = false; + + # hardening + DynamicUser = true; + CapabilityBoundingSet = ""; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + NoNewPrivileges = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + MemoryDenyWriteExecute = true; + LockPersonality = true; + RemoveIPC = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + SystemCallErrorNumber = "EPERM"; + ProtectProc = "invisible"; + ProtectHostname = true; + ProcSubset = "pid"; + }; + }; + + networking.firewall = lib.mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + + }; + + meta.maintainers = with lib.maintainers; [ newam ]; +} diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix new file mode 100644 index 0000000000000..9794bbbec464c --- /dev/null +++ b/nixos/modules/services/misc/ollama.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: let + + cfg = config.services.ollama; + +in { + + options = { + services.ollama = { + enable = lib.mkEnableOption ( + lib.mdDoc "Server for local large language models" + ); + package = lib.mkPackageOption pkgs "ollama" { }; + }; + }; + + config = lib.mkIf cfg.enable { + + systemd = { + services.ollama = { + wantedBy = [ "multi-user.target" ]; + description = "Server for local large language models"; + after = [ "network.target" ]; + environment = { + HOME = "%S/ollama"; + OLLAMA_MODELS = "%S/ollama/models"; + }; + serviceConfig = { + ExecStart = "${lib.getExe cfg.package} serve"; + WorkingDirectory = "/var/lib/ollama"; + StateDirectory = [ "ollama" ]; + DynamicUser = true; + }; + }; + }; + + environment.systemPackages = [ cfg.package ]; + + }; + + meta.maintainers = with lib.maintainers; [ onny ]; + +} diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix index b3bc7d89009db..3c6832958f59a 100644 --- a/nixos/modules/services/misc/paperless.nix +++ b/nixos/modules/services/misc/paperless.nix @@ -10,7 +10,7 @@ let defaultFont = "${pkgs.liberation_ttf}/share/fonts/truetype/LiberationSerif-Regular.ttf"; # Don't start a redis instance if the user sets a custom redis connection - enableRedis = !hasAttr "PAPERLESS_REDIS" cfg.extraConfig; + enableRedis = !(cfg.settings ? PAPERLESS_REDIS); redisServer = config.services.redis.servers.paperless; env = { @@ -24,9 +24,11 @@ let PAPERLESS_TIME_ZONE = config.time.timeZone; } // optionalAttrs enableRedis { PAPERLESS_REDIS = "unix://${redisServer.unixSocket}"; - } // ( - lib.mapAttrs (_: toString) cfg.extraConfig - ); + } // (lib.mapAttrs (_: s: + if (lib.isAttrs s || lib.isList s) then builtins.toJSON s + else if lib.isBool s then lib.boolToString s + else toString s + ) cfg.settings); manage = pkgs.writeShellScript "manage" '' set -o allexport # Export the following env vars @@ -82,6 +84,7 @@ in imports = [ (mkRenamedOptionModule [ "services" "paperless-ng" ] [ "services" "paperless" ]) + (mkRenamedOptionModule [ "services" "paperless" "extraConfig" ] [ "services" "paperless" "settings" ]) ]; options.services.paperless = { @@ -160,32 +163,30 @@ in description = lib.mdDoc "Web interface port."; }; - # FIXME this should become an RFC42-style settings attr - extraConfig = mkOption { - type = types.attrs; + settings = mkOption { + type = lib.types.submodule { + freeformType = with lib.types; attrsOf (let + typeList = [ bool float int str path package ]; + in oneOf (typeList ++ [ (listOf (oneOf typeList)) (attrsOf (oneOf typeList)) ])); + }; default = { }; description = lib.mdDoc '' Extra paperless config options. - See [the documentation](https://docs.paperless-ngx.com/configuration/) - for available options. + See [the documentation](https://docs.paperless-ngx.com/configuration/) for available options. - Note that some options such as `PAPERLESS_CONSUMER_IGNORE_PATTERN` expect JSON values. Use `builtins.toJSON` to ensure proper quoting. + Note that some settings such as `PAPERLESS_CONSUMER_IGNORE_PATTERN` expect JSON values. + Settings declared as lists or attrsets will automatically be serialised into JSON strings for your convenience. ''; - example = literalExpression '' - { - PAPERLESS_OCR_LANGUAGE = "deu+eng"; - - PAPERLESS_DBHOST = "/run/postgresql"; - - PAPERLESS_CONSUMER_IGNORE_PATTERN = builtins.toJSON [ ".DS_STORE/*" "desktop.ini" ]; - - PAPERLESS_OCR_USER_ARGS = builtins.toJSON { - optimize = 1; - pdfa_image_compression = "lossless"; - }; + example = { + PAPERLESS_OCR_LANGUAGE = "deu+eng"; + PAPERLESS_DBHOST = "/run/postgresql"; + PAPERLESS_CONSUMER_IGNORE_PATTERN = [ ".DS_STORE/*" "desktop.ini" ]; + PAPERLESS_OCR_USER_ARGS = { + optimize = 1; + pdfa_image_compression = "lossless"; }; - ''; + }; }; user = mkOption { diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix index 3299b6404c2b5..7036a372d1ea8 100644 --- a/nixos/modules/services/misc/portunus.nix +++ b/nixos/modules/services/misc/portunus.nix @@ -102,7 +102,9 @@ in ldap = { package = mkOption { type = types.package; - # needs openldap built with a libxcrypt that support crypt sha256 until https://github.com/majewsky/portunus/issues/2 is solved + # needs openldap built with a libxcrypt that support crypt sha256 until users have had time to migrate to newer hashes + # Ref: <https://github.com/majewsky/portunus/issues/2> + # TODO: remove in NixOS 24.11 (cf. same note on pkgs/servers/portunus/default.nix) default = pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }; defaultText = lib.literalExpression "pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }"; description = lib.mdDoc "The OpenLDAP package to use."; @@ -247,6 +249,7 @@ in acmeDirectory = config.security.acme.certs."${cfg.domain}".directory; in { + PORTUNUS_SERVER_HTTP_SECURE = "true"; PORTUNUS_SLAPD_TLS_CA_CERTIFICATE = "/etc/ssl/certs/ca-certificates.crt"; PORTUNUS_SLAPD_TLS_CERTIFICATE = "${acmeDirectory}/cert.pem"; PORTUNUS_SLAPD_TLS_DOMAIN_NAME = cfg.domain; diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix index b517170cda216..c1209e34a92b5 100644 --- a/nixos/modules/services/misc/redmine.nix +++ b/nixos/modules/services/misc/redmine.nix @@ -53,7 +53,7 @@ in enable = mkEnableOption (lib.mdDoc "Redmine"); package = mkPackageOption pkgs "redmine" { - example = "redmine.override { ruby = pkgs.ruby_2_7; }"; + example = "redmine.override { ruby = pkgs.ruby_3_2; }"; }; user = mkOption { diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index 39abd293b2d18..35db8a7376b11 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -64,6 +64,7 @@ let "pgbouncer" "php-fpm" "pihole" + "ping" "postfix" "postgres" "process" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/kea.nix b/nixos/modules/services/monitoring/prometheus/exporters/kea.nix index 8b1cd47d0a409..3abb6ff6bdf8b 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/kea.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/kea.nix @@ -15,8 +15,8 @@ in { type = types.listOf types.str; example = literalExpression '' [ - "/run/kea-dhcp4/kea-dhcp4.socket" - "/run/kea-dhcp6/kea-dhcp6.socket" + "/run/kea/kea-dhcp4.socket" + "/run/kea/kea-dhcp6.socket" ] ''; description = lib.mdDoc '' @@ -31,13 +31,15 @@ in { ]; serviceConfig = { User = "kea"; + DynamicUser = true; ExecStart = '' ${pkgs.prometheus-kea-exporter}/bin/kea-exporter \ --address ${cfg.listenAddress} \ --port ${toString cfg.port} \ ${concatStringsSep " " cfg.controlSocketPaths} ''; - SupplementaryGroups = [ "kea" ]; + RuntimeDirectory = "kea"; + RuntimeDirectoryPreserve = true; RestrictAddressFamilies = [ # Need AF_UNIX to collect data "AF_UNIX" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/ping.nix b/nixos/modules/services/monitoring/prometheus/exporters/ping.nix new file mode 100644 index 0000000000000..af78b6bef6258 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/ping.nix @@ -0,0 +1,48 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.ping; + + settingsFormat = pkgs.formats.yaml {}; + configFile = settingsFormat.generate "config.yml" cfg.settings; +in +{ + port = 9427; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + + settings = mkOption { + type = settingsFormat.type; + default = {}; + + description = lib.mdDoc '' + Configuration for ping_exporter, see + <https://github.com/czerwonk/ping_exporter> + for supported values. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + # ping-exporter needs `CAP_NET_RAW` to run as non root https://github.com/czerwonk/ping_exporter#running-as-non-root-user + CapabilityBoundingSet = [ "CAP_NET_RAW" ]; + AmbientCapabilities = [ "CAP_NET_RAW" ]; + ExecStart = '' + ${pkgs.prometheus-ping-exporter}/bin/ping_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --config.path="${configFile}" \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/snmpd.nix b/nixos/modules/services/monitoring/snmpd.nix new file mode 100644 index 0000000000000..f2d3953e6a620 --- /dev/null +++ b/nixos/modules/services/monitoring/snmpd.nix @@ -0,0 +1,83 @@ +{ pkgs, config, lib, ... }: + +let + cfg = config.services.snmpd; + configFile = if cfg.configText != "" then + pkgs.writeText "snmpd.cfg" '' + ${cfg.configText} + '' else null; +in { + options.services.snmpd = { + enable = lib.mkEnableOption "snmpd"; + + package = lib.mkPackageOption pkgs "net-snmp" {}; + + listenAddress = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = lib.mdDoc '' + The address to listen on for SNMP and AgentX messages. + ''; + example = "127.0.0.1"; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 161; + description = lib.mdDoc '' + The port to listen on for SNMP and AgentX messages. + ''; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc '' + Open port in firewall for snmpd. + ''; + }; + + configText = lib.mkOption { + type = lib.types.lines; + default = ""; + description = lib.mdDoc '' + The contents of the snmpd.conf. If the {option}`configFile` option + is set, this value will be ignored. + + Note that the contents of this option will be added to the Nix + store as world-readable plain text, {option}`configFile` can be used in + addition to a secret management tool to protect sensitive data. + ''; + }; + + configFile = lib.mkOption { + type = lib.types.path; + default = configFile; + defaultText = lib.literalMD "The value of {option}`configText`."; + description = lib.mdDoc '' + Path to the snmpd.conf file. By default, if {option}`configText` is set, + a config file will be automatically generated. + ''; + }; + + }; + + config = lib.mkIf cfg.enable { + systemd.services."snmpd" = { + description = "Simple Network Management Protocol (SNMP) daemon."; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${lib.getExe' cfg.package "snmpd"} -f -Lo -c ${cfg.configFile} ${cfg.listenAddress}:${toString cfg.port}"; + }; + }; + + networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [ + cfg.port + ]; + }; + + meta.maintainers = [ lib.maintainers.eliandoran ]; + +} diff --git a/nixos/modules/services/monitoring/thanos.nix b/nixos/modules/services/monitoring/thanos.nix index 5baa0d8446e54..02502816ef5d7 100644 --- a/nixos/modules/services/monitoring/thanos.nix +++ b/nixos/modules/services/monitoring/thanos.nix @@ -394,9 +394,8 @@ let Maximum number of queries processed concurrently by query node. ''; - query.replica-labels = mkAttrsParam "query.replica-label" '' + query.replica-labels = mkListParam "query.replica-label" '' Labels to treat as a replica indicator along which data is - deduplicated. Still you will be able to query without deduplication using diff --git a/nixos/modules/services/network-filesystems/eris-server.nix b/nixos/modules/services/network-filesystems/eris-server.nix index 66eccfac408c4..104676a52c61f 100644 --- a/nixos/modules/services/network-filesystems/eris-server.nix +++ b/nixos/modules/services/network-filesystems/eris-server.nix @@ -3,6 +3,7 @@ let cfg = config.services.eris-server; stateDirectoryPath = "\${STATE_DIRECTORY}"; + nullOrStr = with lib.types; nullOr str; in { options.services.eris-server = { @@ -26,7 +27,7 @@ in { }; listenCoap = lib.mkOption { - type = lib.types.str; + type = nullOrStr; default = ":5683"; example = "[::1]:5683"; description = '' @@ -39,8 +40,8 @@ in { }; listenHttp = lib.mkOption { - type = lib.types.str; - default = ""; + type = nullOrStr; + default = null; example = "[::1]:8080"; description = "Server HTTP listen address. Do not listen by default."; }; @@ -58,8 +59,8 @@ in { }; mountpoint = lib.mkOption { - type = lib.types.str; - default = ""; + type = nullOrStr; + default = null; example = "/eris"; description = '' Mountpoint for FUSE namespace that exposes "urn:eris:…" files. @@ -69,33 +70,44 @@ in { }; config = lib.mkIf cfg.enable { + assertions = [{ + assertion = lib.strings.versionAtLeast cfg.package.version "20231219"; + message = + "Version of `config.services.eris-server.package` is incompatible with this module"; + }]; + systemd.services.eris-server = let - cmd = - "${cfg.package}/bin/eris-go server --coap '${cfg.listenCoap}' --http '${cfg.listenHttp}' ${ - lib.optionalString cfg.decode "--decode " - }${ - lib.optionalString (cfg.mountpoint != "") - ''--mountpoint "${cfg.mountpoint}" '' - }${lib.strings.escapeShellArgs cfg.backends}"; + cmd = "${cfg.package}/bin/eris-go server" + + (lib.optionalString (cfg.listenCoap != null) + " --coap '${cfg.listenCoap}'") + + (lib.optionalString (cfg.listenHttp != null) + " --http '${cfg.listenHttp}'") + + (lib.optionalString cfg.decode " --decode") + + (lib.optionalString (cfg.mountpoint != null) + " --mountpoint '${cfg.mountpoint}'"); in { description = "ERIS block server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - script = lib.mkIf (cfg.mountpoint != "") '' + environment.ERIS_STORE_URL = toString cfg.backends; + script = lib.mkIf (cfg.mountpoint != null) '' export PATH=${config.security.wrapperDir}:$PATH ${cmd} ''; serviceConfig = let - umounter = lib.mkIf (cfg.mountpoint != "") + umounter = lib.mkIf (cfg.mountpoint != null) "-${config.security.wrapperDir}/fusermount -uz ${cfg.mountpoint}"; - in { - ExecStartPre = umounter; - ExecStart = lib.mkIf (cfg.mountpoint == "") cmd; - ExecStopPost = umounter; - Restart = "always"; - RestartSec = 20; - AmbientCapabilities = "CAP_NET_BIND_SERVICE"; - }; + in if (cfg.mountpoint == null) then { + ExecStart = cmd; + } else + { + ExecStartPre = umounter; + ExecStopPost = umounter; + } // { + Restart = "always"; + RestartSec = 20; + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + }; }; }; diff --git a/nixos/modules/services/network-filesystems/kubo.nix b/nixos/modules/services/network-filesystems/kubo.nix index 8226fc614bc47..126e0902d5b4a 100644 --- a/nixos/modules/services/network-filesystems/kubo.nix +++ b/nixos/modules/services/network-filesystems/kubo.nix @@ -282,8 +282,9 @@ in environment.systemPackages = [ cfg.package ]; environment.variables.IPFS_PATH = fakeKuboRepo; - # https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size + # https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000; + boot.kernel.sysctl."net.core.wmem_max" = mkDefault 2500000; programs.fuse = mkIf cfg.autoMount { userAllowOther = true; diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix index 89b30996e8faa..08fc092e230c0 100644 --- a/nixos/modules/services/networking/avahi-daemon.nix +++ b/nixos/modules/services/networking/avahi-daemon.nix @@ -95,7 +95,6 @@ in ipv6 = mkOption { type = types.bool; default = false; - defaultText = literalExpression "config.networking.enableIPv6"; description = lib.mdDoc "Whether to use IPv6."; }; diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix index a67f0c5de9ba3..18f205b8d99ef 100644 --- a/nixos/modules/services/networking/ddclient.nix +++ b/nixos/modules/services/networking/ddclient.nix @@ -217,7 +217,7 @@ with lib; inherit RuntimeDirectory; inherit StateDirectory; Type = "oneshot"; - ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}"; + ExecStartPre = [ "!${pkgs.writeShellScript "ddclient-prestart" preStart}" ]; ExecStart = "${lib.getExe cfg.package} -file /run/${RuntimeDirectory}/ddclient.conf"; }; }; diff --git a/nixos/modules/services/networking/dnsmasq.md b/nixos/modules/services/networking/dnsmasq.md new file mode 100644 index 0000000000000..6fc9178b1c0d5 --- /dev/null +++ b/nixos/modules/services/networking/dnsmasq.md @@ -0,0 +1,68 @@ +# Dnsmasq {#module-services-networking-dnsmasq} + +Dnsmasq is an integrated DNS, DHCP and TFTP server for small networks. + +## Configuration {#module-services-networking-dnsmasq-configuration} + +### An authoritative DHCP and DNS server on a home network {#module-services-networking-dnsmasq-configuration-home} + +On a home network, you can use Dnsmasq as a DHCP and DNS server. New devices on +your network will be configured by Dnsmasq, and instructed to use it as the DNS +server by default. This allows you to rely on your own server to perform DNS +queries and caching, with DNSSEC enabled. + +The following example assumes that + +- you have disabled your router's integrated DHCP server, if it has one +- your router's address is set in [](#opt-networking.defaultGateway.address) +- your system's Ethernet interface is `eth0` +- you have configured the address(es) to forward DNS queries in [](#opt-networking.nameservers) + +```nix +{ + services.dnsmasq = { + enable = true; + settings = { + interface = "eth0"; + bind-interfaces = true; # Only bind to the specified interface + dhcp-authoritative = true; # Should be set when dnsmasq is definitely the only DHCP server on a network + + server = config.networking.nameservers; # Upstream dns servers to which requests should be forwarded + + dhcp-host = [ + # Give the current system a fixed address of 192.168.0.254 + "dc:a6:32:0b:ea:b9,192.168.0.254,${config.networking.hostName},infinite" + ]; + + dhcp-option = [ + # Address of the gateway, i.e. your router + "option:router,${config.networking.defaultGateway.address}" + ]; + + dhcp-range = [ + # Range of IPv4 addresses to give out + # <range start>,<range end>,<lease time> + "192.168.0.10,192.168.0.253,24h" + # Enable stateless IPv6 allocation + "::f,::ff,constructor:eth0,ra-stateless" + ]; + + dhcp-rapid-commit = true; # Faster DHCP negotiation for IPv6 + local-service = true; # Accept DNS queries only from hosts whose address is on a local subnet + log-queries = true; # Log results of all DNS queries + bogus-priv = true; # Don't forward requests for the local address ranges (192.168.x.x etc) to upstream nameservers + domain-needed = true; # Don't forward requests without dots or domain parts to upstream nameservers + + dnssec = true; # Enable DNSSEC + # DNSSEC trust anchor. Source: https://data.iana.org/root-anchors/root-anchors.xml + trust-anchor = ".,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"; + }; + }; +} +``` + +## References {#module-services-networking-dnsmasq-references} + +- Upstream website: <https://dnsmasq.org> +- Manpage: <https://dnsmasq.org/docs/dnsmasq-man.html> +- FAQ: <https://dnsmasq.org/docs/FAQ> diff --git a/nixos/modules/services/networking/dnsmasq.nix b/nixos/modules/services/networking/dnsmasq.nix index 8d1ca36c38ed2..d01a1b6707a53 100644 --- a/nixos/modules/services/networking/dnsmasq.nix +++ b/nixos/modules/services/networking/dnsmasq.nix @@ -181,4 +181,6 @@ in restartTriggers = [ config.environment.etc.hosts.source ]; }; }; + + meta.doc = ./dnsmasq.md; } diff --git a/nixos/modules/services/networking/firewall-iptables.nix b/nixos/modules/services/networking/firewall-iptables.nix index e900868387203..2d11517700086 100644 --- a/nixos/modules/services/networking/firewall-iptables.nix +++ b/nixos/modules/services/networking/firewall-iptables.nix @@ -308,8 +308,9 @@ in description = "Firewall"; wantedBy = [ "sysinit.target" ]; wants = [ "network-pre.target" ]; - before = [ "network-pre.target" ]; after = [ "systemd-modules-load.service" ]; + before = [ "network-pre.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; path = [ cfg.package ] ++ cfg.extraPackages; diff --git a/nixos/modules/services/networking/gns3-server.md b/nixos/modules/services/networking/gns3-server.md new file mode 100644 index 0000000000000..9320d914fbd3a --- /dev/null +++ b/nixos/modules/services/networking/gns3-server.md @@ -0,0 +1,31 @@ +# GNS3 Server {#module-services-gns3-server} + +[GNS3](https://www.gns3.com/), a network software emulator. + +## Basic Usage {#module-services-gns3-server-basic-usage} + +A minimal configuration looks like this: + +```nix +{ + services.gns3-server = { + enable = true; + + auth = { + enable = true; + user = "gns3"; + passwordFile = "/var/lib/secrets/gns3_password"; + }; + + ssl = { + enable = true; + certFile = "/var/lib/gns3/ssl/cert.pem"; + keyFile = "/var/lib/gns3/ssl/key.pem"; + }; + + dynamips.enable = true; + ubridge.enable = true; + vpcs.enable = true; + }; +} +``` diff --git a/nixos/modules/services/networking/gns3-server.nix b/nixos/modules/services/networking/gns3-server.nix new file mode 100644 index 0000000000000..25583765de672 --- /dev/null +++ b/nixos/modules/services/networking/gns3-server.nix @@ -0,0 +1,263 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.gns3-server; + + settingsFormat = pkgs.formats.ini { }; + configFile = settingsFormat.generate "gns3-server.conf" cfg.settings; + +in { + meta = { + doc = ./gns3-server.md; + maintainers = [ lib.maintainers.anthonyroussel ]; + }; + + options = { + services.gns3-server = { + enable = lib.mkEnableOption (lib.mdDoc "GNS3 Server daemon"); + + package = lib.mkPackageOptionMD pkgs "gns3-server" { }; + + auth = { + enable = lib.mkEnableOption (lib.mdDoc "password based HTTP authentication to access the GNS3 Server"); + + user = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "gns3"; + description = lib.mdDoc ''Username used to access the GNS3 Server.''; + }; + + passwordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/secrets/gns3-server-password"; + description = lib.mdDoc '' + A file containing the password to access the GNS3 Server. + + ::: {.warning} + This should be a string, not a nix path, since nix paths + are copied into the world-readable nix store. + ::: + ''; + }; + }; + + settings = lib.mkOption { + type = lib.types.submodule { freeformType = settingsFormat.type; }; + default = {}; + example = { host = "127.0.0.1"; port = 3080; }; + description = lib.mdDoc '' + The global options in `config` file in ini format. + + Refer to <https://docs.gns3.com/docs/using-gns3/administration/gns3-server-configuration-file/> + for all available options. + ''; + }; + + log = { + file = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = "/var/log/gns3/server.log"; + description = lib.mdDoc ''Path of the file GNS3 Server should log to.''; + }; + + debug = lib.mkEnableOption (lib.mdDoc "debug logging"); + }; + + ssl = { + enable = lib.mkEnableOption (lib.mdDoc "SSL encryption"); + + certFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/gns3/ssl/server.pem"; + description = lib.mdDoc '' + Path to the SSL certificate file. This certificate will + be offered to, and may be verified by, clients. + ''; + }; + + keyFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/gns3/ssl/server.key"; + description = lib.mdDoc "Private key file for the certificate."; + }; + }; + + dynamips = { + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable Dynamips support.''); + package = lib.mkPackageOptionMD pkgs "dynamips" { }; + }; + + ubridge = { + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable uBridge support.''); + package = lib.mkPackageOptionMD pkgs "ubridge" { }; + }; + + vpcs = { + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable VPCS support.''); + package = lib.mkPackageOptionMD pkgs "vpcs" { }; + }; + }; + }; + + config = let + flags = { + enableDocker = config.virtualisation.docker.enable; + enableLibvirtd = config.virtualisation.libvirtd.enable; + }; + + in lib.mkIf cfg.enable { + assertions = [ + { + assertion = cfg.ssl.enable -> cfg.ssl.certFile != null; + message = "Please provide a certificate to use for SSL encryption."; + } + { + assertion = cfg.ssl.enable -> cfg.ssl.keyFile != null; + message = "Please provide a private key to use for SSL encryption."; + } + { + assertion = cfg.auth.enable -> cfg.auth.user != null; + message = "Please provide a username to use for HTTP authentication."; + } + { + assertion = cfg.auth.enable -> cfg.auth.passwordFile != null; + message = "Please provide a password file to use for HTTP authentication."; + } + ]; + + users.groups.ubridge = lib.mkIf cfg.ubridge.enable { }; + + security.wrappers.ubridge = lib.mkIf cfg.ubridge.enable { + capabilities = "cap_net_raw,cap_net_admin=eip"; + group = "ubridge"; + owner = "root"; + permissions = "u=rwx,g=rx,o=r"; + source = lib.getExe cfg.ubridge.package; + }; + + services.gns3-server.settings = lib.mkMerge [ + { + Server = { + appliances_path = lib.mkDefault "/var/lib/gns3/appliances"; + configs_path = lib.mkDefault "/var/lib/gns3/configs"; + images_path = lib.mkDefault "/var/lib/gns3/images"; + projects_path = lib.mkDefault "/var/lib/gns3/projects"; + symbols_path = lib.mkDefault "/var/lib/gns3/symbols"; + }; + } + (lib.mkIf (cfg.ubridge.enable) { + Server.ubridge_path = lib.mkDefault (lib.getExe cfg.ubridge.package); + }) + (lib.mkIf (cfg.auth.enable) { + Server = { + auth = lib.mkDefault (lib.boolToString cfg.auth.enable); + user = lib.mkDefault cfg.auth.user; + password = lib.mkDefault "@AUTH_PASSWORD@"; + }; + }) + (lib.mkIf (cfg.vpcs.enable) { + VPCS.vpcs_path = lib.mkDefault (lib.getExe cfg.vpcs.package); + }) + (lib.mkIf (cfg.dynamips.enable) { + Dynamips.dynamips_path = lib.mkDefault (lib.getExe cfg.dynamips.package); + }) + ]; + + systemd.services.gns3-server = let + commandArgs = lib.cli.toGNUCommandLineShell { } { + config = "/etc/gns3/gns3_server.conf"; + pid = "/run/gns3/server.pid"; + log = cfg.log.file; + ssl = cfg.ssl.enable; + # These are implicitly not set if `null` + certfile = cfg.ssl.certFile; + certkey = cfg.ssl.keyFile; + }; + in + { + description = "GNS3 Server"; + + after = [ "network.target" "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; + + # configFile cannot be stored in RuntimeDirectory, because GNS3 + # uses the `--config` base path to stores supplementary configuration files at runtime. + # + preStart = '' + install -m660 ${configFile} /etc/gns3/gns3_server.conf + + ${lib.optionalString cfg.auth.enable '' + ${pkgs.replace-secret}/bin/replace-secret \ + '@AUTH_PASSWORD@' \ + "''${CREDENTIALS_DIRECTORY}/AUTH_PASSWORD" \ + /etc/gns3/gns3_server.conf + ''} + ''; + + path = lib.optional flags.enableLibvirtd pkgs.qemu; + + reloadTriggers = [ configFile ]; + + serviceConfig = { + ConfigurationDirectory = "gns3"; + ConfigurationDirectoryMode = "0750"; + DynamicUser = true; + Environment = "HOME=%S/gns3"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStart = "${lib.getExe cfg.package} ${commandArgs}"; + Group = "gns3"; + LimitNOFILE = 16384; + LoadCredential = lib.mkIf cfg.auth.enable [ "AUTH_PASSWORD:${cfg.auth.passwordFile}" ]; + LogsDirectory = "gns3"; + LogsDirectoryMode = "0750"; + PIDFile = "/run/gns3/server.pid"; + Restart = "on-failure"; + RestartSec = 5; + RuntimeDirectory = "gns3"; + StateDirectory = "gns3"; + StateDirectoryMode = "0750"; + SupplementaryGroups = lib.optional flags.enableDocker "docker" + ++ lib.optional flags.enableLibvirtd "libvirtd" + ++ lib.optional cfg.ubridge.enable "ubridge"; + User = "gns3"; + WorkingDirectory = "%S/gns3"; + + # Hardening + DeviceAllow = lib.optional flags.enableLibvirtd "/dev/kvm"; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateTmp = true; + PrivateUsers = true; + # Don't restrict ProcSubset because python3Packages.psutil requires read access to /proc/stat + # ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + "AF_UNIX" + "AF_PACKET" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + UMask = "0077"; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/harmonia.nix b/nixos/modules/services/networking/harmonia.nix index d0f4a8a6e6333..b384ac9261376 100644 --- a/nixos/modules/services/networking/harmonia.nix +++ b/nixos/modules/services/networking/harmonia.nix @@ -55,6 +55,7 @@ in ExecStart = lib.getExe cfg.package; User = "harmonia"; Group = "harmonia"; + Restart = "on-failure"; PrivateUsers = true; DeviceAllow = [ "" ]; UMask = "0066"; diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix index 2f922a026a3a9..5ca705976c413 100644 --- a/nixos/modules/services/networking/kea.nix +++ b/nixos/modules/services/networking/kea.nix @@ -254,6 +254,8 @@ in DynamicUser = true; User = "kea"; ConfigurationDirectory = "kea"; + RuntimeDirectory = "kea"; + RuntimeDirectoryPreserve = true; StateDirectory = "kea"; UMask = "0077"; }; @@ -288,8 +290,8 @@ in ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-ctrl-agent"; - KEA_LOCKFILE_DIR = "/run/kea-ctrl-agent"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -300,7 +302,6 @@ in ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.ctrl-agent.extraArgs}"; KillMode = "process"; Restart = "on-failure"; - RuntimeDirectory = "kea-ctrl-agent"; } // commonServiceConfig; }; }) @@ -329,8 +330,8 @@ in ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-dhcp4"; - KEA_LOCKFILE_DIR = "/run/kea-dhcp4"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -348,7 +349,6 @@ in "CAP_NET_BIND_SERVICE" "CAP_NET_RAW" ]; - RuntimeDirectory = "kea-dhcp4"; } // commonServiceConfig; }; }) @@ -377,8 +377,8 @@ in ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-dhcp6"; - KEA_LOCKFILE_DIR = "/run/kea-dhcp6"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -394,7 +394,6 @@ in CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; - RuntimeDirectory = "kea-dhcp6"; } // commonServiceConfig; }; }) @@ -423,8 +422,8 @@ in ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-dhcp-ddns"; - KEA_LOCKFILE_DIR = "/run/kea-dhcp-ddns"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -439,7 +438,6 @@ in CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; - RuntimeDirectory = "kea-dhcp-ddns"; } // commonServiceConfig; }; }) diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index f54ce59174387..39793922ab516 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -674,7 +674,11 @@ in (lport: "sshd -G -T -C lport=${toString lport} -f ${sshconf} > /dev/null") cfg.ports} ${concatMapStringsSep "\n" - (la: "sshd -G -T -C ${escapeShellArg "laddr=${la.addr},lport=${toString la.port}"} -f ${sshconf} > /dev/null") + (la: + concatMapStringsSep "\n" + (port: "sshd -G -T -C ${escapeShellArg "laddr=${la.addr},lport=${toString port}"} -f ${sshconf} > /dev/null") + (if la.port != null then [ la.port ] else cfg.ports) + ) cfg.listenAddresses} touch $out '') diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix index 3822df81063d9..1070e4e252967 100644 --- a/nixos/modules/services/networking/tailscale.nix +++ b/nixos/modules/services/networking/tailscale.nix @@ -100,8 +100,8 @@ in { }; systemd.services.tailscaled-autoconnect = mkIf (cfg.authKeyFile != null) { - after = ["tailscale.service"]; - wants = ["tailscale.service"]; + after = ["tailscaled.service"]; + wants = ["tailscaled.service"]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; diff --git a/nixos/modules/services/networking/yggdrasil.nix b/nixos/modules/services/networking/yggdrasil.nix index 514753687d699..9173e7eb3457b 100644 --- a/nixos/modules/services/networking/yggdrasil.nix +++ b/nixos/modules/services/networking/yggdrasil.nix @@ -137,16 +137,24 @@ in message = "networking.enableIPv6 must be true for yggdrasil to work"; }]; - system.activationScripts.yggdrasil = mkIf cfg.persistentKeys '' - if [ ! -e ${keysPath} ] - then - mkdir --mode=700 -p ${builtins.dirOf keysPath} - ${binYggdrasil} -genconf -json \ - | ${pkgs.jq}/bin/jq \ - 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ - > ${keysPath} - fi - ''; + # This needs to be a separate service. The yggdrasil service fails if + # this is put into its preStart. + systemd.services.yggdrasil-persistent-keys = lib.mkIf cfg.persistentKeys { + wantedBy = [ "multi-user.target" ]; + before = [ "yggdrasil.service" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = '' + if [ ! -e ${keysPath} ] + then + mkdir --mode=700 -p ${builtins.dirOf keysPath} + ${binYggdrasil} -genconf -json \ + | ${pkgs.jq}/bin/jq \ + 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ + > ${keysPath} + fi + ''; + }; systemd.services.yggdrasil = { description = "Yggdrasil Network Service"; diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix index 3a2744303474f..1f044384a5b83 100644 --- a/nixos/modules/services/printing/cupsd.nix +++ b/nixos/modules/services/printing/cupsd.nix @@ -4,9 +4,10 @@ with lib; let - inherit (pkgs) cups cups-pk-helper cups-filters xdg-utils; + inherit (pkgs) cups-pk-helper cups-filters xdg-utils; cfg = config.services.printing; + cups = cfg.package; avahiEnabled = config.services.avahi.enable; polkitEnabled = config.security.polkit.enable; @@ -140,6 +141,8 @@ in ''; }; + package = lib.mkPackageOption pkgs "cups" {}; + stateless = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/services/security/bitwarden-directory-connector-cli.nix b/nixos/modules/services/security/bitwarden-directory-connector-cli.nix new file mode 100644 index 0000000000000..18c02e22fd7e6 --- /dev/null +++ b/nixos/modules/services/security/bitwarden-directory-connector-cli.nix @@ -0,0 +1,323 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.services.bitwarden-directory-connector-cli; +in { + options.services.bitwarden-directory-connector-cli = { + enable = mkEnableOption "Bitwarden Directory Connector"; + + package = mkPackageOption pkgs "bitwarden-directory-connector-cli" {}; + + domain = mkOption { + type = types.str; + description = lib.mdDoc "The domain the Bitwarden/Vaultwarden is accessible on."; + example = "https://vaultwarden.example.com"; + }; + + user = mkOption { + type = types.str; + description = lib.mdDoc "User to run the program."; + default = "bwdc"; + }; + + interval = mkOption { + type = types.str; + default = "*:0,15,30,45"; + description = lib.mdDoc "The interval when to run the connector. This uses systemd's OnCalendar syntax."; + }; + + ldap = mkOption { + description = lib.mdDoc '' + Options to configure the LDAP connection. + If you used the desktop application to test the configuration you can find the settings by searching for `ldap` in `~/.config/Bitwarden\ Directory\ Connector/data.json`. + ''; + default = {}; + type = types.submodule ({ + config, + options, + ... + }: { + freeformType = types.attrsOf (pkgs.formats.json {}).type; + + config.finalJSON = builtins.toJSON (removeAttrs config (filter (x: x == "finalJSON" || ! options.${x}.isDefined or false) (attrNames options))); + + options = { + finalJSON = mkOption { + type = (pkgs.formats.json {}).type; + internal = true; + readOnly = true; + visible = false; + }; + + ssl = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to use TLS."; + }; + startTls = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to use STARTTLS."; + }; + + hostname = mkOption { + type = types.str; + description = lib.mdDoc "The host the LDAP is accessible on."; + example = "ldap.example.com"; + }; + + port = mkOption { + type = types.port; + default = 389; + description = lib.mdDoc "Port LDAP is accessible on."; + }; + + ad = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether the LDAP Server is an Active Directory."; + }; + + pagedSearch = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether the LDAP server paginates search results."; + }; + + rootPath = mkOption { + type = types.str; + description = lib.mdDoc "Root path for LDAP."; + example = "dc=example,dc=com"; + }; + + username = mkOption { + type = types.str; + description = lib.mdDoc "The user to authenticate as."; + example = "cn=admin,dc=example,dc=com"; + }; + }; + }); + }; + + sync = mkOption { + description = lib.mdDoc '' + Options to configure what gets synced. + If you used the desktop application to test the configuration you can find the settings by searching for `sync` in `~/.config/Bitwarden\ Directory\ Connector/data.json`. + ''; + default = {}; + type = types.submodule ({ + config, + options, + ... + }: { + freeformType = types.attrsOf (pkgs.formats.json {}).type; + + config.finalJSON = builtins.toJSON (removeAttrs config (filter (x: x == "finalJSON" || ! options.${x}.isDefined or false) (attrNames options))); + + options = { + finalJSON = mkOption { + type = (pkgs.formats.json {}).type; + internal = true; + readOnly = true; + visible = false; + }; + + removeDisabled = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc "Remove users from bitwarden groups if no longer in the ldap group."; + }; + + overwriteExisting = mkOption { + type = types.bool; + default = false; + description = + lib.mdDoc "Remove and re-add users/groups, See https://bitwarden.com/help/user-group-filters/#overwriting-syncs for more details."; + }; + + largeImport = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Enable if you are syncing more than 2000 users/groups."; + }; + + memberAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute that lists members in a LDAP group."; + example = "uniqueMember"; + }; + + creationDateAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute that lists a user's creation date."; + example = "whenCreated"; + }; + + useEmailPrefixSuffix = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "If a user has no email address, combine a username prefix with a suffix value to form an email."; + }; + emailPrefixAttribute = mkOption { + type = types.str; + description = lib.mdDoc "The attribute that contains the users username."; + example = "accountName"; + }; + emailSuffix = mkOption { + type = types.str; + description = lib.mdDoc "Suffix for the email, normally @example.com."; + example = "@example.com"; + }; + + users = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Sync users."; + }; + userPath = mkOption { + type = types.str; + description = lib.mdDoc "User directory, relative to root."; + default = "ou=users"; + }; + userObjectClass = mkOption { + type = types.str; + description = lib.mdDoc "Class that users must have."; + default = "inetOrgPerson"; + }; + userEmailAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute for a users email."; + default = "mail"; + }; + userFilter = mkOption { + type = types.str; + description = lib.mdDoc "LDAP filter for users."; + example = "(memberOf=cn=sales,ou=groups,dc=example,dc=com)"; + default = ""; + }; + + groups = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to sync ldap groups into BitWarden."; + }; + groupPath = mkOption { + type = types.str; + description = lib.mdDoc "Group directory, relative to root."; + default = "ou=groups"; + }; + groupObjectClass = mkOption { + type = types.str; + description = lib.mdDoc "A class that groups will have."; + default = "groupOfNames"; + }; + groupNameAttribute = mkOption { + type = types.str; + description = lib.mdDoc "Attribute for a name of group."; + default = "cn"; + }; + groupFilter = mkOption { + type = types.str; + description = lib.mdDoc "LDAP filter for groups."; + example = "(cn=sales)"; + default = ""; + }; + }; + }); + }; + + secrets = { + ldap = mkOption { + type = types.str; + description = "Path to file that contains LDAP password for user in {option}`ldap.username"; + }; + + bitwarden = { + client_path_id = mkOption { + type = types.str; + description = "Path to file that contains Client ID."; + }; + client_path_secret = mkOption { + type = types.str; + description = "Path to file that contains Client Secret."; + }; + }; + }; + }; + + config = mkIf cfg.enable { + users.groups."${cfg.user}" = {}; + users.users."${cfg.user}" = { + isSystemUser = true; + group = cfg.user; + }; + + systemd = { + timers.bitwarden-directory-connector-cli = { + description = "Sync timer for Bitwarden Directory Connector"; + wantedBy = ["timers.target"]; + after = ["network-online.target"]; + timerConfig = { + OnCalendar = cfg.interval; + Unit = "bitwarden-directory-connector-cli.service"; + Persistent = true; + }; + }; + + services.bitwarden-directory-connector-cli = { + description = "Main process for Bitwarden Directory Connector"; + path = [pkgs.jq]; + + environment = { + BITWARDENCLI_CONNECTOR_APPDATA_DIR = "/tmp"; + BITWARDENCLI_CONNECTOR_PLAINTEXT_SECRETS = "true"; + }; + + serviceConfig = { + Type = "oneshot"; + User = "${cfg.user}"; + PrivateTmp = true; + preStart = '' + set -eo pipefail + + # create the config file + ${lib.getExe cfg.package} data-file + touch /tmp/data.json.tmp + chmod 600 /tmp/data.json{,.tmp} + + ${lib.getExe cfg.package} config server ${cfg.domain} + + # now login to set credentials + export BW_CLIENTID="$(< ${escapeShellArg cfg.secrets.bitwarden.client_path_id})" + export BW_CLIENTSECRET="$(< ${escapeShellArg cfg.secrets.bitwarden.client_path_secret})" + ${lib.getExe cfg.package} login + + jq '.authenticatedAccounts[0] as $account + | .[$account].directoryConfigurations.ldap |= $ldap_data + | .[$account].directorySettings.organizationId |= $orgID + | .[$account].directorySettings.sync |= $sync_data' \ + --argjson ldap_data ${escapeShellArg cfg.ldap.finalJSON} \ + --arg orgID "''${BW_CLIENTID//organization.}" \ + --argjson sync_data ${escapeShellArg cfg.sync.finalJSON} \ + /tmp/data.json \ + > /tmp/data.json.tmp + + mv -f /tmp/data.json.tmp /tmp/data.json + + # final config + ${lib.getExe cfg.package} config directory 0 + ${lib.getExe cfg.package} config ldap.password --secretfile ${cfg.secrets.ldap} + ''; + + ExecStart = "${lib.getExe cfg.package} sync"; + }; + }; + }; + }; + + meta.maintainers = with maintainers; [Silver-Golden]; +} diff --git a/nixos/modules/services/security/munge.nix b/nixos/modules/services/security/munge.nix index 4d6fe33f697b8..9d306c205f946 100644 --- a/nixos/modules/services/security/munge.nix +++ b/nixos/modules/services/security/munge.nix @@ -45,19 +45,25 @@ in systemd.services.munged = { wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + wants = [ + "network-online.target" + "time-sync.target" + ]; + after = [ + "network-online.target" + "time-sync.target" + ]; path = [ pkgs.munge pkgs.coreutils ]; serviceConfig = { ExecStartPre = "+${pkgs.coreutils}/bin/chmod 0400 ${cfg.password}"; - ExecStart = "${pkgs.munge}/bin/munged --syslog --key-file ${cfg.password}"; - PIDFile = "/run/munge/munged.pid"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStart = "${pkgs.munge}/bin/munged --foreground --key-file ${cfg.password}"; User = "munge"; Group = "munge"; StateDirectory = "munge"; StateDirectoryMode = "0711"; + Restart = "on-failure"; RuntimeDirectory = "munge"; }; diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix index 4ff941251c99b..dea20dec1ab47 100644 --- a/nixos/modules/services/security/tor.nix +++ b/nixos/modules/services/security/tor.nix @@ -854,7 +854,7 @@ in BridgeRelay = true; ExtORPort.port = mkDefault "auto"; ServerTransportPlugin.transports = mkDefault ["obfs4"]; - ServerTransportPlugin.exec = mkDefault "${pkgs.obfs4}/bin/obfs4proxy managed"; + ServerTransportPlugin.exec = mkDefault "${lib.getExe pkgs.obfs4} managed"; } // optionalAttrs (cfg.relay.role == "private-bridge") { ExtraInfoStatistics = false; PublishServerDescriptor = false; diff --git a/nixos/modules/services/security/vaultwarden/backup.sh b/nixos/modules/services/security/vaultwarden/backup.sh index 2a3de0ab1deeb..7668da5bc88f3 100644 --- a/nixos/modules/services/security/vaultwarden/backup.sh +++ b/nixos/modules/services/security/vaultwarden/backup.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # Based on: https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault -if ! mkdir -p "$BACKUP_FOLDER"; then - echo "Could not create backup folder '$BACKUP_FOLDER'" >&2 +if [ ! -d "$BACKUP_FOLDER" ]; then + echo "Backup folder '$BACKUP_FOLDER' does not exist" >&2 exit 1 fi diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix index 14bbfa95a9ca2..470db735bf649 100644 --- a/nixos/modules/services/security/vaultwarden/default.nix +++ b/nixos/modules/services/security/vaultwarden/default.nix @@ -55,6 +55,7 @@ in { description = lib.mdDoc '' The directory under which vaultwarden will backup its persistent data. ''; + example = "/var/backup/vaultwarden"; }; config = mkOption { @@ -230,6 +231,13 @@ in { }; wantedBy = [ "multi-user.target" ]; }; + + systemd.tmpfiles.settings = mkIf (cfg.backupDir != null) { + "10-vaultwarden".${cfg.backupDir}.d = { + inherit user group; + mode = "0770"; + }; + }; }; # uses attributes of the linked package diff --git a/nixos/modules/services/system/cachix-watch-store.nix b/nixos/modules/services/system/cachix-watch-store.nix index 992a59cbc075b..8aa5f0358fa97 100644 --- a/nixos/modules/services/system/cachix-watch-store.nix +++ b/nixos/modules/services/system/cachix-watch-store.nix @@ -23,6 +23,14 @@ in ''; }; + signingKeyFile = mkOption { + type = types.nullOr types.path; + description = lib.mdDoc '' + Optional file containing a self-managed signing key to sign uploaded store paths. + ''; + default = null; + }; + compressionLevel = mkOption { type = types.nullOr types.int; description = lib.mdDoc "The compression level for ZSTD compression (between 0 and 16)"; @@ -69,7 +77,8 @@ in DynamicUser = true; LoadCredential = [ "cachix-token:${toString cfg.cachixTokenFile}" - ]; + ] + ++ lib.optional (cfg.signingKeyFile != null) "signing-key:${toString cfg.signingKeyFile}"; }; script = let @@ -80,6 +89,7 @@ in in '' export CACHIX_AUTH_TOKEN="$(<"$CREDENTIALS_DIRECTORY/cachix-token")" + ${lib.optionalString (cfg.signingKeyFile != null) ''export CACHIX_SIGNING_KEY="$(<"$CREDENTIALS_DIRECTORY/signing-key")"''} ${lib.escapeShellArgs command} ''; }; diff --git a/nixos/modules/services/system/zram-generator.nix b/nixos/modules/services/system/zram-generator.nix index 10b9992375cc1..429531e5743d8 100644 --- a/nixos/modules/services/system/zram-generator.nix +++ b/nixos/modules/services/system/zram-generator.nix @@ -27,7 +27,7 @@ in config = lib.mkIf cfg.enable { system.requiredKernelConfig = with config.lib.kernelConfig; [ - (isModule "ZRAM") + (isEnabled "ZRAM") ]; systemd.packages = [ cfg.package ]; diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix index 7fb7847ce9358..5dd02eb331633 100644 --- a/nixos/modules/services/torrent/transmission.nix +++ b/nixos/modules/services/torrent/transmission.nix @@ -251,6 +251,20 @@ in For instance, SSH sessions may time out more easily. ''; }; + + webHome = mkOption { + type = types.nullOr types.path; + default = null; + example = "pkgs.flood-for-transmission"; + description = lib.mdDoc '' + If not `null`, sets the value of the `TRANSMISSION_WEB_HOME` + environment variable used by the service. Useful for overriding + the web interface files, without overriding the transmission + package and thus requiring rebuilding it locally. Use this if + you want to use an alternative web interface, such as + `pkgs.flood-for-transmission`. + ''; + }; }; }; @@ -280,6 +294,7 @@ in requires = optional apparmor.enable "apparmor.service"; wantedBy = [ "multi-user.target" ]; environment.CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source; + environment.TRANSMISSION_WEB_HOME = lib.mkIf (cfg.webHome != null) cfg.webHome; serviceConfig = { # Use "+" because credentialsFile may not be accessible to User= or Group=. @@ -314,6 +329,9 @@ in BindPaths = [ "${cfg.home}/${settingsDir}" cfg.settings.download-dir + # Transmission may need to read in the host's /run (eg. /run/systemd/resolve) + # or write in its private /run (eg. /run/host). + "/run" ] ++ optional cfg.settings.incomplete-dir-enabled cfg.settings.incomplete-dir ++ @@ -324,7 +342,6 @@ in # an AppArmor profile is provided to get a confinement based upon paths and rights. builtins.storeDir "/etc" - "/run" ] ++ optional (cfg.settings.script-torrent-done-enabled && cfg.settings.script-torrent-done-filename != null) @@ -349,10 +366,10 @@ in MemoryDenyWriteExecute = true; NoNewPrivileges = true; PrivateDevices = true; - PrivateMounts = true; + PrivateMounts = mkDefault true; PrivateNetwork = mkDefault false; PrivateTmp = true; - PrivateUsers = true; + PrivateUsers = mkDefault true; ProtectClock = true; ProtectControlGroups = true; # ProtectHome=true would not allow BindPaths= to work across /home, @@ -490,6 +507,10 @@ in # https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorStacking#seccomp-and-no_new_privs px ${cfg.settings.script-torrent-done-filename} -> &@{dirs}, ''} + + ${optionalString (cfg.webHome != null) '' + r ${cfg.webHome}/**, + ''} ''; }; diff --git a/nixos/modules/services/video/frigate.nix b/nixos/modules/services/video/frigate.nix index 146e968780c38..b7945282ba09b 100644 --- a/nixos/modules/services/video/frigate.nix +++ b/nixos/modules/services/video/frigate.nix @@ -353,6 +353,7 @@ in ]; serviceConfig = { ExecStart = "${cfg.package.python.interpreter} -m frigate"; + Restart = "on-failure"; User = "frigate"; Group = "frigate"; diff --git a/nixos/modules/services/web-apps/code-server.nix b/nixos/modules/services/web-apps/code-server.nix new file mode 100644 index 0000000000000..11601f6c30449 --- /dev/null +++ b/nixos/modules/services/web-apps/code-server.nix @@ -0,0 +1,259 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.code-server; + defaultUser = "code-server"; + defaultGroup = defaultUser; +in { + options = { + services.code-server = { + enable = lib.mkEnableOption (lib.mdDoc "code-server"); + + package = lib.mkPackageOptionMD pkgs "code-server" { + example = '' + pkgs.vscode-with-extensions.override { + vscode = pkgs.code-server; + vscodeExtensions = with pkgs.vscode-extensions; [ + bbenoist.nix + dracula-theme.theme-dracula + ]; + } + ''; + }; + + extraPackages = lib.mkOption { + default = [ ]; + description = lib.mdDoc '' + Additional packages to add to the code-server {env}`PATH`. + ''; + example = lib.literalExpression "[ pkgs.go ]"; + type = lib.types.listOf lib.types.package; + }; + + extraEnvironment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + description = lib.mdDoc '' + Additional environment variables to pass to code-server. + ''; + default = { }; + example = { PKG_CONFIG_PATH = "/run/current-system/sw/lib/pkgconfig"; }; + }; + + extraArguments = lib.mkOption { + default = [ ]; + description = lib.mdDoc '' + Additional arguments to pass to code-server. + ''; + example = lib.literalExpression ''[ "--log=info" ]''; + type = lib.types.listOf lib.types.str; + }; + + host = lib.mkOption { + default = "localhost"; + description = lib.mdDoc '' + The host name or IP address the server should listen to. + ''; + type = lib.types.str; + }; + + port = lib.mkOption { + default = 4444; + description = lib.mdDoc '' + The port the server should listen to. + ''; + type = lib.types.port; + }; + + auth = lib.mkOption { + default = "password"; + description = lib.mdDoc '' + The type of authentication to use. + ''; + type = lib.types.enum [ "none" "password" ]; + }; + + hashedPassword = lib.mkOption { + default = ""; + description = lib.mdDoc '' + Create the password with: `echo -n 'thisismypassword' | npx argon2-cli -e`. + ''; + type = lib.types.str; + }; + + user = lib.mkOption { + default = defaultUser; + example = "yourUser"; + description = lib.mdDoc '' + The user to run code-server as. + By default, a user named `${defaultUser}` will be created. + ''; + type = lib.types.str; + }; + + group = lib.mkOption { + default = defaultGroup; + example = "yourGroup"; + description = lib.mdDoc '' + The group to run code-server under. + By default, a group named `${defaultGroup}` will be created. + ''; + type = lib.types.str; + }; + + extraGroups = lib.mkOption { + default = [ ]; + description = lib.mdDoc '' + An array of additional groups for the `${defaultUser}` user. + ''; + example = [ "docker" ]; + type = lib.types.listOf lib.types.str; + }; + + socket = lib.mkOption { + default = null; + example = "/run/code-server/socket"; + description = lib.mdDoc '' + Path to a socket (bind-addr will be ignored). + ''; + type = lib.types.nullOr lib.types.str; + }; + + socketMode = lib.mkOption { + default = null; + description = lib.mdDoc '' + File mode of the socket. + ''; + type = lib.types.nullOr lib.types.str; + }; + + userDataDir = lib.mkOption { + default = null; + description = lib.mdDoc '' + Path to the user data directory. + ''; + type = lib.types.nullOr lib.types.str; + }; + + extensionsDir = lib.mkOption { + default = null; + description = lib.mdDoc '' + Path to the extensions directory. + ''; + type = lib.types.nullOr lib.types.str; + }; + + proxyDomain = lib.mkOption { + default = null; + example = "code-server.lan"; + description = lib.mdDoc '' + Domain used for proxying ports. + ''; + type = lib.types.nullOr lib.types.str; + }; + + disableTelemetry = lib.mkOption { + default = false; + example = true; + description = lib.mdDoc '' + Disable telemetry. + ''; + type = lib.types.bool; + }; + + disableUpdateCheck = lib.mkOption { + default = false; + example = true; + description = lib.mdDoc '' + Disable update check. + Without this flag, code-server checks every 6 hours against the latest github release and + then notifies you once every week that a new release is available. + ''; + type = lib.types.bool; + }; + + disableFileDownloads = lib.mkOption { + default = false; + example = true; + description = lib.mdDoc '' + Disable file downloads from Code. + ''; + type = lib.types.bool; + }; + + disableWorkspaceTrust = lib.mkOption { + default = false; + example = true; + description = lib.mdDoc '' + Disable Workspace Trust feature. + ''; + type = lib.types.bool; + }; + + disableGettingStartedOverride = lib.mkOption { + default = false; + example = true; + description = lib.mdDoc '' + Disable the coder/coder override in the Help: Getting Started page. + ''; + type = lib.types.bool; + }; + + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.code-server = { + description = "Code server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + path = cfg.extraPackages; + environment = { + HASHED_PASSWORD = cfg.hashedPassword; + } // cfg.extraEnvironment; + serviceConfig = { + ExecStart = '' + ${lib.getExe cfg.package} \ + --auth=${cfg.auth} \ + --bind-addr=${cfg.host}:${toString cfg.port} \ + '' + lib.optionalString (cfg.socket != null) '' + --socket=${cfg.socket} \ + '' + lib.optionalString (cfg.userDataDir != null) '' + --user-data-dir=${cfg.userDataDir} \ + '' + lib.optionalString (cfg.extensionsDir != null) '' + --extensions-dir=${cfg.extensionsDir} \ + '' + lib.optionalString (cfg.disableTelemetry == true) '' + --disable-telemetry \ + '' + lib.optionalString (cfg.disableUpdateCheck == true) '' + --disable-update-check \ + '' + lib.optionalString (cfg.disableFileDownloads == true) '' + --disable-file-downloads \ + '' + lib.optionalString (cfg.disableWorkspaceTrust == true) '' + --disable-workspace-trust \ + '' + lib.optionalString (cfg.disableGettingStartedOverride == true) '' + --disable-getting-started-override \ + '' + lib.escapeShellArgs cfg.extraArguments; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + RuntimeDirectory = cfg.user; + User = cfg.user; + Group = cfg.group; + Restart = "on-failure"; + }; + }; + + users.users."${cfg.user}" = lib.mkMerge [ + (lib.mkIf (cfg.user == defaultUser) { + isNormalUser = true; + description = "code-server user"; + inherit (cfg) group; + }) + { + packages = cfg.extraPackages; + inherit (cfg) extraGroups; + } + ]; + + users.groups."${defaultGroup}" = lib.mkIf (cfg.group == defaultGroup) { }; + }; + + meta.maintainers = [ lib.maintainers.stackshadow ]; +} diff --git a/nixos/modules/services/web-apps/dokuwiki.nix b/nixos/modules/services/web-apps/dokuwiki.nix index 1df1cbf9f0e18..256ab3229ea6c 100644 --- a/nixos/modules/services/web-apps/dokuwiki.nix +++ b/nixos/modules/services/web-apps/dokuwiki.nix @@ -122,62 +122,8 @@ let }; }; - # The current implementations of `doRename`, `mkRenamedOptionModule` do not provide the full options path when used with submodules. - # They would only show `settings.useacl' instead of `services.dokuwiki.sites."site1.local".settings.useacl' - # The partial re-implementation of these functions is done to help users in debugging by showing the full path. - mkRenamed = from: to: { config, options, name, ... }: let - pathPrefix = [ "services" "dokuwiki" "sites" name ]; - fromPath = pathPrefix ++ from; - fromOpt = getAttrFromPath from options; - toOp = getAttrsFromPath to config; - toPath = pathPrefix ++ to; - in { - options = setAttrByPath from (mkOption { - visible = false; - description = lib.mdDoc "Alias of {option}${showOption toPath}"; - apply = x: builtins.trace "Obsolete option `${showOption fromPath}' is used. It was renamed to ${showOption toPath}" toOp; - }); - config = mkMerge [ - { - warnings = optional fromOpt.isDefined - "The option `${showOption fromPath}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption toPath}'."; - } - (lib.modules.mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt) - ]; - }; - siteOpts = { options, config, lib, name, ... }: { - imports = [ - (mkRenamed [ "aclUse" ] [ "settings" "useacl" ]) - (mkRenamed [ "superUser" ] [ "settings" "superuser" ]) - (mkRenamed [ "disableActions" ] [ "settings" "disableactions" ]) - ({ config, options, ... }: let - showPath = suffix: lib.options.showOption ([ "services" "dokuwiki" "sites" name ] ++ suffix); - replaceExtraConfig = "Please use `${showPath ["settings"]}' to pass structured settings instead."; - ecOpt = options.extraConfig; - ecPath = showPath [ "extraConfig" ]; - in { - options.extraConfig = mkOption { - visible = false; - apply = x: throw "The option ${ecPath} can no longer be used since it's been removed.\n${replaceExtraConfig}"; - }; - config.assertions = [ - { - assertion = !ecOpt.isDefined; - message = "The option definition `${ecPath}' in ${showFiles ecOpt.files} no longer has any effect; please remove it.\n${replaceExtraConfig}"; - } - { - assertion = config.mergedConfig.useacl -> (config.acl != null || config.aclFile != null); - message = "Either ${showPath [ "acl" ]} or ${showPath [ "aclFile" ]} is mandatory if ${showPath [ "settings" "useacl" ]} is true"; - } - { - assertion = config.usersFile != null -> config.mergedConfig.useacl != false; - message = "${showPath [ "settings" "useacl" ]} is required when ${showPath [ "usersFile" ]} is set (Currently defined as `${config.usersFile}' in ${showFiles options.usersFile.files})."; - } - ]; - }) - ]; options = { enable = mkEnableOption (lib.mdDoc "DokuWiki web application"); @@ -392,21 +338,6 @@ let ''; }; - # Required for the mkRenamedOptionModule - # TODO: Remove me once https://github.com/NixOS/nixpkgs/issues/96006 is fixed - # or we don't have any more notes about the removal of extraConfig, ... - warnings = mkOption { - type = types.listOf types.unspecified; - default = [ ]; - visible = false; - internal = true; - }; - assertions = mkOption { - type = types.listOf types.unspecified; - default = [ ]; - visible = false; - internal = true; - }; }; }; in @@ -440,10 +371,6 @@ in # implementation config = mkIf (eachSite != {}) (mkMerge [{ - warnings = flatten (mapAttrsToList (_: cfg: cfg.warnings) eachSite); - - assertions = flatten (mapAttrsToList (_: cfg: cfg.assertions) eachSite); - services.phpfpm.pools = mapAttrs' (hostName: cfg: ( nameValuePair "dokuwiki-${hostName}" { inherit user; diff --git a/nixos/modules/services/web-apps/freshrss.nix b/nixos/modules/services/web-apps/freshrss.nix index 9683730bbe1f8..c8399143c37ba 100644 --- a/nixos/modules/services/web-apps/freshrss.nix +++ b/nixos/modules/services/web-apps/freshrss.nix @@ -294,7 +294,6 @@ in systemd.services.freshrss-updater = { description = "FreshRSS feed updater"; after = [ "freshrss-config.service" ]; - wantedBy = [ "multi-user.target" ]; startAt = "*:0/5"; environment = { DATA_PATH = cfg.dataDir; diff --git a/nixos/modules/services/web-apps/invidious.nix b/nixos/modules/services/web-apps/invidious.nix index 471027c357056..359aaabfe673a 100644 --- a/nixos/modules/services/web-apps/invidious.nix +++ b/nixos/modules/services/web-apps/invidious.nix @@ -153,10 +153,11 @@ let message = '' For local automatic database provisioning (services.invidious.database.createLocally == true) to work, the username used to connect to PostgreSQL must match the database name, that is - services.invidious.database.user must match services.invidious.database.dbName. + services.invidious.settings.db.user must match services.invidious.settings.db.dbname. This is the default since NixOS 24.05. For older systems, it is normally safe to manually set - services.invidious.database.user to "invidious" as the new user will be created with permissions - for the existing database. + the user to "invidious" as the new user will be created with permissions + for the existing database. `REASSIGN OWNED BY kemal TO invidious;` may also be needed, it can be + run as `sudo -u postgres env psql --user=postgres --dbname=invidious -c 'reassign OWNED BY kemal to invidious;'`. ''; } ]; diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix index 40219edcd4470..538e728fcc72f 100644 --- a/nixos/modules/services/web-apps/mastodon.nix +++ b/nixos/modules/services/web-apps/mastodon.nix @@ -136,7 +136,7 @@ let # System Call Filtering SystemCallFilter = [ ("~" + lib.concatStringsSep " " systemCallsList) "@chown" "pipe" "pipe2" ]; } // cfgService; - path = with pkgs; [ file imagemagick ffmpeg ]; + path = with pkgs; [ ffmpeg-headless file imagemagick ]; }) ) cfg.sidekiqProcesses; @@ -773,7 +773,7 @@ in { # System Call Filtering SystemCallFilter = [ ("~" + lib.concatStringsSep " " systemCallsList) "@chown" "pipe" "pipe2" ]; } // cfgService; - path = with pkgs; [ file imagemagick ffmpeg ]; + path = with pkgs; [ ffmpeg-headless file imagemagick ]; }; systemd.services.mastodon-media-auto-remove = lib.mkIf cfg.mediaAutoRemove.enable { diff --git a/nixos/modules/services/web-apps/nextcloud.md b/nixos/modules/services/web-apps/nextcloud.md index b10fd566abb31..ce8f96a6a3896 100644 --- a/nixos/modules/services/web-apps/nextcloud.md +++ b/nixos/modules/services/web-apps/nextcloud.md @@ -51,7 +51,7 @@ to ensure that changes can be applied by changing the module's options. In case the application serves multiple domains (those are checked with [`$_SERVER['HTTP_HOST']`](https://www.php.net/manual/en/reserved.variables.server.php)) it's needed to add them to -[`services.nextcloud.config.extraTrustedDomains`](#opt-services.nextcloud.config.extraTrustedDomains). +[`services.nextcloud.extraOptions.trusted_domains`](#opt-services.nextcloud.extraOptions.trusted_domains). Auto updates for Nextcloud apps can be enabled using [`services.nextcloud.autoUpdateApps`](#opt-services.nextcloud.autoUpdateApps.enable). diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 501df47942e56..39f4e8f11620c 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -9,6 +9,7 @@ let jsonFormat = pkgs.formats.json {}; defaultPHPSettings = { + output_buffering = "0"; short_open_tag = "Off"; expose_php = "Off"; error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT"; @@ -23,6 +24,43 @@ let catch_workers_output = "yes"; }; + appStores = { + # default apps bundled with pkgs.nextcloudXX, e.g. files, contacts + apps = { + enabled = true; + writable = false; + }; + # apps installed via cfg.extraApps + nix-apps = { + enabled = cfg.extraApps != { }; + linkTarget = pkgs.linkFarm "nix-apps" + (mapAttrsToList (name: path: { inherit name path; }) cfg.extraApps); + writable = false; + }; + # apps installed via the app store. + store-apps = { + enabled = cfg.appstoreEnable == null || cfg.appstoreEnable; + linkTarget = "${cfg.home}/store-apps"; + writable = true; + }; + }; + + webroot = pkgs.runCommand + "${cfg.package.name or "nextcloud"}-with-apps" + { } + '' + mkdir $out + ln -sfv "${cfg.package}"/* "$out" + ${concatStrings + (mapAttrsToList (name: store: optionalString (store.enabled && store?linkTarget) '' + if [ -e "$out"/${name} ]; then + echo "Didn't expect ${name} already in $out!" + exit 1 + fi + ln -sfTv ${store.linkTarget} "$out"/${name} + '') appStores)} + ''; + inherit (cfg) datadir; phpPackage = cfg.phpPackage.buildEnv { @@ -45,7 +83,7 @@ let occ = pkgs.writeScriptBin "nextcloud-occ" '' #! ${pkgs.runtimeShell} - cd ${cfg.package} + cd ${webroot} sudo=exec if [[ "$USER" != nextcloud ]]; then sudo='exec /run/wrappers/bin/sudo -u nextcloud --preserve-env=NEXTCLOUD_CONFIG_DIR --preserve-env=OC_PASS' @@ -94,6 +132,25 @@ in { (mkRemovedOptionModule [ "services" "nextcloud" "disableImagemagick" ] '' Use services.nextcloud.enableImagemagick instead. '') + (mkRemovedOptionModule [ "services" "nextcloud" "config" "dbport" ] '' + Add port to services.nextcloud.config.dbhost instead. + '') + (mkRenamedOptionModule + [ "services" "nextcloud" "logLevel" ] [ "services" "nextcloud" "extraOptions" "loglevel" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "logType" ] [ "services" "nextcloud" "extraOptions" "log_type" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "defaultPhoneRegion" ] [ "services" "nextcloud" "extraOptions" "default_phone_region" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "overwriteProtocol" ] [ "services" "nextcloud" "extraOptions" "overwriteprotocol" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "skeletonDirectory" ] [ "services" "nextcloud" "extraOptions" "skeletondirectory" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "globalProfiles" ] [ "services" "nextcloud" "extraOptions" "profile.enabled" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "extraTrustedDomains" ] [ "services" "nextcloud" "extraOptions" "trusted_domains" ]) + (mkRenamedOptionModule + [ "services" "nextcloud" "config" "trustedProxies" ] [ "services" "nextcloud" "extraOptions" "trusted_proxies" ]) ]; options.services.nextcloud = { @@ -157,32 +214,6 @@ in { Set this to false to disable the installation of apps from the global appstore. App management is always enabled regardless of this setting. ''; }; - logLevel = mkOption { - type = types.ints.between 0 4; - default = 2; - description = lib.mdDoc '' - Log level value between 0 (DEBUG) and 4 (FATAL). - - - 0 (debug): Log all activity. - - - 1 (info): Log activity such as user logins and file activities, plus warnings, errors, and fatal errors. - - - 2 (warn): Log successful operations, as well as warnings of potential problems, errors and fatal errors. - - - 3 (error): Log failed operations and fatal errors. - - - 4 (fatal): Log only fatal errors that cause the server to stop. - ''; - }; - logType = mkOption { - type = types.enum [ "errorlog" "file" "syslog" "systemd" ]; - default = "syslog"; - description = lib.mdDoc '' - Logging backend to use. - systemd requires the php-systemd package to be added to services.nextcloud.phpExtraExtensions. - See the [nextcloud documentation](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/logging_configuration.html) for details. - ''; - }; https = mkOption { type = types.bool; default = false; @@ -206,16 +237,6 @@ in { ''; }; - skeletonDirectory = mkOption { - default = ""; - type = types.str; - description = lib.mdDoc '' - The directory where the skeleton files are located. These files will be - copied to the data directory of new users. Leave empty to not copy any - skeleton files. - ''; - }; - webfinger = mkOption { type = types.bool; default = false; @@ -315,7 +336,6 @@ in { }; - config = { dbtype = mkOption { type = types.enum [ "sqlite" "pgsql" "mysql" ]; @@ -346,18 +366,14 @@ in { else if mysqlLocal then "localhost:/run/mysqld/mysqld.sock" else "localhost"; defaultText = "localhost"; + example = "localhost:5000"; description = lib.mdDoc '' - Database host or socket path. + Database host (+port) or socket path. If [](#opt-services.nextcloud.database.createLocally) is true and [](#opt-services.nextcloud.config.dbtype) is either `pgsql` or `mysql`, defaults to the correct Unix socket instead. ''; }; - dbport = mkOption { - type = with types; nullOr (either int str); - default = null; - description = lib.mdDoc "Database port."; - }; dbtableprefix = mkOption { type = types.nullOr types.str; default = null; @@ -380,53 +396,6 @@ in { setup of Nextcloud by the systemd service `nextcloud-setup.service`. ''; }; - - extraTrustedDomains = mkOption { - type = types.listOf types.str; - default = []; - description = lib.mdDoc '' - Trusted domains from which the Nextcloud installation will be - accessible. You don't need to add - `services.nextcloud.hostname` here. - ''; - }; - - trustedProxies = mkOption { - type = types.listOf types.str; - default = []; - description = lib.mdDoc '' - Trusted proxies to provide if the Nextcloud installation is being - proxied to secure against, e.g. spoofing. - ''; - }; - - overwriteProtocol = mkOption { - type = types.nullOr (types.enum [ "http" "https" ]); - default = null; - example = "https"; - - description = lib.mdDoc '' - Force Nextcloud to always use HTTP or HTTPS i.e. for link generation. - Nextcloud uses the currently used protocol by default, but when - behind a reverse-proxy, it may use `http` for everything although - Nextcloud may be served via HTTPS. - ''; - }; - - defaultPhoneRegion = mkOption { - default = null; - type = types.nullOr types.str; - example = "DE"; - description = lib.mdDoc '' - An [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) - country code which replaces automatic phone-number detection - without a country code. - - As an example, with `DE` set as the default phone region, - the `+49` prefix can be omitted for phone numbers. - ''; - }; - objectstore = { s3 = { enable = mkEnableOption (lib.mdDoc '' @@ -609,30 +578,109 @@ in { The nextcloud-occ program preconfigured to target this Nextcloud instance. ''; }; - globalProfiles = mkEnableOption (lib.mdDoc "global profiles") // { - description = lib.mdDoc '' - Makes user-profiles globally available under `nextcloud.tld/u/user.name`. - Even though it's enabled by default in Nextcloud, it must be explicitly enabled - here because it has the side-effect that personal information is even accessible to - unauthenticated users by default. - - By default, the following properties are set to “Show to everyone” - if this flag is enabled: - - About - - Full name - - Headline - - Organisation - - Profile picture - - Role - - Twitter - - Website - - Only has an effect in Nextcloud 23 and later. - ''; - }; extraOptions = mkOption { - type = jsonFormat.type; + type = types.submodule { + freeformType = jsonFormat.type; + options = { + + loglevel = mkOption { + type = types.ints.between 0 4; + default = 2; + description = lib.mdDoc '' + Log level value between 0 (DEBUG) and 4 (FATAL). + + - 0 (debug): Log all activity. + + - 1 (info): Log activity such as user logins and file activities, plus warnings, errors, and fatal errors. + + - 2 (warn): Log successful operations, as well as warnings of potential problems, errors and fatal errors. + + - 3 (error): Log failed operations and fatal errors. + + - 4 (fatal): Log only fatal errors that cause the server to stop. + ''; + }; + log_type = mkOption { + type = types.enum [ "errorlog" "file" "syslog" "systemd" ]; + default = "syslog"; + description = lib.mdDoc '' + Logging backend to use. + systemd requires the php-systemd package to be added to services.nextcloud.phpExtraExtensions. + See the [nextcloud documentation](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/logging_configuration.html) for details. + ''; + }; + skeletondirectory = mkOption { + default = ""; + type = types.str; + description = lib.mdDoc '' + The directory where the skeleton files are located. These files will be + copied to the data directory of new users. Leave empty to not copy any + skeleton files. + ''; + }; + trusted_domains = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Trusted domains, from which the nextcloud installation will be + accessible. You don't need to add + `services.nextcloud.hostname` here. + ''; + }; + trusted_proxies = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Trusted proxies, to provide if the nextcloud installation is being + proxied to secure against e.g. spoofing. + ''; + }; + overwriteprotocol = mkOption { + type = types.enum [ "" "http" "https" ]; + default = ""; + example = "https"; + description = lib.mdDoc '' + Force Nextcloud to always use HTTP or HTTPS i.e. for link generation. + Nextcloud uses the currently used protocol by default, but when + behind a reverse-proxy, it may use `http` for everything although + Nextcloud may be served via HTTPS. + ''; + }; + default_phone_region = mkOption { + default = ""; + type = types.str; + example = "DE"; + description = lib.mdDoc '' + An [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) + country code which replaces automatic phone-number detection + without a country code. + + As an example, with `DE` set as the default phone region, + the `+49` prefix can be omitted for phone numbers. + ''; + }; + "profile.enabled" = mkEnableOption (lib.mdDoc "global profiles") // { + description = lib.mdDoc '' + Makes user-profiles globally available under `nextcloud.tld/u/user.name`. + Even though it's enabled by default in Nextcloud, it must be explicitly enabled + here because it has the side-effect that personal information is even accessible to + unauthenticated users by default. + By default, the following properties are set to “Show to everyone” + if this flag is enabled: + - About + - Full name + - Headline + - Organisation + - Profile picture + - Role + - Twitter + - Website + Only has an effect in Nextcloud 23 and later. + ''; + }; + }; + }; default = {}; description = lib.mdDoc '' Extra options which should be appended to Nextcloud's config.php file. @@ -766,11 +814,10 @@ in { # When upgrading the Nextcloud package, Nextcloud can report errors such as # "The files of the app [all apps in /var/lib/nextcloud/apps] were not replaced correctly" # Restarting phpfpm on Nextcloud package update fixes these issues (but this is a workaround). - phpfpm-nextcloud.restartTriggers = [ cfg.package ]; + phpfpm-nextcloud.restartTriggers = [ webroot ]; nextcloud-setup = let c = cfg.config; - writePhpArray = a: "[${concatMapStringsSep "," (val: ''"${toString val}"'') a}]"; requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable; objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable '' 'objectstore' => [ @@ -800,6 +847,10 @@ in { nextcloudGreaterOrEqualThan = req: versionAtLeast cfg.package.version req; + mkAppStoreConfig = name: { enabled, writable, ... }: optionalString enabled '' + [ 'path' => '${webroot}/${name}', 'url' => '/${name}', 'writable' => ${boolToString writable} ], + ''; + overrideConfig = pkgs.writeText "nextcloud-config.php" '' <?php ${optionalString requiresReadSecretFunction '' @@ -828,20 +879,12 @@ in { } $CONFIG = [ 'apps_paths' => [ - ${optionalString (cfg.extraApps != { }) "[ 'path' => '${cfg.home}/nix-apps', 'url' => '/nix-apps', 'writable' => false ],"} - [ 'path' => '${cfg.home}/apps', 'url' => '/apps', 'writable' => false ], - [ 'path' => '${cfg.home}/store-apps', 'url' => '/store-apps', 'writable' => true ], + ${concatStrings (mapAttrsToList mkAppStoreConfig appStores)} ], ${optionalString (showAppStoreSetting) "'appstoreenabled' => ${renderedAppStoreSetting},"} - 'datadirectory' => '${datadir}/data', - 'skeletondirectory' => '${cfg.skeletonDirectory}', ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} - 'log_type' => '${cfg.logType}', - 'loglevel' => '${builtins.toString cfg.logLevel}', - ${optionalString (c.overwriteProtocol != null) "'overwriteprotocol' => '${c.overwriteProtocol}',"} ${optionalString (c.dbname != null) "'dbname' => '${c.dbname}',"} ${optionalString (c.dbhost != null) "'dbhost' => '${c.dbhost}',"} - ${optionalString (c.dbport != null) "'dbport' => '${toString c.dbport}',"} ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"} ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"} ${optionalString (c.dbpassFile != null) '' @@ -851,10 +894,6 @@ in { '' } 'dbtype' => '${c.dbtype}', - 'trusted_domains' => ${writePhpArray ([ cfg.hostName ] ++ c.extraTrustedDomains)}, - 'trusted_proxies' => ${writePhpArray (c.trustedProxies)}, - ${optionalString (c.defaultPhoneRegion != null) "'default_phone_region' => '${c.defaultPhoneRegion}',"} - ${optionalString (nextcloudGreaterOrEqualThan "23") "'profile.enabled' => ${boolToString cfg.globalProfiles},"} ${objectstoreConfig} ]; @@ -890,7 +929,6 @@ in { # will be omitted. ${if c.dbname != null then "--database-name" else null} = ''"${c.dbname}"''; ${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"''; - ${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"''; ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; "--database-pass" = "\"\$${dbpass.arg}\""; "--admin-user" = ''"${c.adminuser}"''; @@ -907,7 +945,7 @@ in { (i: v: '' ${occ}/bin/nextcloud-occ config:system:set trusted_domains \ ${toString i} --value="${toString v}" - '') ([ cfg.hostName ] ++ cfg.config.extraTrustedDomains)); + '') ([ cfg.hostName ] ++ cfg.extraOptions.trusted_domains)); in { wantedBy = [ "multi-user.target" ]; @@ -935,17 +973,16 @@ in { exit 1 fi - ln -sf ${cfg.package}/apps ${cfg.home}/ - - # Install extra apps - ln -sfT \ - ${pkgs.linkFarm "nix-apps" - (mapAttrsToList (name: path: { inherit name path; }) cfg.extraApps)} \ - ${cfg.home}/nix-apps + ${concatMapStrings (name: '' + if [ -d "${cfg.home}"/${name} ]; then + echo "Cleaning up ${name}; these are now bundled in the webroot store-path!" + rm -r "${cfg.home}"/${name} + fi + '') [ "nix-apps" "apps" ]} # create nextcloud directories. # if the directories exist already with wrong permissions, we fix that - for dir in ${datadir}/config ${datadir}/data ${cfg.home}/store-apps ${cfg.home}/nix-apps; do + for dir in ${datadir}/config ${datadir}/data ${cfg.home}/store-apps; do if [ ! -e $dir ]; then install -o nextcloud -g nextcloud -d $dir elif [ $(stat -c "%G" $dir) != "nextcloud" ]; then @@ -982,7 +1019,7 @@ in { environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config"; serviceConfig.Type = "oneshot"; serviceConfig.User = "nextcloud"; - serviceConfig.ExecStart = "${phpPackage}/bin/php -f ${cfg.package}/cron.php"; + serviceConfig.ExecStart = "${phpPackage}/bin/php -f ${webroot}/cron.php"; }; nextcloud-update-plugins = mkIf cfg.autoUpdateApps.enable { after = [ "nextcloud-setup.service" ]; @@ -1043,22 +1080,25 @@ in { user = "nextcloud"; }; - services.nextcloud = lib.mkIf cfg.configureRedis { - caching.redis = true; - extraOptions = { + services.nextcloud = { + caching.redis = lib.mkIf cfg.configureRedis true; + extraOptions = mkMerge [({ + datadirectory = lib.mkDefault "${datadir}/data"; + trusted_domains = [ cfg.hostName ]; + }) (lib.mkIf cfg.configureRedis { "memcache.distributed" = ''\OC\Memcache\Redis''; "memcache.locking" = ''\OC\Memcache\Redis''; redis = { host = config.services.redis.servers.nextcloud.unixSocket; port = 0; }; - }; + })]; }; services.nginx.enable = mkDefault true; services.nginx.virtualHosts.${cfg.hostName} = { - root = cfg.package; + root = webroot; locations = { "= /robots.txt" = { priority = 100; @@ -1075,14 +1115,6 @@ in { } ''; }; - "~ ^/store-apps" = { - priority = 201; - extraConfig = "root ${cfg.home};"; - }; - "~ ^/nix-apps" = { - priority = 201; - extraConfig = "root ${cfg.home};"; - }; "^~ /.well-known" = { priority = 210; extraConfig = '' diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 6c08d0aee3d77..1285c2bbb916d 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -352,10 +352,11 @@ let # The acme-challenge location doesn't need to be added if we are not using any automated # certificate provisioning and can also be omitted when we use a certificate obtained via a DNS-01 challenge - acmeLocation = optionalString (vhost.enableACME || (vhost.useACMEHost != null && config.security.acme.certs.${vhost.useACMEHost}.dnsProvider == null)) '' + acmeLocation = optionalString (vhost.enableACME || (vhost.useACMEHost != null && config.security.acme.certs.${vhost.useACMEHost}.dnsProvider == null)) # Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx) # We use ^~ here, so that we don't check any regexes (which could # otherwise easily override this intended match accidentally). + '' location ^~ /.well-known/acme-challenge/ { ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"} ${optionalString (vhost.acmeRoot != null) "root ${vhost.acmeRoot};"} @@ -375,10 +376,11 @@ let ${concatMapStringsSep "\n" listenString redirectListen} server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases}; - ${acmeLocation} + location / { return ${toString vhost.redirectCode} https://$host$request_uri; } + ${acmeLocation} } ''} @@ -392,13 +394,6 @@ let http3 ${if vhost.http3 then "on" else "off"}; http3_hq ${if vhost.http3_hq then "on" else "off"}; ''} - ${acmeLocation} - ${optionalString (vhost.root != null) "root ${vhost.root};"} - ${optionalString (vhost.globalRedirect != null) '' - location / { - return ${toString vhost.redirectCode} http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri; - } - ''} ${optionalString hasSSL '' ssl_certificate ${vhost.sslCertificate}; ssl_certificate_key ${vhost.sslCertificateKey}; @@ -421,6 +416,14 @@ let ${mkBasicAuth vhostName vhost} + ${optionalString (vhost.root != null) "root ${vhost.root};"} + + ${optionalString (vhost.globalRedirect != null) '' + location / { + return ${toString vhost.redirectCode} http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri; + } + ''} + ${acmeLocation} ${mkLocations vhost.locations} ${vhost.extraConfig} @@ -1130,14 +1133,6 @@ in } { - assertion = any (host: host.kTLS) (attrValues virtualHosts) -> versionAtLeast cfg.package.version "1.21.4"; - message = '' - services.nginx.virtualHosts.<name>.kTLS requires nginx version - 1.21.4 or above; see the documentation for services.nginx.package. - ''; - } - - { assertion = all (host: !(host.enableACME && host.useACMEHost != null)) (attrValues virtualHosts); message = '' Options services.nginx.service.virtualHosts.<name>.enableACME and @@ -1345,6 +1340,8 @@ in nginx.gid = config.ids.gids.nginx; }; + boot.kernelModules = optional (versionAtLeast config.boot.kernelPackages.kernel.version "4.17") "tls"; + # do not delete the default temp directories created upon nginx startup systemd.tmpfiles.rules = [ "X /tmp/systemd-private-%b-nginx.service-*/tmp/nginx_*" diff --git a/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixos/modules/services/x11/desktop-managers/cinnamon.nix index e9cadf2194682..f5a6c05865c47 100644 --- a/nixos/modules/services/x11/desktop-managers/cinnamon.nix +++ b/nixos/modules/services/x11/desktop-managers/cinnamon.nix @@ -79,20 +79,19 @@ in package = mkDefault pkgs.cinnamon.mint-cursor-themes; }; }; - services.xserver.displayManager.sessionCommands = '' - if test "$XDG_CURRENT_DESKTOP" = "Cinnamon"; then - true - ${concatMapStrings (p: '' - if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then - export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name} - fi - - if [ -d "${p}/lib/girepository-1.0" ]; then - export GI_TYPELIB_PATH=$GI_TYPELIB_PATH''${GI_TYPELIB_PATH:+:}${p}/lib/girepository-1.0 - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib - fi - '') cfg.sessionPath} - fi + + # Have to take care of GDM + Cinnamon on Wayland users + environment.extraInit = '' + ${concatMapStrings (p: '' + if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then + export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name} + fi + + if [ -d "${p}/lib/girepository-1.0" ]; then + export GI_TYPELIB_PATH=$GI_TYPELIB_PATH''${GI_TYPELIB_PATH:+:}${p}/lib/girepository-1.0 + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib + fi + '') cfg.sessionPath} ''; # Default services diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 6ca7a4425f892..0576619cc8d28 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -7,7 +7,7 @@ let cfg = dmcfg.sddm; xEnv = config.systemd.services.display-manager.environment; - sddm = pkgs.libsForQt5.sddm; + sddm = cfg.package; iniFmt = pkgs.formats.ini { }; @@ -108,6 +108,8 @@ in ''; }; + package = mkPackageOption pkgs [ "plasma5Packages" "sddm" ] {}; + enableHidpi = mkOption { type = types.bool; default = true; diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix index d2a5b5895e0aa..0ea21eb1dce3a 100644 --- a/nixos/modules/services/x11/hardware/libinput.nix +++ b/nixos/modules/services/x11/hardware/libinput.nix @@ -130,9 +130,9 @@ let cfg = config.services.xserver.libinput; default = true; description = lib.mdDoc '' - Disables horizontal scrolling. When disabled, this driver will discard any horizontal scroll - events from libinput. Note that this does not disable horizontal scrolling, it merely - discards the horizontal axis from any scroll events. + Enables or disables horizontal scrolling. When disabled, this driver will discard any + horizontal scroll events from libinput. This does not disable horizontal scroll events + from libinput; it merely discards the horizontal axis from any scroll events. ''; }; diff --git a/nixos/modules/system/activation/bootspec.nix b/nixos/modules/system/activation/bootspec.nix index 98c234bc340d0..2ed6964b2a6a6 100644 --- a/nixos/modules/system/activation/bootspec.nix +++ b/nixos/modules/system/activation/bootspec.nix @@ -11,6 +11,7 @@ let cfg = config.boot.bootspec; children = lib.mapAttrs (childName: childConfig: childConfig.configuration.system.build.toplevel) config.specialisation; + hasAtLeastOneInitrdSecret = lib.length (lib.attrNames config.boot.initrd.secrets) > 0; schemas = { v1 = rec { filename = "boot.json"; @@ -27,6 +28,7 @@ let label = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"; } // lib.optionalAttrs config.boot.initrd.enable { initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; + } // lib.optionalAttrs hasAtLeastOneInitrdSecret { initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets"; }; })); diff --git a/nixos/modules/system/boot/binfmt.nix b/nixos/modules/system/boot/binfmt.nix index d16152ab9dec5..08e3dce708447 100644 --- a/nixos/modules/system/boot/binfmt.nix +++ b/nixos/modules/system/boot/binfmt.nix @@ -1,6 +1,6 @@ { config, lib, pkgs, ... }: let - inherit (lib) mkOption mkDefault types optionalString stringAfter; + inherit (lib) mkOption mkDefault types optionalString; cfg = config.boot.binfmt; diff --git a/nixos/modules/system/boot/grow-partition.nix b/nixos/modules/system/boot/grow-partition.nix index 897602f9826ab..8a0fc3a03dac4 100644 --- a/nixos/modules/system/boot/grow-partition.nix +++ b/nixos/modules/system/boot/grow-partition.nix @@ -25,7 +25,7 @@ with lib; systemd.services.growpart = { wantedBy = [ "-.mount" ]; after = [ "-.mount" ]; - before = [ "systemd-growfs-root.service" ]; + before = [ "systemd-growfs-root.service" "shutdown.target" ]; conflicts = [ "shutdown.target" ]; unitConfig.DefaultDependencies = false; serviceConfig = { diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix index a8cd2e8f05fcc..61e61f32bc5ee 100644 --- a/nixos/modules/system/boot/initrd-ssh.nix +++ b/nixos/modules/system/boot/initrd-ssh.nix @@ -243,8 +243,10 @@ in services.sshd = { description = "SSH Daemon"; - wantedBy = ["initrd.target"]; - after = ["network.target" "initrd-nixos-copy-secrets.service"]; + wantedBy = [ "initrd.target" ]; + after = [ "network.target" "initrd-nixos-copy-secrets.service" ]; + before = [ "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; # Keys from Nix store are world-readable, which sshd doesn't # like. If this were a real nix store and not the initrd, we diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix index 7097e1d83dca9..0556c875241a1 100644 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ b/nixos/modules/system/boot/loader/grub/grub.nix @@ -36,7 +36,7 @@ let # Package set of targeted architecture if cfg.forcei686 then pkgs.pkgsi686Linux else pkgs; - realGrub = if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; } + realGrub = if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; zfs = cfg.zfsPackage; } else grubPkgs.grub2; grub = @@ -614,6 +614,16 @@ in ''; }; + zfsPackage = mkOption { + type = types.package; + internal = true; + default = pkgs.zfs; + defaultText = literalExpression "pkgs.zfs"; + description = lib.mdDoc '' + Which ZFS package to use if `config.boot.loader.grub.zfsSupport` is true. + ''; + }; + efiSupport = mkOption { default = false; type = types.bool; 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 e2e7ffe59dcd2..6cd46f30373b5 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 @@ -20,13 +20,13 @@ from dataclasses import dataclass class BootSpec: init: str initrd: str - initrdSecrets: str kernel: str kernelParams: List[str] label: str system: str toplevel: str specialisations: Dict[str, "BootSpec"] + initrdSecrets: str | None = None @@ -131,9 +131,8 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None specialisation=" (%s)" % specialisation if specialisation else "") try: - subprocess.check_call([bootspec.initrdSecrets, "@efiSysMountPoint@%s" % (initrd)]) - except FileNotFoundError: - pass + if bootspec.initrdSecrets is not None: + subprocess.check_call([bootspec.initrdSecrets, "@efiSysMountPoint@%s" % (initrd)]) except subprocess.CalledProcessError: if current: print("failed to create initrd secrets!", file=sys.stderr) diff --git a/nixos/modules/system/boot/systemd/initrd-secrets.nix b/nixos/modules/system/boot/systemd/initrd-secrets.nix index 7b59c0cbe7b84..d375238aa146e 100644 --- a/nixos/modules/system/boot/systemd/initrd-secrets.nix +++ b/nixos/modules/system/boot/systemd/initrd-secrets.nix @@ -11,7 +11,8 @@ description = "Copy secrets into place"; # Run as early as possible wantedBy = [ "sysinit.target" ]; - before = [ "cryptsetup-pre.target" ]; + before = [ "cryptsetup-pre.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; unitConfig.DefaultDependencies = false; # We write the secrets to /.initrd-secrets and move them because this allows diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index d7e8a67c4bc9d..4ae07944afc3c 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -396,8 +396,7 @@ in { ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)} ''; - "/lib/modules".source = "${modulesClosure}/lib/modules"; - "/lib/firmware".source = "${modulesClosure}/lib/firmware"; + "/lib".source = "${modulesClosure}/lib"; "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; diff --git a/nixos/modules/system/boot/systemd/oomd.nix b/nixos/modules/system/boot/systemd/oomd.nix index fad755e278c77..000b18c01609a 100644 --- a/nixos/modules/system/boot/systemd/oomd.nix +++ b/nixos/modules/system/boot/systemd/oomd.nix @@ -3,14 +3,18 @@ cfg = config.systemd.oomd; in { + imports = [ + (lib.mkRenamedOptionModule [ "systemd" "oomd" "enableUserServices" ] [ "systemd" "oomd" "enableUserSlices" ]) + ]; + options.systemd.oomd = { enable = lib.mkEnableOption (lib.mdDoc "the `systemd-oomd` OOM killer") // { default = true; }; # Fedora enables the first and third option by default. See the 10-oomd-* files here: - # https://src.fedoraproject.org/rpms/systemd/tree/acb90c49c42276b06375a66c73673ac351025597 + # https://src.fedoraproject.org/rpms/systemd/tree/806c95e1c70af18f81d499b24cd7acfa4c36ffd6 enableRootSlice = lib.mkEnableOption (lib.mdDoc "oomd on the root slice (`-.slice`)"); enableSystemSlice = lib.mkEnableOption (lib.mdDoc "oomd on the system slice (`system.slice`)"); - enableUserServices = lib.mkEnableOption (lib.mdDoc "oomd on all user services (`user@.service`)"); + enableUserSlices = lib.mkEnableOption (lib.mdDoc "oomd on all user slices (`user@.slice`) and all user owned slices"); extraConfig = lib.mkOption { type = with lib.types; attrsOf (oneOf [ str int bool ]); @@ -44,14 +48,24 @@ in { users.groups.systemd-oom = { }; systemd.slices."-".sliceConfig = lib.mkIf cfg.enableRootSlice { - ManagedOOMSwap = "kill"; + ManagedOOMMemoryPressure = "kill"; + ManagedOOMMemoryPressureLimit = "80%"; }; systemd.slices."system".sliceConfig = lib.mkIf cfg.enableSystemSlice { - ManagedOOMSwap = "kill"; + ManagedOOMMemoryPressure = "kill"; + ManagedOOMMemoryPressureLimit = "80%"; }; - systemd.services."user@".serviceConfig = lib.mkIf cfg.enableUserServices { + systemd.slices."user-".sliceConfig = lib.mkIf cfg.enableUserSlices { ManagedOOMMemoryPressure = "kill"; - ManagedOOMMemoryPressureLimit = "50%"; + ManagedOOMMemoryPressureLimit = "80%"; + }; + systemd.user.units."slice" = lib.mkIf cfg.enableUserSlices { + text = '' + [Slice] + ManagedOOMMemoryPressure=kill + ManagedOOMMemoryPressureLimit=80% + ''; + overrideStrategy = "asDropin"; }; }; } diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index 91e30aa4c0af9..1378a0090c1df 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -406,7 +406,8 @@ in ConditionVirtualization = "!container"; DefaultDependencies = false; # needed to prevent a cycle }; - before = [ "systemd-pstore.service" ]; + before = [ "systemd-pstore.service" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; wantedBy = [ "systemd-pstore.service" ]; }; }; diff --git a/nixos/modules/tasks/filesystems/bcachefs.nix b/nixos/modules/tasks/filesystems/bcachefs.nix index 639ff87841b6a..fdb149a3d9a17 100644 --- a/nixos/modules/tasks/filesystems/bcachefs.nix +++ b/nixos/modules/tasks/filesystems/bcachefs.nix @@ -78,9 +78,10 @@ let value = { description = "Unlock bcachefs for ${fs.mountPoint}"; requiredBy = [ mountUnit ]; - before = [ mountUnit ]; - bindsTo = [ deviceUnit ]; after = [ deviceUnit ]; + before = [ mountUnit "shutdown.target" ]; + bindsTo = [ deviceUnit ]; + conflicts = [ "shutdown.target" ]; unitConfig.DefaultDependencies = false; serviceConfig = { Type = "oneshot"; @@ -122,15 +123,8 @@ in inherit assertions; # needed for systemd-remount-fs system.fsPackages = [ pkgs.bcachefs-tools ]; - - # FIXME: Replace this with `linuxPackages_testing` after NixOS 23.11 is released - # FIXME: Replace this with `linuxPackages_latest` when 6.7 is released, remove this line when the LTS version is at least 6.7 - boot.kernelPackages = lib.mkDefault ( - # FIXME: Remove warning after NixOS 23.11 is released - lib.warn "Please upgrade to Linux 6.7-rc1 or later: 'linuxPackages_testing_bcachefs' is deprecated. Use 'boot.kernelPackages = pkgs.linuxPackages_testing;' to silence this warning" - pkgs.linuxPackages_testing_bcachefs - ); - + # FIXME: Remove this line when the default kernel has bcachefs + boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest; systemd.services = lib.mapAttrs' (mkUnits "") (lib.filterAttrs (n: fs: (fs.fsType == "bcachefs") && (!utils.fsNeededForBoot fs)) config.fileSystems); } diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index fc9420fb8d28c..b38f228fc1606 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -130,7 +130,8 @@ let "systemd-ask-password-console.service" ] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target"; requiredBy = getPoolMounts prefix pool ++ [ "zfs-import.target" ]; - before = getPoolMounts prefix pool ++ [ "zfs-import.target" ]; + before = getPoolMounts prefix pool ++ [ "shutdown.target" "zfs-import.target" ]; + conflicts = [ "shutdown.target" ]; unitConfig = { DefaultDependencies = "no"; }; @@ -666,6 +667,7 @@ in # TODO FIXME See https://github.com/NixOS/nixpkgs/pull/99386#issuecomment-798813567. To not break people's bootloader and as probably not everybody would read release notes that thoroughly add inSystem. boot.loader.grub = mkIf (inInitrd || inSystem) { zfsSupport = true; + zfsPackage = cfgZfs.package; }; services.zfs.zed.settings = { diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index 53ffaa028038d..2b2d24a64cb20 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -1410,9 +1410,11 @@ in systemd.services.domainname = lib.mkIf (cfg.domain != null) { wantedBy = [ "sysinit.target" ]; - before = [ "sysinit.target" ]; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; unitConfig.DefaultDependencies = false; serviceConfig.ExecStart = ''${pkgs.nettools}/bin/domainname "${cfg.domain}"''; + serviceConfig.Type = "oneshot"; }; environment.etc.hostid = mkIf (cfg.hostId != null) { source = hostidFile; }; diff --git a/nixos/modules/tasks/trackpoint.nix b/nixos/modules/tasks/trackpoint.nix index d197a0feb337c..317613b847927 100644 --- a/nixos/modules/tasks/trackpoint.nix +++ b/nixos/modules/tasks/trackpoint.nix @@ -80,10 +80,17 @@ with lib; ACTION=="add|change", SUBSYSTEM=="input", ATTR{name}=="${cfg.device}", ATTR{device/speed}="${toString cfg.speed}", ATTR{device/sensitivity}="${toString cfg.sensitivity}" ''; - system.activationScripts.trackpoint = - '' - ${config.systemd.package}/bin/udevadm trigger --attr-match=name="${cfg.device}" + systemd.services.trackpoint = { + wantedBy = [ "sysinit.target" ] ; + before = [ "sysinit.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; + unitConfig.DefaultDependencies = false; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + serviceConfig.ExecStart = '' + ${config.systemd.package}/bin/udevadm trigger --attr-match=name="${cfg.device} ''; + }; }) (mkIf (cfg.emulateWheel) { diff --git a/nixos/modules/virtualisation/lxd-agent.nix b/nixos/modules/virtualisation/lxd-agent.nix index 63d46c1ee6aa1..8a2a1530eeb79 100644 --- a/nixos/modules/virtualisation/lxd-agent.nix +++ b/nixos/modules/virtualisation/lxd-agent.nix @@ -58,7 +58,17 @@ in { systemd.services.lxd-agent = { enable = true; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.kmod pkgs.util-linux ]; + before = [ "shutdown.target" ] ++ lib.optionals config.services.cloud-init.enable [ + "cloud-init.target" "cloud-init.service" "cloud-init-local.service" + ]; + conflicts = [ "shutdown.target" ]; + path = [ + pkgs.kmod + pkgs.util-linux + + # allow `incus exec` to find system binaries + "/run/current-system/sw" + ]; preStart = preStartScript; @@ -70,7 +80,6 @@ in { Description = "LXD - agent"; Documentation = "https://documentation.ubuntu.com/lxd/en/latest"; ConditionPathExists = "/dev/virtio-ports/org.linuxcontainers.lxd"; - Before = lib.optionals config.services.cloud-init.enable [ "cloud-init.target" "cloud-init.service" "cloud-init-local.service" ]; DefaultDependencies = "no"; StartLimitInterval = "60"; StartLimitBurst = "10"; diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index c4c856d9be30d..885fb4e078530 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -33,21 +33,11 @@ in { ''; }; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.lxd; - defaultText = lib.literalExpression "pkgs.lxd"; - description = lib.mdDoc '' - The LXD package to use. - ''; - }; + package = lib.mkPackageOption pkgs "lxd" { }; - lxcPackage = lib.mkOption { - type = lib.types.package; - default = pkgs.lxc; - defaultText = lib.literalExpression "pkgs.lxc"; - description = lib.mdDoc '' - The LXC package to use with LXD (required for AppArmor profiles). + lxcPackage = lib.mkPackageOption pkgs "lxc" { + extraDescription = '' + Required for AppArmor profiles. ''; }; @@ -149,7 +139,7 @@ in { ui = { enable = lib.mkEnableOption (lib.mdDoc "(experimental) LXD UI"); - package = lib.mkPackageOption pkgs.lxd-unwrapped "ui" { }; + package = lib.mkPackageOption pkgs [ "lxd-unwrapped" "ui" ] { }; }; }; }; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index d92fd48a6103c..3d7f3ccb62f84 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -1256,6 +1256,8 @@ in unitConfig.RequiresMountsFor = "/sysroot/nix/.ro-store"; }]; services.rw-store = { + before = [ "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; unitConfig = { DefaultDependencies = false; RequiresMountsFor = "/sysroot/nix/.rw-store"; diff --git a/nixos/modules/virtualisation/vmware-host.nix b/nixos/modules/virtualisation/vmware-host.nix index 1eaa896fe0965..094114623a424 100644 --- a/nixos/modules/virtualisation/vmware-host.nix +++ b/nixos/modules/virtualisation/vmware-host.nix @@ -85,34 +85,43 @@ in }; }; - ###### wrappers activation script + # Services - system.activationScripts.vmwareWrappers = - lib.stringAfter [ "specialfs" "users" ] - '' - mkdir -p "${parentWrapperDir}" - chmod 755 "${parentWrapperDir}" - # We want to place the tmpdirs for the wrappers to the parent dir. - wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX) - chmod a+rx "$wrapperDir" - ${lib.concatStringsSep "\n" (vmwareWrappers)} - if [ -L ${wrapperDir} ]; then - # Atomically replace the symlink - # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/ - old=$(readlink -f ${wrapperDir}) - if [ -e "${wrapperDir}-tmp" ]; then - rm --force --recursive "${wrapperDir}-tmp" - fi - ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp" - mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}" - rm --force --recursive "$old" - else - # For initial setup - ln --symbolic "$wrapperDir" "${wrapperDir}" + systemd.services."vmware-wrappers" = { + description = "Create VMVare Wrappers"; + wantedBy = [ "multi-user.target" ]; + before = [ + "vmware-authdlauncher.service" + "vmware-networks-configuration.service" + "vmware-networks.service" + "vmware-usbarbitrator.service" + ]; + after = [ "systemd-sysusers.service" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = '' + mkdir -p "${parentWrapperDir}" + chmod 755 "${parentWrapperDir}" + # We want to place the tmpdirs for the wrappers to the parent dir. + wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX) + chmod a+rx "$wrapperDir" + ${lib.concatStringsSep "\n" (vmwareWrappers)} + if [ -L ${wrapperDir} ]; then + # Atomically replace the symlink + # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/ + old=$(readlink -f ${wrapperDir}) + if [ -e "${wrapperDir}-tmp" ]; then + rm --force --recursive "${wrapperDir}-tmp" fi - ''; - - # Services + ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp" + mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}" + rm --force --recursive "$old" + else + # For initial setup + ln --symbolic "$wrapperDir" "${wrapperDir}" + fi + ''; + }; systemd.services."vmware-authdlauncher" = { description = "VMware Authentication Daemon"; diff --git a/nixos/modules/virtualisation/waydroid.nix b/nixos/modules/virtualisation/waydroid.nix index b0e85b685083b..1f466c780cf22 100644 --- a/nixos/modules/virtualisation/waydroid.nix +++ b/nixos/modules/virtualisation/waydroid.nix @@ -32,7 +32,7 @@ in system.requiredKernelConfig = [ (kCfg.isEnabled "ANDROID_BINDER_IPC") (kCfg.isEnabled "ANDROID_BINDERFS") - (kCfg.isEnabled "ASHMEM") # FIXME Needs memfd support instead on Linux 5.18 and waydroid 1.2.1 + (kCfg.isEnabled "MEMFD_CREATE") ]; /* NOTE: we always enable this flag even if CONFIG_PSI_DEFAULT_DISABLED is not on |