diff options
Diffstat (limited to 'nixos/modules')
56 files changed, 1480 insertions, 756 deletions
diff --git a/nixos/modules/config/fonts/fontdir.nix b/nixos/modules/config/fonts/fontdir.nix index 30e0dfe2566..9d41463c947 100644 --- a/nixos/modules/config/fonts/fontdir.nix +++ b/nixos/modules/config/fonts/fontdir.nix @@ -8,7 +8,7 @@ let x11Fonts = pkgs.runCommand "X11-fonts" { preferLocalBuild = true; } '' mkdir -p "$out/share/X11/fonts" - font_regexp='.*\.\(ttf\|ttc\|otf\|pcf\|pfa\|pfb\|bdf\)\(\.gz\)?' + font_regexp='.*\.\(ttf\|ttc\|otb\|otf\|pcf\|pfa\|pfb\|bdf\)\(\.gz\)?' find ${toString config.fonts.fonts} -regex "$font_regexp" \ -exec ln -sf -t "$out/share/X11/fonts" '{}' \; cd "$out/share/X11/fonts" diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index 4640a0f3d6b..4c9e286ea5f 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -539,14 +539,12 @@ in { # systemd initrd boot.initrd.systemd.users = mkOption { - visible = false; description = '' Users to include in initrd. ''; default = {}; type = types.attrsOf (types.submodule ({ name, ... }: { options.uid = mkOption { - visible = false; type = types.int; description = '' ID of the user in initrd. @@ -555,7 +553,6 @@ in { default = cfg.users.${name}.uid; }; options.group = mkOption { - visible = false; type = types.singleLineStr; description = '' Group the user belongs to in initrd. @@ -567,14 +564,12 @@ in { }; boot.initrd.systemd.groups = mkOption { - visible = false; description = '' Groups to include in initrd. ''; default = {}; type = types.attrsOf (types.submodule ({ name, ... }: { options.gid = mkOption { - visible = false; type = types.int; description = '' ID of the group in initrd. diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix index 75247286368..9e7a01c58af 100644 --- a/nixos/modules/hardware/all-firmware.nix +++ b/nixos/modules/hardware/all-firmware.nix @@ -55,7 +55,6 @@ in { intel2200BGFirmware rtl8192su-firmware rt5677-firmware - rtl8723bs-firmware rtl8761b-firmware rtw88-firmware zd1211fw diff --git a/nixos/modules/hardware/video/displaylink.nix b/nixos/modules/hardware/video/displaylink.nix index 912f53da836..ce5fbeeae53 100644 --- a/nixos/modules/hardware/video/displaylink.nix +++ b/nixos/modules/hardware/video/displaylink.nix @@ -26,6 +26,7 @@ in Identifier "DisplayLink" MatchDriver "evdi" Driver "modesetting" + Option "TearFree" "true" Option "AccelMethod" "none" EndSection ''; diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix index 573b31b439c..ea8056ff870 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix @@ -6,6 +6,7 @@ imports = [ ./installation-cd-graphical-base.nix ]; isoImage.edition = "gnome"; + isoImage.graphicalGrub = true; services.xserver.desktopManager.gnome = { # Add Firefox and other tools useful for installation to the launcher diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix index f9cbafc2865..c430048d659 100644 --- a/nixos/modules/installer/cd-dvd/iso-image.nix +++ b/nixos/modules/installer/cd-dvd/iso-image.nix @@ -283,7 +283,7 @@ let cat <<EOF > $out/EFI/boot/grub.cfg set with_fonts=false - set textmode=false + set textmode=${boolToString (!config.isoImage.graphicalGrub)} # If you want to use serial for "terminal_*" commands, you need to set one up: # Example manual configuration: # → serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 @@ -658,6 +658,16 @@ in ''; }; + isoImage.graphicalGrub = mkOption { + default = false; + type = types.bool; + example = true; + description = lib.mdDoc '' + Whether to use textmode or graphical grub. + false means we use textmode grub. + ''; + }; + }; # store them in lib so we can mkImageMediaOverride the diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index e728dcb3f19..bd715535dd6 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -476,6 +476,7 @@ ./services/games/deliantra-server.nix ./services/games/factorio.nix ./services/games/freeciv.nix + ./services/games/mchprs.nix ./services/games/minecraft-server.nix ./services/games/minetest-server.nix ./services/games/openarena.nix @@ -1282,6 +1283,7 @@ ./services/web-servers/pomerium.nix ./services/web-servers/rustus.nix ./services/web-servers/stargazer.nix + ./services/web-servers/static-web-server.nix ./services/web-servers/tomcat.nix ./services/web-servers/traefik.nix ./services/web-servers/trafficserver/default.nix diff --git a/nixos/modules/profiles/headless.nix b/nixos/modules/profiles/headless.nix index c17cb287b72..eb29f3d6510 100644 --- a/nixos/modules/profiles/headless.nix +++ b/nixos/modules/profiles/headless.nix @@ -6,8 +6,6 @@ with lib; { - boot.vesa = false; - # Don't start a tty on the serial consoles. systemd.services."serial-getty@ttyS0".enable = lib.mkDefault false; systemd.services."serial-getty@hvc0".enable = false; @@ -15,7 +13,7 @@ with lib; systemd.services."autovt@".enable = false; # Since we can't manually respond to a panic, just reboot. - boot.kernelParams = [ "panic=1" "boot.panic_on_fail" ]; + boot.kernelParams = [ "panic=1" "boot.panic_on_fail" "vga=0x317" "nomodeset" ]; # Don't allow emergency mode, because we don't have a console. systemd.enableEmergencyMode = false; diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix index 3dfc53c5261..697b6e9a0bd 100644 --- a/nixos/modules/programs/gnupg.nix +++ b/nixos/modules/programs/gnupg.nix @@ -75,9 +75,7 @@ in defaultText = literalMD ''matching the configured desktop environment''; description = lib.mdDoc '' Which pinentry interface to use. If not null, the path to the - pinentry binary will be passed to gpg-agent via commandline and - thus overrides the pinentry option in gpg-agent.conf in the user's - home directory. + pinentry binary will be set in /etc/gnupg/gpg-agent.conf. If not set at all, it'll pick an appropriate flavor depending on the system configuration (qt flavor for lxqt and plasma5, gtk2 for xfce 4.12, gnome3 on all other systems with X enabled, ncurses otherwise). @@ -94,12 +92,13 @@ in }; config = mkIf cfg.agent.enable { - environment.etc."gnupg/gpg-agent.conf".text = '' + environment.etc."gnupg/gpg-agent.conf".text = + lib.optionalString (cfg.agent.pinentryFlavor != null) '' pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry ''; # This overrides the systemd user unit shipped with the gnupg package - systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) { + systemd.user.services.gpg-agent = { unitConfig = { Description = "GnuPG cryptographic agent and passphrase cache"; Documentation = "man:gpg-agent(1)"; diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix index 35267acd6bb..00895db03fc 100644 --- a/nixos/modules/programs/shadow.nix +++ b/nixos/modules/programs/shadow.nix @@ -1,67 +1,131 @@ # Configuration for the pwdutils suite of tools: passwd, useradd, etc. - { config, lib, utils, pkgs, ... }: - with lib; - let - - /* - There are three different sources for user/group id ranges, each of which gets - used by different programs: - - The login.defs file, used by the useradd, groupadd and newusers commands - - The update-users-groups.pl file, used by NixOS in the activation phase to - decide on which ids to use for declaratively defined users without a static - id - - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used - by systemd for features like ConditionUser=@system and systemd-sysusers - */ - loginDefs = - '' - DEFAULT_HOME yes - - SYS_UID_MIN 400 - SYS_UID_MAX 999 - UID_MIN 1000 - UID_MAX 29999 - - SYS_GID_MIN 400 - SYS_GID_MAX 999 - GID_MIN 1000 - GID_MAX 29999 - - TTYGROUP tty - TTYPERM 0620 - - # Ensure privacy for newly created home directories. - UMASK 077 - - # Uncomment this and install chfn SUID to allow non-root - # users to change their account GECOS information. - # This should be made configurable. - #CHFN_RESTRICT frwh - - # The default crypt() method, keep in sync with the PAM default - ENCRYPT_METHOD YESCRYPT - ''; - - mkSetuidRoot = source: - { setuid = true; - owner = "root"; - group = "root"; - inherit source; - }; - + cfg = config.security.loginDefs; in - { + options = with types; { + security.loginDefs = { + package = mkPackageOptionMD pkgs "shadow" { }; + + chfnRestrict = mkOption { + description = mdDoc '' + Use chfn SUID to allow non-root users to change their account GECOS information. + ''; + type = nullOr str; + default = null; + }; - ###### interface - - options = { + settings = mkOption { + description = mdDoc '' + Config options for the /etc/login.defs file, that defines + the site-specific configuration for the shadow password suite. + See login.defs(5) man page for available options. + ''; + type = submodule { + freeformType = (pkgs.formats.keyValue { }).type; + /* There are three different sources for user/group id ranges, each of which gets + used by different programs: + - The login.defs file, used by the useradd, groupadd and newusers commands + - The update-users-groups.pl file, used by NixOS in the activation phase to + decide on which ids to use for declaratively defined users without a static + id + - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used + by systemd for features like ConditionUser=@system and systemd-sysusers + */ + options = { + DEFAULT_HOME = mkOption { + description = mdDoc "Indicate if login is allowed if we can't cd to the home directory."; + default = "yes"; + type = enum [ "yes" "no" ]; + }; + + ENCRYPT_METHOD = mkOption { + description = mdDoc "This defines the system default encryption algorithm for encrypting passwords."; + # The default crypt() method, keep in sync with the PAM default + default = "YESCRYPT"; + type = enum [ "YESCRYPT" "SHA512" "SHA256" "MD5" "DES"]; + }; + + SYS_UID_MIN = mkOption { + description = mdDoc "Range of user IDs used for the creation of system users by useradd or newusers."; + default = 400; + type = int; + }; + + SYS_UID_MAX = mkOption { + description = mdDoc "Range of user IDs used for the creation of system users by useradd or newusers."; + default = 999; + type = int; + }; + + UID_MIN = mkOption { + description = mdDoc "Range of user IDs used for the creation of regular users by useradd or newusers."; + default = 1000; + type = int; + }; + + UID_MAX = mkOption { + description = mdDoc "Range of user IDs used for the creation of regular users by useradd or newusers."; + default = 29999; + type = int; + }; + + SYS_GID_MIN = mkOption { + description = mdDoc "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers"; + default = 400; + type = int; + }; + + SYS_GID_MAX = mkOption { + description = mdDoc "Range of group IDs used for the creation of system groups by useradd, groupadd, or newusers"; + default = 999; + type = int; + }; + + GID_MIN = mkOption { + description = mdDoc "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers."; + default = 1000; + type = int; + }; + + GID_MAX = mkOption { + description = mdDoc "Range of group IDs used for the creation of regular groups by useradd, groupadd, or newusers."; + default = 29999; + type = int; + }; + + TTYGROUP = mkOption { + description = mdDoc '' + The terminal permissions: the login tty will be owned by the TTYGROUP group, + and the permissions will be set to TTYPERM''; + default = "tty"; + type = str; + }; + + TTYPERM = mkOption { + description = mdDoc '' + The terminal permissions: the login tty will be owned by the TTYGROUP group, + and the permissions will be set to TTYPERM''; + default = "0620"; + type = str; + }; + + # Ensure privacy for newly created home directories. + UMASK = mkOption { + description = mdDoc "The file mode creation mask is initialized to this value."; + default = "077"; + type = str; + }; + }; + }; + default = { }; + }; + }; - users.defaultUserShell = lib.mkOption { - description = lib.mdDoc '' + users.defaultUserShell = mkOption { + description = mdDoc '' This option defines the default shell assigned to user accounts. This can be either a full system path or a shell package. @@ -69,63 +133,107 @@ in used outside the store (in particular in /etc/passwd). ''; example = literalExpression "pkgs.zsh"; - type = types.either types.path types.shellPackage; + type = either path shellPackage; }; - }; - ###### implementation config = { - - environment.systemPackages = - lib.optional config.users.mutableUsers pkgs.shadow ++ - lib.optional (types.shellPackage.check config.users.defaultUserShell) - config.users.defaultUserShell; + assertions = [ + { + assertion = cfg.settings.SYS_UID_MIN <= cfg.settings.SYS_UID_MAX; + message = "SYS_UID_MIN must be less than or equal to SYS_UID_MAX"; + } + { + assertion = cfg.settings.UID_MIN <= cfg.settings.UID_MAX; + message = "UID_MIN must be less than or equal to UID_MAX"; + } + { + assertion = cfg.settings.SYS_GID_MIN <= cfg.settings.SYS_GID_MAX; + message = "SYS_GID_MIN must be less than or equal to SYS_GID_MAX"; + } + { + assertion = cfg.settings.GID_MIN <= cfg.settings.GID_MAX; + message = "GID_MIN must be less than or equal to GID_MAX"; + } + ]; + + security.loginDefs.settings.CHFN_RESTRICT = + mkIf (cfg.chfnRestrict != null) cfg.chfnRestrict; + + environment.systemPackages = optional config.users.mutableUsers cfg.package + ++ optional (types.shellPackage.check config.users.defaultUserShell) config.users.defaultUserShell + ++ optional (cfg.chfnRestrict != null) pkgs.util-linux; environment.etc = - { # /etc/login.defs: global configuration for pwdutils. You - # cannot login without it! - "login.defs".source = pkgs.writeText "login.defs" loginDefs; + # Create custom toKeyValue generator + # see https://man7.org/linux/man-pages/man5/login.defs.5.html for config specification + let + toKeyValue = generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault { } " "; + }; + in + { + # /etc/login.defs: global configuration for pwdutils. + # You cannot login without it! + "login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings); # /etc/default/useradd: configuration for useradd. - "default/useradd".source = pkgs.writeText "useradd" - '' - GROUP=100 - HOME=/home - SHELL=${utils.toShellPath config.users.defaultUserShell} - ''; + "default/useradd".source = pkgs.writeText "useradd" '' + GROUP=100 + HOME=/home + SHELL=${utils.toShellPath config.users.defaultUserShell} + ''; }; - security.pam.services = - { chsh = { rootOK = true; }; - chfn = { rootOK = true; }; - su = { rootOK = true; forwardXAuth = true; logFailures = true; }; - passwd = {}; - # Note: useradd, groupadd etc. aren't setuid root, so it - # doesn't really matter what the PAM config says as long as it - # lets root in. - useradd = { rootOK = true; }; - usermod = { rootOK = true; }; - userdel = { rootOK = true; }; - groupadd = { rootOK = true; }; - groupmod = { rootOK = true; }; - groupmems = { rootOK = true; }; - groupdel = { rootOK = true; }; - login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; }; - chpasswd = { rootOK = true; }; + security.pam.services = { + chsh = { rootOK = true; }; + chfn = { rootOK = true; }; + su = { + rootOK = true; + forwardXAuth = true; + logFailures = true; }; - - security.wrappers = { - su = mkSetuidRoot "${pkgs.shadow.su}/bin/su"; - sg = mkSetuidRoot "${pkgs.shadow.out}/bin/sg"; - newgrp = mkSetuidRoot "${pkgs.shadow.out}/bin/newgrp"; - newuidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newuidmap"; - newgidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newgidmap"; - } // lib.optionalAttrs config.users.mutableUsers { - chsh = mkSetuidRoot "${pkgs.shadow.out}/bin/chsh"; - passwd = mkSetuidRoot "${pkgs.shadow.out}/bin/passwd"; + passwd = { }; + # Note: useradd, groupadd etc. aren't setuid root, so it + # doesn't really matter what the PAM config says as long as it + # lets root in. + useradd.rootOK = true; + usermod.rootOK = true; + userdel.rootOK = true; + groupadd.rootOK = true; + groupmod.rootOK = true; + groupmems.rootOK = true; + groupdel.rootOK = true; + login = { + startSession = true; + allowNullPassword = true; + showMotd = true; + updateWtmp = true; + }; + chpasswd = { rootOK = true; }; }; + + security.wrappers = + let + mkSetuidRoot = source: { + setuid = true; + owner = "root"; + group = "root"; + inherit source; + }; + in + { + su = mkSetuidRoot "${cfg.package.su}/bin/su"; + sg = mkSetuidRoot "${cfg.package.out}/bin/sg"; + newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp"; + newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap"; + newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap"; + } + // optionalAttrs config.users.mutableUsers { + chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh"; + passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd"; + }; }; } diff --git a/nixos/modules/services/continuous-integration/jenkins/job-builder.nix b/nixos/modules/services/continuous-integration/jenkins/job-builder.nix index d6a8c2a3f7c..a8e3effd1f7 100644 --- a/nixos/modules/services/continuous-integration/jenkins/job-builder.nix +++ b/nixos/modules/services/continuous-integration/jenkins/job-builder.nix @@ -9,25 +9,20 @@ let in { options = { services.jenkins.jobBuilder = { - enable = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc '' - Whether or not to enable the Jenkins Job Builder (JJB) service. It - allows defining jobs for Jenkins in a declarative manner. + enable = mkEnableOption (mdDoc '' + the Jenkins Job Builder (JJB) service. It + allows defining jobs for Jenkins in a declarative manner. - Jobs managed through the Jenkins WebUI (or by other means) are left - unchanged. + Jobs managed through the Jenkins WebUI (or by other means) are left + unchanged. - Note that it really is declarative configuration; if you remove a - previously defined job, the corresponding job directory will be - deleted. + Note that it really is declarative configuration; if you remove a + previously defined job, the corresponding job directory will be + deleted. - Please see the Jenkins Job Builder documentation for more info: - [ - http://docs.openstack.org/infra/jenkins-job-builder/](http://docs.openstack.org/infra/jenkins-job-builder/) - ''; - }; + Please see the Jenkins Job Builder documentation for more info: + <https://jenkins-job-builder.readthedocs.io/> + ''); accessUser = mkOption { default = "admin"; diff --git a/nixos/modules/services/games/mchprs.nix b/nixos/modules/services/games/mchprs.nix new file mode 100644 index 00000000000..a65001b0b3e --- /dev/null +++ b/nixos/modules/services/games/mchprs.nix @@ -0,0 +1,341 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.mchprs; + settingsFormat = pkgs.formats.toml { }; + + whitelistFile = pkgs.writeText "whitelist.json" + (builtins.toJSON + (mapAttrsToList (n: v: { name = n; uuid = v; }) cfg.whitelist.list)); + + configToml = + (removeAttrs cfg.settings [ "address" "port" ]) // + { + bind_address = cfg.settings.address + ":" + toString cfg.settings.port; + whitelist = cfg.whitelist.enable; + }; + + configTomlFile = settingsFormat.generate "Config.toml" configToml; +in +{ + options = { + services.mchprs = { + enable = mkEnableOption "MCHPRS"; + + declarativeSettings = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to use a declarative configuration for MCHPRS. + ''; + }; + + declarativeWhitelist = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to use a declarative whitelist. + The options {option}`services.mchprs.whitelist.list` + will be applied if and only if set to `true`. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/mchprs"; + description = mdDoc '' + Directory to store MCHPRS database and other state/data files. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to open ports in the firewall for the server. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + maxRuntime = mkOption { + type = types.str; + default = "infinity"; + example = "7d"; + description = mdDoc '' + Automatically restart the server after + {option}`services.mchprs.maxRuntime`. + The time span format is described here: + https://www.freedesktop.org/software/systemd/man/systemd.time.html#Parsing%20Time%20Spans. + If `null`, then the server is not restarted automatically. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.mchprs; + defaultText = literalExpression "pkgs.mchprs"; + description = mdDoc "Version of MCHPRS to run."; + }; + + settings = mkOption { + type = types.submodule { + freeformType = settingsFormat.type; + + options = { + port = mkOption { + type = types.port; + default = 25565; + description = mdDoc '' + Port for the server. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = mdDoc '' + Address for the server. + Please use enclosing square brackets when using ipv6. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + motd = mkOption { + type = types.str; + default = "Minecraft High Performance Redstone Server"; + description = mdDoc '' + Message of the day. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + chat_format = mkOption { + type = types.str; + default = "<{username}> {message}"; + description = mdDoc '' + How to format chat message interpolating `username` + and `message` with curly braces. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + max_players = mkOption { + type = types.ints.positive; + default = 99999; + description = mdDoc '' + Maximum number of simultaneous players. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + view_distance = mkOption { + type = types.ints.positive; + default = 8; + description = mdDoc '' + Maximal distance (in chunks) between players and loaded chunks. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + bungeecord = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Enable compatibility with + [BungeeCord](https://github.com/SpigotMC/BungeeCord). + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + schemati = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Mimic the verification and directory layout used by the + Open Redstone Engineers + [Schemati plugin](https://github.com/OpenRedstoneEngineers/Schemati). + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + block_in_hitbox = mkOption { + type = types.bool; + default = true; + description = mdDoc '' + Allow placing blocks inside of players + (hitbox logic is simplified). + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + auto_redpiler = mkOption { + type = types.bool; + default = true; + description = mdDoc '' + Use redpiler automatically. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + }; + }; + default = { }; + + description = mdDoc '' + Configuration for MCHPRS via `Config.toml`. + See https://github.com/MCHPR/MCHPRS/blob/master/README.md for documentation. + ''; + }; + + whitelist = { + enable = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether or not the whitelist (in `whitelist.json`) shoud be enabled. + Only has effect when {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + list = mkOption { + type = + let + minecraftUUID = types.strMatching + "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" // { + description = "Minecraft UUID"; + }; + in + types.attrsOf minecraftUUID; + default = { }; + example = literalExpression '' + { + username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; + username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"; + }; + ''; + description = mdDoc '' + Whitelisted players, only has an effect when + {option}`services.mchprs.declarativeWhitelist` is + `true` and the whitelist is enabled + via {option}`services.mchprs.whitelist.enable`. + This is a mapping from Minecraft usernames to UUIDs. + You can use <https://mcuuid.net/> to get a + Minecraft UUID for a username. + ''; + }; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.mchprs = { + description = "MCHPRS service user"; + home = cfg.dataDir; + createHome = true; + isSystemUser = true; + group = "mchprs"; + }; + users.groups.mchprs = { }; + + systemd.services.mchprs = { + description = "MCHPRS Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${lib.getExe cfg.package}"; + Restart = "always"; + RuntimeMaxSec = cfg.maxRuntime; + User = "mchprs"; + WorkingDirectory = cfg.dataDir; + + StandardOutput = "journal"; + StandardError = "journal"; + + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + + preStart = + (if cfg.declarativeSettings then '' + if [ -e .declarativeSettings ]; then + + # Settings were declarative before, no need to back up anything + cp -f ${configTomlFile} Config.toml + + else + + # Declarative settings for the first time, backup stateful files + cp -b --suffix=.stateful ${configTomlFile} Config.toml + + echo "Autogenerated file that implies that this server configuration is managed declaratively by NixOS" \ + > .declarativeSettings + + fi + '' else '' + if [ -e .declarativeSettings ]; then + rm .declarativeSettings + fi + '') + (if cfg.declarativeWhitelist then '' + if [ -e .declarativeWhitelist ]; then + + # Whitelist was declarative before, no need to back up anything + ln -sf ${whitelistFile} whitelist.json + + else + + # Declarative whitelist for the first time, backup stateful files + ln -sb --suffix=.stateful ${whitelistFile} whitelist.json + + echo "Autogenerated file that implies that this server's whitelist is managed declaratively by NixOS" \ + > .declarativeWhitelist + + fi + '' else '' + if [ -e .declarativeWhitelist ]; then + rm .declarativeWhitelist + fi + ''); + }; + + networking.firewall = mkIf (cfg.declarativeSettings && cfg.openFirewall) { + allowedUDPPorts = [ cfg.settings.port ]; + allowedTCPPorts = [ cfg.settings.port ]; + }; + }; + + meta.maintainers = with maintainers; [ gdd ]; +} diff --git a/nixos/modules/services/games/minetest-server.nix b/nixos/modules/services/games/minetest-server.nix index 578364ec542..8dc36015349 100644 --- a/nixos/modules/services/games/minetest-server.nix +++ b/nixos/modules/services/games/minetest-server.nix @@ -3,15 +3,52 @@ with lib; let + CONTAINS_NEWLINE_RE = ".*\n.*"; + # The following values are reserved as complete option values: + # { - start of a group. + # """ - start of a multi-line string. + RESERVED_VALUE_RE = "[[:space:]]*(\"\"\"|\\{)[[:space:]]*"; + NEEDS_MULTILINE_RE = "${CONTAINS_NEWLINE_RE}|${RESERVED_VALUE_RE}"; + + # There is no way to encode """ on its own line in a Minetest config. + UNESCAPABLE_RE = ".*\n\"\"\"\n.*"; + + toConfMultiline = name: value: + assert lib.assertMsg + ((builtins.match UNESCAPABLE_RE value) == null) + ''""" can't be on its own line in a minetest config.''; + "${name} = \"\"\"\n${value}\n\"\"\"\n"; + + toConf = values: + lib.concatStrings + (lib.mapAttrsToList + (name: value: { + bool = "${name} = ${toString value}\n"; + int = "${name} = ${toString value}\n"; + null = ""; + set = "${name} = {\n${toConf value}}\n"; + string = + if (builtins.match NEEDS_MULTILINE_RE value) != null + then toConfMultiline name value + else "${name} = ${value}\n"; + }.${builtins.typeOf value}) + values); + cfg = config.services.minetest-server; - flag = val: name: optionalString (val != null) "--${name} ${toString val} "; + flag = val: name: lib.optionals (val != null) ["--${name}" "${toString val}"]; + flags = [ - (flag cfg.gameId "gameid") - (flag cfg.world "world") - (flag cfg.configPath "config") - (flag cfg.logPath "logfile") - (flag cfg.port "port") - ]; + "--server" + ] + ++ ( + if cfg.configPath != null + then ["--config" cfg.configPath] + else ["--config" (builtins.toFile "minetest.conf" (toConf cfg.config))]) + ++ (flag cfg.gameId "gameid") + ++ (flag cfg.world "world") + ++ (flag cfg.logPath "logfile") + ++ (flag cfg.port "port") + ++ cfg.extraArgs; in { options = { @@ -55,6 +92,16 @@ in ''; }; + config = mkOption { + type = types.attrsOf types.anything; + default = {}; + description = lib.mdDoc '' + Settings to add to the minetest config file. + + This option is ignored if `configPath` is set. + ''; + }; + logPath = mkOption { type = types.nullOr types.path; default = null; @@ -75,6 +122,14 @@ in If set to null, the default 30000 will be used. ''; }; + + extraArgs = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Additional command line flags to pass to the minetest executable. + ''; + }; }; }; @@ -100,7 +155,7 @@ in script = '' cd /var/lib/minetest - exec ${pkgs.minetest}/bin/minetest --server ${concatStrings flags} + exec ${pkgs.minetest}/bin/minetest ${lib.escapeShellArgs flags} ''; }; }; diff --git a/nixos/modules/services/hardware/pcscd.nix b/nixos/modules/services/hardware/pcscd.nix index a09c64645c4..a9e4998efe3 100644 --- a/nixos/modules/services/hardware/pcscd.nix +++ b/nixos/modules/services/hardware/pcscd.nix @@ -24,7 +24,6 @@ in plugins = mkOption { type = types.listOf types.package; - default = [ pkgs.ccid ]; defaultText = literalExpression "[ pkgs.ccid ]"; example = literalExpression "[ pkgs.pcsc-cyberjack ]"; description = lib.mdDoc "Plugin packages to be used for PCSC-Lite."; @@ -56,6 +55,8 @@ in environment.systemPackages = [ package ]; systemd.packages = [ (getBin package) ]; + services.pcscd.plugins = [ pkgs.ccid ]; + systemd.sockets.pcscd.wantedBy = [ "sockets.target" ]; systemd.services.pcscd = { diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index e3451e1f6c7..56120094871 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -296,7 +296,6 @@ in packages = mkOption { type = types.listOf types.path; default = []; - visible = false; description = lib.mdDoc '' *This will only be used when systemd is used in stage 1.* @@ -311,7 +310,6 @@ in binPackages = mkOption { type = types.listOf types.path; default = []; - visible = false; description = lib.mdDoc '' *This will only be used when systemd is used in stage 1.* diff --git a/nixos/modules/services/misc/ankisyncd.nix b/nixos/modules/services/misc/ankisyncd.nix index 5198b824202..7be8dc7dab8 100644 --- a/nixos/modules/services/misc/ankisyncd.nix +++ b/nixos/modules/services/misc/ankisyncd.nix @@ -9,22 +9,16 @@ let stateDir = "/var/lib/${name}"; - authDbPath = "${stateDir}/auth.db"; + toml = pkgs.formats.toml {}; - sessionDbPath = "${stateDir}/session.db"; - - configFile = pkgs.writeText "ankisyncd.conf" (lib.generators.toINI {} { - sync_app = { + configFile = toml.generate "ankisyncd.conf" { + listen = { host = cfg.host; port = cfg.port; - data_root = stateDir; - auth_db_path = authDbPath; - session_db_path = sessionDbPath; - - base_url = "/sync/"; - base_media_url = "/msync/"; }; - }); + paths.root_dir = stateDir; + # encryption.ssl_enable / cert_file / key_file + }; in { options.services.ankisyncd = { @@ -59,8 +53,6 @@ in config = mkIf cfg.enable { networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; - environment.etc."ankisyncd/ankisyncd.conf".source = configFile; - systemd.services.ankisyncd = { description = "ankisyncd - Anki sync server"; after = [ "network.target" ]; @@ -71,7 +63,7 @@ in Type = "simple"; DynamicUser = true; StateDirectory = name; - ExecStart = "${cfg.package}/bin/ankisyncd"; + ExecStart = "${cfg.package}/bin/ankisyncd --config ${configFile}"; Restart = "always"; }; }; diff --git a/nixos/modules/services/misc/calibre-server.nix b/nixos/modules/services/misc/calibre-server.nix index 77c60381a31..e1ddae1de1f 100644 --- a/nixos/modules/services/misc/calibre-server.nix +++ b/nixos/modules/services/misc/calibre-server.nix @@ -6,6 +6,17 @@ let cfg = config.services.calibre-server; + documentationLink = "https://manual.calibre-ebook.com"; + generatedDocumentationLink = documentationLink + "/generated/en/calibre-server.html"; + + execFlags = (concatStringsSep " " + (mapAttrsToList (k: v: "${k} ${toString v}") (filterAttrs (name: value: value != null) { + "--listen-on" = cfg.host; + "--port" = cfg.port; + "--auth-mode" = cfg.auth.mode; + "--userdb" = cfg.auth.userDb; + }) ++ [(optionalString (cfg.auth.enable == true) "--enable-auth")]) + ); in { @@ -18,52 +29,100 @@ in ) ]; - ###### interface - options = { services.calibre-server = { enable = mkEnableOption (lib.mdDoc "calibre-server"); + package = lib.mkPackageOptionMD pkgs "calibre" { }; libraries = mkOption { + type = types.listOf types.path; + default = [ "/var/lib/calibre-server" ]; description = lib.mdDoc '' + Make sure each library path is initialized before service startup. The directories of the libraries to serve. They must be readable for the user under which the server runs. + See the [calibredb documentation](${documentationLink}/generated/en/calibredb.html#add) for details. ''; - type = types.listOf types.path; }; user = mkOption { - description = lib.mdDoc "The user under which calibre-server runs."; type = types.str; default = "calibre-server"; + description = lib.mdDoc "The user under which calibre-server runs."; }; group = mkOption { - description = lib.mdDoc "The group under which calibre-server runs."; type = types.str; default = "calibre-server"; + description = lib.mdDoc "The group under which calibre-server runs."; }; - }; - }; + host = mkOption { + type = types.str; + default = "0.0.0.0"; + example = "::1"; + description = lib.mdDoc '' + The interface on which to listen for connections. + See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-listen-on) for details. + ''; + }; + + port = mkOption { + default = 8080; + type = types.port; + description = lib.mdDoc '' + The port on which to listen for connections. + See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-port) for details. + ''; + }; + auth = { + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Password based authentication to access the server. + See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-enable-auth) for details. + ''; + }; - ###### implementation + mode = mkOption { + type = types.enum [ "auto" "basic" "digest" ]; + default = "auto"; + description = lib.mdDoc '' + Choose the type of authentication used. + Set the HTTP authentication mode used by the server. + See the [calibre-server documentation](${generatedDocumentationLink}#cmdoption-calibre-server-auth-mode) for details. + ''; + }; + + userDb = mkOption { + default = null; + type = types.nullOr types.path; + description = lib.mdDoc '' + Choose users database file to use for authentication. + Make sure users database file is initialized before service startup. + See the [calibre-server documentation](${documentationLink}/server.html#managing-user-accounts-from-the-command-line-only) for details. + ''; + }; + }; + }; + }; config = mkIf cfg.enable { systemd.services.calibre-server = { - description = "Calibre Server"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = cfg.user; - Restart = "always"; - ExecStart = "${pkgs.calibre}/bin/calibre-server ${lib.concatStringsSep " " cfg.libraries}"; - }; - + description = "Calibre Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = cfg.user; + Restart = "always"; + ExecStart = "${cfg.package}/bin/calibre-server ${lib.concatStringsSep " " cfg.libraries} ${execFlags}"; }; + }; + environment.systemPackages = [ pkgs.calibre ]; users.users = optionalAttrs (cfg.user == "calibre-server") { @@ -83,4 +142,5 @@ in }; + meta.maintainers = with lib.maintainers; [ gaelreyrol ]; } diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix index 5c35cfa177a..945009f0058 100644 --- a/nixos/modules/services/misc/gitea.nix +++ b/nixos/modules/services/misc/gitea.nix @@ -467,10 +467,8 @@ in systemd.tmpfiles.rules = [ "d '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -" "z '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -" - "Z '${cfg.dump.backupDir}' - ${cfg.user} ${cfg.group} - -" "d '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -" "z '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -" - "Z '${cfg.repositoryRoot}' - ${cfg.user} ${cfg.group} - -" "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" "d '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" "d '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -" @@ -484,7 +482,6 @@ in "z '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" "z '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -" "z '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -" - "Z '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -" # If we have a folder or symlink with gitea locales, remove it # And symlink the current gitea locales in place @@ -493,13 +490,12 @@ in ] ++ lib.optionals cfg.lfs.enable [ "d '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -" "z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -" - "Z '${cfg.lfs.contentDir}' - ${cfg.user} ${cfg.group} - -" ]; systemd.services.gitea = { description = "gitea"; after = [ "network.target" ] ++ optional usePostgresql "postgresql.service" ++ optional useMysql "mysql.service"; - requires = optional usePostgresql "postgresql.service" ++ optional useMysql "mysql.service"; + requires = optional (cfg.database.createDatabase && usePostgresql) "postgresql.service" ++ optional (cfg.database.createDatabase && useMysql) "mysql.service"; wantedBy = [ "multi-user.target" ]; path = [ cfg.package pkgs.git pkgs.gnupg ]; @@ -587,7 +583,10 @@ in Restart = "always"; # Runtime directory and mode RuntimeDirectory = "gitea"; - RuntimeDirectoryMode = "0755"; + RuntimeDirectoryMode = "0750"; + # Proc filesystem + ProcSubset = "pid"; + ProtectProc = "invisible"; # Access write directories ReadWritePaths = [ cfg.customDir cfg.dump.backupDir cfg.repositoryRoot cfg.stateDir cfg.lfs.contentDir ]; UMask = "0027"; @@ -607,15 +606,17 @@ in ProtectKernelModules = true; ProtectKernelLogs = true; ProtectControlGroups = true; - RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ]; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; LockPersonality = true; MemoryDenyWriteExecute = true; RestrictRealtime = true; RestrictSUIDSGID = true; + RemoveIPC = true; PrivateMounts = true; # System Call Filtering SystemCallArchitectures = "native"; - SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap"; + SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" "setrlimit" ]; }; environment = { diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 8958d375b2b..a442cb237ed 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -152,6 +152,7 @@ let api_url = "http://${config.services.dockerRegistry.listenAddress}:${toString config.services.dockerRegistry.port}/"; issuer = cfg.registry.issuer; }; + elasticsearch.indexer_path = "${pkgs.gitlab-elasticsearch-indexer}/bin/gitlab-elasticsearch-indexer"; extra = {}; uploads.storage_path = cfg.statePath; pages = optionalAttrs cfg.pages.enable { @@ -1644,6 +1645,7 @@ in { nodejs procps gnupg + gzip ]; serviceConfig = { Type = "notify"; diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix index 4eec9610b5e..d607e92e5ec 100644 --- a/nixos/modules/services/misc/gollum.nix +++ b/nixos/modules/services/misc/gollum.nix @@ -91,18 +91,30 @@ in The package used in the service ''; }; + + user = mkOption { + type = types.str; + default = "gollum"; + description = lib.mdDoc "Specifies the owner of the wiki directory"; + }; + + group = mkOption { + type = types.str; + default = "gollum"; + description = lib.mdDoc "Specifies the owner group of the wiki directory"; + }; }; config = mkIf cfg.enable { - users.users.gollum = { - group = config.users.users.gollum.name; + users.users.gollum = mkIf (cfg.user == "gollum") { + group = cfg.group; description = "Gollum user"; createHome = false; isSystemUser = true; }; - users.groups.gollum = { }; + users.groups."${cfg.group}" = { }; systemd.tmpfiles.rules = [ "d '${cfg.stateDir}' - ${config.users.users.gollum.name} ${config.users.groups.gollum.name} - -" @@ -120,8 +132,8 @@ in ''; serviceConfig = { - User = config.users.users.gollum.name; - Group = config.users.groups.gollum.name; + User = cfg.user; + Group = cfg.group; WorkingDirectory = cfg.stateDir; ExecStart = '' ${cfg.package}/bin/gollum \ @@ -142,5 +154,5 @@ in }; }; - meta.maintainers = with lib.maintainers; [ erictapen bbenno ]; + meta.maintainers = with lib.maintainers; [ erictapen bbenno joscha ]; } diff --git a/nixos/modules/services/misc/n8n.nix b/nixos/modules/services/misc/n8n.nix index cdfe9dc8482..c72ed106f2d 100644 --- a/nixos/modules/services/misc/n8n.nix +++ b/nixos/modules/services/misc/n8n.nix @@ -26,6 +26,15 @@ in ''; }; + webhookUrl = mkOption { + type = types.string; + default = ""; + description = lib.mdDoc '' + WEBHOOK_URL for n8n, in case we're running behind a reverse proxy. + This cannot be set through configuration and must reside in an environment variable. + ''; + }; + }; config = mkIf cfg.enable { @@ -44,6 +53,7 @@ in N8N_USER_FOLDER = "/var/lib/n8n"; HOME = "/var/lib/n8n"; N8N_CONFIG_FILES = "${configFile}"; + WEBHOOK_URL = "${webhookUrl}"; # Don't phone home N8N_DIAGNOSTICS_ENABLED = "false"; diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 7b1282b15b3..44cf71ad401 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -656,7 +656,7 @@ in to view the current value. By default it is empty. Nix configurations defined under {option}`nix.*` will be translated and applied to this - option. In addition, configuration specified in {option}`nix.extraOptions` which will be appended + option. In addition, configuration specified in {option}`nix.extraOptions` will be appended verbatim to the resulting config file. ''; }; diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix index db8148c060e..0398229a13d 100644 --- a/nixos/modules/services/misc/nix-optimise.nix +++ b/nixos/modules/services/misc/nix-optimise.nix @@ -1,28 +1,21 @@ { config, lib, ... }: -with lib; - let cfg = config.nix.optimise; in { - - ###### interface - options = { - nix.optimise = { - - automatic = mkOption { + automatic = lib.mkOption { default = false; - type = types.bool; + type = lib.types.bool; description = lib.mdDoc "Automatically run the nix store optimiser at a specific time."; }; - dates = mkOption { + dates = lib.mkOption { default = ["03:45"]; - type = types.listOf types.str; + type = with lib.types; listOf str; description = lib.mdDoc '' Specification (in the format described by {manpage}`systemd.time(7)`) of the time at @@ -32,9 +25,6 @@ in }; }; - - ###### implementation - config = { assertions = [ { @@ -43,14 +33,19 @@ in } ]; - systemd.services.nix-optimise = lib.mkIf config.nix.enable - { description = "Nix Store Optimiser"; + systemd = lib.mkIf config.nix.enable { + services.nix-optimise = { + description = "Nix Store Optimiser"; # No point this if the nix daemon (and thus the nix store) is outside unitConfig.ConditionPathIsReadWrite = "/nix/var/nix/daemon-socket"; serviceConfig.ExecStart = "${config.nix.package}/bin/nix-store --optimise"; - startAt = optionals cfg.automatic cfg.dates; + startAt = lib.optionals cfg.automatic cfg.dates; }; + timers.nix-optimise.timerConfig = { + Persistent = true; + RandomizedDelaySec = 1800; + }; + }; }; - } diff --git a/nixos/modules/services/misc/ntfy-sh.nix b/nixos/modules/services/misc/ntfy-sh.nix index 3258f91f704..8fc1df93afb 100644 --- a/nixos/modules/services/misc/ntfy-sh.nix +++ b/nixos/modules/services/misc/ntfy-sh.nix @@ -32,7 +32,25 @@ in }; settings = mkOption { - type = types.submodule { freeformType = settingsFormat.type; }; + type = types.submodule { + freeformType = settingsFormat.type; + options = { + base-url = mkOption { + type = types.str; + example = "https://ntfy.example"; + description = lib.mdDoc '' + Public facing base URL of the service + + This setting is required for any of the following features: + - attachments (to return a download URL) + - e-mail sending (for the topic URL in the email footer) + - iOS push notifications for self-hosted servers + (to calculate the Firebase poll_request topic) + - Matrix Push Gateway (to validate that the pushkey is correct) + ''; + }; + }; + }; default = { }; diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix index 4199e771330..8fe628a4088 100644 --- a/nixos/modules/services/misc/paperless.nix +++ b/nixos/modules/services/misc/paperless.nix @@ -86,12 +86,11 @@ let SupplementaryGroups = optional enableRedis redisServer.user; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged @setuid @keyring" ]; - # Does not work well with the temporary root - #UMask = "0066"; + UMask = "0066"; }; in { - meta.maintainers = with maintainers; [ erikarvstedt Flakebi ]; + meta.maintainers = with maintainers; [ erikarvstedt Flakebi leona ]; imports = [ (mkRenamedOptionModule [ "services" "paperless-ng" ] [ "services" "paperless" ]) diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix index 12f89196808..571b9a3aeeb 100644 --- a/nixos/modules/services/monitoring/grafana.nix +++ b/nixos/modules/services/monitoring/grafana.nix @@ -1126,7 +1126,7 @@ in It will notify, via the UI, when a new version is available. The check itself will not prompt any auto-updates of the Grafana software, nor will it send any sensitive information. ''; - default = true; + default = false; type = types.bool; }; diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index 501f126e1a3..397125b5123 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -56,6 +56,7 @@ let "nut" "openldap" "openvpn" + "php-fpm" "pihole" "postfix" "postgres" @@ -65,6 +66,7 @@ let "redis" "rspamd" "rtl_433" + "scaphandre" "script" "shelly" "snmp" @@ -301,6 +303,21 @@ in Please specify either 'services.prometheus.exporters.sql.configuration' or 'services.prometheus.exporters.sql.configFile' ''; + } { + assertion = cfg.scaphandre.enable -> (pkgs.stdenv.targetPlatform.isx86_64 == true); + message = '' + Scaphandre only support x86_64 architectures. + ''; + } { + assertion = cfg.scaphandre.enable -> ((lib.kernel.whenHelpers pkgs.linux.version).whenOlder "5.11" true).condition == false; + message = '' + Scaphandre requires a kernel version newer than '5.11', '${pkgs.linux.version}' given. + ''; + } { + assertion = cfg.scaphandre.enable -> (builtins.elem "intel_rapl_common" config.boot.kernelModules); + message = '' + Scaphandre needs 'intel_rapl_common' kernel module to be enabled. Please add it in 'boot.kernelModules'. + ''; } ] ++ (flip map (attrNames exporterOpts) (exporter: { assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall; message = '' diff --git a/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix b/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix new file mode 100644 index 00000000000..8f6942002f7 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix @@ -0,0 +1,65 @@ +{ config +, lib +, pkgs +, options +}: + +let + logPrefix = "services.prometheus.exporter.php-fpm"; + cfg = config.services.prometheus.exporters.php-fpm; +in { + port = 9253; + extraOpts = { + package = lib.mkPackageOptionMD pkgs "prometheus-php-fpm-exporter" {}; + + telemetryPath = lib.mkOption { + type = lib.types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/root/prometheus-php-fpm-exporter.env"; + description = lib.mdDoc '' + Environment file as defined in {manpage}`systemd.exec(5)`. + + Secrets may be passed to the service without adding them to the + world-readable Nix store, by specifying placeholder variables as + the option value in Nix and setting these variables accordingly in the + environment file. + + Environment variables from this file will be interpolated into the + config file using envsubst with this syntax: + `$ENVIRONMENT ''${VARIABLE}` + + For variables to use see [options and defaults](https://github.com/hipages/php-fpm_exporter#options-and-defaults). + + The main use is to set the PHP_FPM_SCRAPE_URI that indicate how to connect to PHP-FPM process. + + ``` + # Content of the environment file + PHP_FPM_SCRAPE_URI="unix:///tmp/php.sock;/status" + ``` + + Note that this file needs to be available on the host on which + this exporter is running. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; + ExecStart = '' + ${lib.getExe cfg.package} server \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + ${lib.concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix b/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix new file mode 100644 index 00000000000..3b6ebf65b09 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix @@ -0,0 +1,33 @@ +{ config +, lib +, pkgs +, options +}: + +let + logPrefix = "services.prometheus.exporter.scaphandre"; + cfg = config.services.prometheus.exporters.scaphandre; +in { + port = 8080; + extraOpts = { + telemetryPath = lib.mkOption { + type = lib.types.str; + default = "/metrics"; + description = lib.mdDoc '' + Path under which to expose metrics. + ''; + }; + }; + + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.scaphandre}/bin/scaphandre prometheus \ + --address ${cfg.listenAddress} \ + --port ${toString cfg.port} \ + --suffix ${cfg.telemetryPath} \ + ${lib.concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/pdns-recursor.nix b/nixos/modules/services/networking/pdns-recursor.nix index 2f07cefc736..f929532ba09 100644 --- a/nixos/modules/services/networking/pdns-recursor.nix +++ b/nixos/modules/services/networking/pdns-recursor.nix @@ -159,6 +159,8 @@ in { config = mkIf cfg.enable { + environment.etc."pdns-recursor".source = configDir; + services.pdns-recursor.settings = mkDefaultAttrs { local-address = cfg.dns.address; local-port = cfg.dns.port; diff --git a/nixos/modules/services/networking/powerdns.nix b/nixos/modules/services/networking/powerdns.nix index 850a128cf1a..03bf93301d8 100644 --- a/nixos/modules/services/networking/powerdns.nix +++ b/nixos/modules/services/networking/powerdns.nix @@ -38,6 +38,8 @@ in { config = mkIf cfg.enable { + environment.etc.pdns.source = finalConfigDir; + systemd.packages = [ pkgs.pdns ]; systemd.services.pdns = { diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix index 6c57ddbde2d..40648c72481 100644 --- a/nixos/modules/services/networking/searx.nix +++ b/nixos/modules/services/networking/searx.nix @@ -10,6 +10,8 @@ let settingsFile = pkgs.writeText "settings.yml" (builtins.toJSON cfg.settings); + limiterSettingsFile = (pkgs.formats.toml { }).generate "limiter.toml" cfg.limiterSettings; + generateConfig = '' cd ${runDir} @@ -65,6 +67,15 @@ in ''; }; + redisCreateLocally = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Configure a local Redis server for SearXNG. This is required if you + want to enable the rate limiter and bot protection of SearXNG. + ''; + }; + settings = mkOption { type = types.attrsOf settingType; default = { }; @@ -111,6 +122,31 @@ in ''; }; + limiterSettings = mkOption { + type = types.attrsOf settingType; + default = { }; + example = literalExpression '' + { + real_ip = { + x_for = 1; + ipv4_prefix = 32; + ipv6_prefix = 56; + } + botdetection.ip_lists.block_ip = [ + # "93.184.216.34" # example.org + ]; + } + ''; + description = lib.mdDoc '' + Limiter settings for SearXNG. + + ::: {.note} + For available settings, see the SearXNG + [schema file](https://github.com/searxng/searxng/blob/master/searx/botdetection/limiter.toml). + ::: + ''; + }; + package = mkOption { type = types.package; default = pkgs.searx; @@ -158,6 +194,17 @@ in ###### implementation config = mkIf cfg.enable { + assertions = [ + { + assertion = (cfg.limiterSettings != { }) -> cfg.package.pname == "searxng"; + message = "services.searx.limiterSettings requires services.searx.package to be searxng."; + } + { + assertion = cfg.redisCreateLocally -> cfg.package.pname == "searxng"; + message = "services.searx.redisCreateLocally requires services.searx.package to be searxng."; + } + ]; + environment.systemPackages = [ cfg.package ]; users.users.searx = @@ -206,6 +253,7 @@ in services.searx.settings = { # merge NixOS settings with defaults settings.yml use_default_settings = mkDefault true; + redis.url = lib.mkIf cfg.redisCreateLocally "unix://${config.services.redis.servers.searx.unixSocket}"; }; services.uwsgi = mkIf (cfg.runInUwsgi) { @@ -231,7 +279,16 @@ in } // cfg.uwsgiConfig; }; + services.redis.servers.searx = lib.mkIf cfg.redisCreateLocally { + enable = true; + user = "searx"; + port = 0; + }; + + environment.etc."searxng/limiter.toml" = lib.mkIf (cfg.limiterSettings != { }) { + source = limiterSettingsFile; + }; }; - meta.maintainers = with maintainers; [ rnhmjoj ]; + meta.maintainers = with maintainers; [ rnhmjoj _999eagle ]; } diff --git a/nixos/modules/services/networking/sing-box.nix b/nixos/modules/services/networking/sing-box.nix index d32725f7714..a884bcd271e 100644 --- a/nixos/modules/services/networking/sing-box.nix +++ b/nixos/modules/services/networking/sing-box.nix @@ -56,6 +56,7 @@ in systemd.services.sing-box = { preStart = '' + umask 0077 mkdir -p /etc/sing-box ${utils.genJqSecretsReplacementSnippet cfg.settings "/etc/sing-box/config.json"} ''; diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index fb9774bafde..e75239e059d 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -279,10 +279,12 @@ in settings = mkOption { description = lib.mdDoc "Configuration for `sshd_config(5)`."; default = { }; - example = literalExpression ''{ - UseDns = true; - PasswordAuthentication = false; - }''; + example = literalExpression '' + { + UseDns = true; + PasswordAuthentication = false; + } + ''; type = types.submodule ({name, ...}: { freeformType = settingsFormat.type; options = { diff --git a/nixos/modules/services/networking/thelounge.nix b/nixos/modules/services/networking/thelounge.nix index 2c4c32bc7cf..321e46fb5d4 100644 --- a/nixos/modules/services/networking/thelounge.nix +++ b/nixos/modules/services/networking/thelounge.nix @@ -48,14 +48,16 @@ in extraConfig = mkOption { default = { }; type = types.attrs; - example = literalExpression ''{ - reverseProxy = true; - defaults = { - name = "Your Network"; - host = "localhost"; - port = 6697; - }; - }''; + example = literalExpression '' + { + reverseProxy = true; + defaults = { + name = "Your Network"; + host = "localhost"; + port = 6697; + }; + } + ''; description = lib.mdDoc '' The Lounge's {file}`config.js` contents as attribute set (will be converted to JSON to generate the configuration file). diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix index eebf2a2f7b5..9393fa75128 100644 --- a/nixos/modules/services/security/fail2ban.nix +++ b/nixos/modules/services/security/fail2ban.nix @@ -3,23 +3,44 @@ with lib; let - cfg = config.services.fail2ban; - fail2banConf = pkgs.writeText "fail2ban.local" cfg.daemonConfig; + settingsFormat = pkgs.formats.keyValue { }; - jailConf = pkgs.writeText "jail.local" '' - [INCLUDES] + configFormat = pkgs.formats.ini { + mkKeyValue = generators.mkKeyValueDefault { } " = "; + }; - before = paths-nixos.conf + mkJailConfig = name: attrs: + optionalAttrs (name != "DEFAULT") { inherit (attrs) enabled; } // + optionalAttrs (attrs.filter != null) { filter = if (builtins.isString filter) then filter else name; } // + attrs.settings; - ${concatStringsSep "\n" (attrValues (flip mapAttrs cfg.jails (name: def: - optionalString (def != "") - '' - [${name}] - ${def} - '')))} - ''; + mkFilter = name: attrs: nameValuePair "fail2ban/filter.d/${name}.conf" { + source = configFormat.generate "filter.d/${name}.conf" attrs.filter; + }; + + fail2banConf = configFormat.generate "fail2ban.local" cfg.daemonSettings; + + strJails = filterAttrs (_: builtins.isString) cfg.jails; + attrsJails = filterAttrs (_: builtins.isAttrs) cfg.jails; + + jailConf = + let + configFile = configFormat.generate "jail.local" ( + { INCLUDES.before = "paths-nixos.conf"; } // (mapAttrs mkJailConfig attrsJails) + ); + extraConfig = concatStringsSep "\n" (attrValues (mapAttrs + (name: def: + optionalString (def != "") + '' + [${name}] + ${def} + '') + strJails)); + + in + pkgs.concatText "jail.local" [ configFile (pkgs.writeText "extra-jail.local" extraConfig) ]; pathsConf = pkgs.writeText "paths-nixos.conf" '' # NixOS @@ -32,15 +53,18 @@ let [DEFAULT] ''; - in { + imports = [ + (mkRemovedOptionModule [ "services" "fail2ban" "daemonConfig" ] "The daemon is now configured through the attribute set `services.fail2ban.daemonSettings`.") + (mkRemovedOptionModule [ "services" "fail2ban" "extraSettings" ] "The extra default configuration can now be set using `services.fail2ban.jails.DEFAULT.settings`.") + ]; + ###### interface options = { - services.fail2ban = { enable = mkOption { default = false; @@ -69,7 +93,7 @@ in }; extraPackages = mkOption { - default = []; + default = [ ]; type = types.listOf types.package; example = lib.literalExpression "[ pkgs.ipset ]"; description = lib.mdDoc '' @@ -180,7 +204,7 @@ in example = true; description = lib.mdDoc '' "bantime.overalljails" (if true) specifies the search of IP in the database will be executed - cross over all jails, if false (default), only current jail of the ban IP will be searched + cross over all jails, if false (default), only current jail of the ban IP will be searched. ''; }; @@ -194,60 +218,75 @@ in ''; }; - daemonConfig = mkOption { - default = '' - [Definition] - logtarget = SYSLOG - socket = /run/fail2ban/fail2ban.sock - pidfile = /run/fail2ban/fail2ban.pid - dbfile = /var/lib/fail2ban/fail2ban.sqlite3 - ''; - type = types.lines; - description = lib.mdDoc '' - The contents of Fail2ban's main configuration file. It's - generally not necessary to change it. - ''; - }; + daemonSettings = mkOption { + inherit (configFormat) type; - extraSettings = mkOption { - type = with types; attrsOf (oneOf [ bool ints.positive str ]); - default = {}; - description = lib.mdDoc '' - Extra default configuration for all jails (i.e. `[DEFAULT]`). See - <https://github.com/fail2ban/fail2ban/blob/master/config/jail.conf> for an overview. - ''; - example = literalExpression '' + defaultText = literalExpression '' { - findtime = "15m"; + Definition = { + logtarget = "SYSLOG"; + socket = "/run/fail2ban/fail2ban.sock"; + pidfile = "/run/fail2ban/fail2ban.pid"; + dbfile = "/var/lib/fail2ban/fail2ban.sqlite3"; + }; } ''; + description = lib.mdDoc '' + The contents of Fail2ban's main configuration file. + It's generally not necessary to change it. + ''; }; jails = mkOption { default = { }; example = literalExpression '' - { apache-nohome-iptables = ''' - # Block an IP address if it accesses a non-existent - # home directory more than 5 times in 10 minutes, - # since that indicates that it's scanning. - filter = apache-nohome - action = iptables-multiport[name=HTTP, port="http,https"] - logpath = /var/log/httpd/error_log* - backend = auto - findtime = 600 - bantime = 600 - maxretry = 5 - '''; - dovecot = ''' - # block IPs which failed to log-in - # aggressive mode add blocking for aborted connections - enabled = true - filter = dovecot[mode=aggressive] - maxretry = 3 - '''; - } + { + apache-nohome-iptables = { + settings = { + # Block an IP address if it accesses a non-existent + # home directory more than 5 times in 10 minutes, + # since that indicates that it's scanning. + filter = "apache-nohome"; + action = '''iptables-multiport[name=HTTP, port="http,https"]'''; + logpath = "/var/log/httpd/error_log*"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + dovecot = { + settings = { + # block IPs which failed to log-in + # aggressive mode add blocking for aborted connections + filter = "dovecot[mode=aggressive]"; + maxretry = 3; + }; + }; + }; ''; - type = types.attrsOf types.lines; + type = with types; attrsOf (either lines (submodule ({ name, ... }: { + options = { + enabled = mkEnableOption "this jail." // { + default = true; + readOnly = name == "DEFAULT"; + }; + + filter = mkOption { + type = nullOr (either str configFormat.type); + + default = null; + description = lib.mdDoc "Content of the filter used for this jail."; + }; + + settings = mkOption { + inherit (settingsFormat) type; + + default = { }; + description = lib.mdDoc "Additional settings for this jail."; + }; + }; + }))); description = lib.mdDoc '' The configuration of each Fail2ban “jail”. A jail consists of an action (such as blocking a port using @@ -278,7 +317,7 @@ in config = mkIf cfg.enable { assertions = [ { - assertion = (cfg.bantime-increment.formula == null || cfg.bantime-increment.multipliers == null); + assertion = cfg.bantime-increment.formula == null || cfg.bantime-increment.multipliers == null; message = '' Options `services.fail2ban.bantime-increment.formula` and `services.fail2ban.bantime-increment.multipliers` cannot be both specified. ''; @@ -300,7 +339,7 @@ in "fail2ban/paths-nixos.conf".source = pathsConf; "fail2ban/action.d".source = "${cfg.package}/etc/fail2ban/action.d/*.conf"; "fail2ban/filter.d".source = "${cfg.package}/etc/fail2ban/filter.d/*.conf"; - }; + } // (mapAttrs' mkFilter (filterAttrs (_: v: v.filter != null && !builtins.isString v.filter) attrsJails)); systemd.packages = [ cfg.package ]; systemd.services.fail2ban = { @@ -335,39 +374,41 @@ in }; }; + # Defaults for the daemon settings + services.fail2ban.daemonSettings.Definition = { + logtarget = mkDefault "SYSLOG"; + socket = mkDefault "/run/fail2ban/fail2ban.sock"; + pidfile = mkDefault "/run/fail2ban/fail2ban.pid"; + dbfile = mkDefault "/var/lib/fail2ban/fail2ban.sqlite3"; + }; + # Add some reasonable default jails. The special "DEFAULT" jail # sets default values for all other jails. - services.fail2ban.jails.DEFAULT = '' - # Bantime increment options - bantime.increment = ${boolToString cfg.bantime-increment.enable} - ${optionalString (cfg.bantime-increment.rndtime != null) "bantime.rndtime = ${cfg.bantime-increment.rndtime}"} - ${optionalString (cfg.bantime-increment.maxtime != null) "bantime.maxtime = ${cfg.bantime-increment.maxtime}"} - ${optionalString (cfg.bantime-increment.factor != null) "bantime.factor = ${cfg.bantime-increment.factor}"} - ${optionalString (cfg.bantime-increment.formula != null) "bantime.formula = ${cfg.bantime-increment.formula}"} - ${optionalString (cfg.bantime-increment.multipliers != null) "bantime.multipliers = ${cfg.bantime-increment.multipliers}"} - ${optionalString (cfg.bantime-increment.overalljails != null) "bantime.overalljails = ${boolToString cfg.bantime-increment.overalljails}"} - # Miscellaneous options - ignoreip = 127.0.0.1/8 ${optionalString config.networking.enableIPv6 "::1"} ${concatStringsSep " " cfg.ignoreIP} - ${optionalString (cfg.bantime != null) '' - bantime = ${cfg.bantime} - ''} - maxretry = ${toString cfg.maxretry} - backend = systemd - # Actions - banaction = ${cfg.banaction} - banaction_allports = ${cfg.banaction-allports} - ${optionalString (cfg.extraSettings != {}) '' - # Extra settings - ${generators.toKeyValue {} cfg.extraSettings} - ''} - ''; - # Block SSH if there are too many failing connection attempts. + services.fail2ban.jails = mkMerge [ + { + DEFAULT.settings = (optionalAttrs cfg.bantime-increment.enable + ({ "bantime.increment" = cfg.bantime-increment.enable; } // (mapAttrs' + (name: nameValuePair "bantime.${name}") + (filterAttrs (n: v: v != null && n != "enable") cfg.bantime-increment)) + ) + ) // { + # Miscellaneous options + inherit (cfg) banaction maxretry; + ignoreip = ''127.0.0.1/8 ${optionalString config.networking.enableIPv6 "::1"} ${concatStringsSep " " cfg.ignoreIP}''; + backend = "systemd"; + # Actions + banaction_allports = cfg.banaction-allports; + }; + } + + # Block SSH if there are too many failing connection attempts. + (mkIf config.services.openssh.enable { + sshd.settings.port = mkDefault (concatMapStringsSep "," builtins.toString config.services.openssh.ports); + }) + ]; + # Benefits from verbose sshd logging to observe failed login attempts, # so we set that here unless the user overrode it. - services.openssh.settings.LogLevel = lib.mkDefault "VERBOSE"; - services.fail2ban.jails.sshd = mkDefault '' - enabled = true - port = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports} - ''; + services.openssh.settings.LogLevel = mkDefault "VERBOSE"; }; } diff --git a/nixos/modules/services/security/usbguard.nix b/nixos/modules/services/security/usbguard.nix index 1d846b19407..651f5255ac8 100644 --- a/nixos/modules/services/security/usbguard.nix +++ b/nixos/modules/services/security/usbguard.nix @@ -150,6 +150,8 @@ in Generate device specific rules including the "via-port" attribute. ''; }; + + dbus.enable = mkEnableOption (lib.mdDoc "USBGuard dbus daemon"); }; }; @@ -160,49 +162,90 @@ in environment.systemPackages = [ cfg.package ]; - systemd.services.usbguard = { - description = "USBGuard daemon"; - - wantedBy = [ "basic.target" ]; - wants = [ "systemd-udevd.service" ]; - - # make sure an empty rule file exists - preStart = ''[ -f "${ruleFile}" ] || touch ${ruleFile}''; - - serviceConfig = { - Type = "simple"; - ExecStart = "${cfg.package}/bin/usbguard-daemon -P -k -c ${daemonConfFile}"; - Restart = "on-failure"; - - StateDirectory = [ - "usbguard" - "usbguard/IPCAccessControl.d" - ]; - - AmbientCapabilities = ""; - CapabilityBoundingSet = "CAP_CHOWN CAP_FOWNER"; - DeviceAllow = "/dev/null rw"; - DevicePolicy = "strict"; - IPAddressDeny = "any"; - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateDevices = true; - PrivateTmp = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectKernelModules = true; - ProtectSystem = true; - ReadOnlyPaths = "-/"; - ReadWritePaths = "-/dev/shm -/tmp"; - RestrictAddressFamilies = [ "AF_UNIX" "AF_NETLINK" ]; - RestrictNamespaces = true; - RestrictRealtime = true; - SystemCallArchitectures = "native"; - SystemCallFilter = "@system-service"; - UMask = "0077"; + systemd.services = { + usbguard = { + description = "USBGuard daemon"; + + wantedBy = [ "basic.target" ]; + wants = [ "systemd-udevd.service" ]; + + # make sure an empty rule file exists + preStart = ''[ -f "${ruleFile}" ] || touch ${ruleFile}''; + + serviceConfig = { + Type = "simple"; + ExecStart = "${cfg.package}/bin/usbguard-daemon -P -k -c ${daemonConfFile}"; + Restart = "on-failure"; + + StateDirectory = [ + "usbguard" + "usbguard/IPCAccessControl.d" + ]; + + AmbientCapabilities = ""; + CapabilityBoundingSet = "CAP_CHOWN CAP_FOWNER"; + DeviceAllow = "/dev/null rw"; + DevicePolicy = "strict"; + IPAddressDeny = "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelModules = true; + ProtectSystem = true; + ReadOnlyPaths = "-/"; + ReadWritePaths = "-/dev/shm -/tmp"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_NETLINK" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + UMask = "0077"; + }; + }; + + usbguard-dbus = mkIf cfg.dbus.enable { + description = "USBGuard D-Bus Service"; + + wantedBy = [ "multi-user.target" ]; + requires = [ "usbguard.service" ]; + + serviceConfig = { + Type = "dbus"; + BusName = "org.usbguard1"; + ExecStart = "${cfg.package}/bin/usbguard-dbus --system"; + Restart = "on-failure"; + }; + + aliases = [ "dbus-org.usbguard.service" ]; }; }; + + security.polkit.extraConfig = + let + groupCheck = (lib.concatStrings (map + (g: "subject.isInGroup(\"${g}\") || ") + cfg.IPCAllowedGroups)) + + "false"; + in + optionalString cfg.dbus.enable '' + polkit.addRule(function(action, subject) { + if ((action.id == "org.usbguard.Policy1.listRules" || + action.id == "org.usbguard.Policy1.appendRule" || + action.id == "org.usbguard.Policy1.removeRule" || + action.id == "org.usbguard.Devices1.applyDevicePolicy" || + action.id == "org.usbguard.Devices1.listDevices" || + action.id == "org.usbguard1.getParameter" || + action.id == "org.usbguard1.setParameter") && + subject.active == true && subject.local == true && + (${groupCheck})) { + return polkit.Result.YES; + } + }); + ''; }; imports = [ (mkRemovedOptionModule [ "services" "usbguard" "ruleFile" ] "The usbguard module now uses ${defaultRuleFile} as ruleFile. Alternatively, use services.usbguard.rules to configure rules.") diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix index aaa3f5507f7..98ab8595bdd 100644 --- a/nixos/modules/services/security/vaultwarden/default.nix +++ b/nixos/modules/services/security/vaultwarden/default.nix @@ -59,7 +59,12 @@ in { config = mkOption { type = attrsOf (nullOr (oneOf [ bool int str ])); - default = {}; + default = { + config = { + ROCKET_ADDRESS = "::1"; # default to localhost + ROCKET_PORT = 8222; + }; + }; example = literalExpression '' { DOMAIN = "https://bitwarden.example.com"; diff --git a/nixos/modules/services/system/cloud-init.nix b/nixos/modules/services/system/cloud-init.nix index 4cda06ed424..d782bb1a366 100644 --- a/nixos/modules/services/system/cloud-init.nix +++ b/nixos/modules/services/system/cloud-init.nix @@ -15,6 +15,7 @@ let ] ++ optional cfg.btrfs.enable btrfs-progs ++ optional cfg.ext4.enable e2fsprogs + ++ optional cfg.xfs.enable xfsprogs ; settingsFormat = pkgs.formats.yaml { }; cfgfile = settingsFormat.generate "cloud.cfg" cfg.settings; @@ -57,6 +58,14 @@ in ''; }; + xfs.enable = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Allow the cloud-init service to operate `xfs` filesystem. + ''; + }; + network.enable = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix index 9d8a62ec78c..8d5b25e6176 100644 --- a/nixos/modules/services/system/dbus.nix +++ b/nixos/modules/services/system/dbus.nix @@ -22,7 +22,7 @@ in options = { boot.initrd.systemd.dbus = { - enable = mkEnableOption (lib.mdDoc "dbus in stage 1") // { visible = false; }; + enable = mkEnableOption (lib.mdDoc "dbus in stage 1"); }; services.dbus = { diff --git a/nixos/modules/services/web-apps/anuko-time-tracker.nix b/nixos/modules/services/web-apps/anuko-time-tracker.nix index c50a0328e34..f43cbc40ec7 100644 --- a/nixos/modules/services/web-apps/anuko-time-tracker.nix +++ b/nixos/modules/services/web-apps/anuko-time-tracker.nix @@ -42,13 +42,15 @@ let mkdir -p $out cp -r * $out/ + # Link config file ln -s ${configFile} $out/WEB-INF/config.php # Link writable templates_c directory rm -rf $out/WEB-INF/templates_c ln -s ${cfg.dataDir}/templates_c $out/WEB-INF/templates_c - # ln -fs ${cfg.dataDir}/templates_c $out/WEB-INF/templates_c + # Remove unsafe dbinstall.php + rm -f $out/dbinstall.php ''; }; in @@ -105,6 +107,41 @@ in ''; }; + hostname = lib.mkOption { + type = lib.types.str; + default = + if config.networking.domain != null + then config.networking.fqdn + else config.networking.hostName; + defaultText = lib.literalExpression "config.networking.fqdn"; + example = "anuko.example.com"; + description = lib.mdDoc '' + The hostname to serve Anuko Time Tracker on. + ''; + }; + + nginx = lib.mkOption { + type = lib.types.submodule ( + lib.recursiveUpdate + (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {} + ); + default = {}; + example = lib.literalExpression '' + { + serverAliases = [ + "anuko.''${config.networking.domain}" + ]; + + # To enable encryption and let let's encrypt take care of certificate + forceSSL = true; + enableACME = true; + } + ''; + description = lib.mdDoc '' + With this option, you can customize the Nginx virtualHost settings. + ''; + }; + dataDir = lib.mkOption { type = lib.types.str; default = "/var/lib/anuko-time-tracker"; @@ -118,15 +155,6 @@ in description = lib.mdDoc "User under which Anuko Time Tracker runs."; }; - virtualHost = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = "localhost"; - description = lib.mdDoc '' - Name of the nginx virtualhost to use and setup. If null, do not setup - any virtualhost. - ''; - }; - settings = { multiorgMode = lib.mkOption { type = lib.types.bool; @@ -286,20 +314,26 @@ in }; }; - services.nginx = lib.mkIf (cfg.virtualHost != null) { - enable = true; - virtualHosts = { - "${cfg.virtualHost}" = { + services.nginx = { + enable = lib.mkDefault true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + virtualHosts."${cfg.hostname}" = lib.mkMerge [ + cfg.nginx + { root = lib.mkForce "${package}"; - locations."/".index = "index.php"; - locations."~ [^/]\\.php(/|$)" = { - extraConfig = '' - fastcgi_split_path_info ^(.+?\.php)(/.*)$; - fastcgi_pass unix:${config.services.phpfpm.pools.anuko-time-tracker.socket}; - ''; + locations = { + "/".index = "index.php"; + "~ [^/]\\.php(/|$)" = { + extraConfig = '' + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_pass unix:${config.services.phpfpm.pools.anuko-time-tracker.socket}; + ''; + }; }; - }; - }; + } + ]; }; services.mysql = lib.mkIf cfg.database.createLocally { diff --git a/nixos/modules/services/web-apps/lemmy.nix b/nixos/modules/services/web-apps/lemmy.nix index dd335302fa4..afbd7497610 100644 --- a/nixos/modules/services/web-apps/lemmy.nix +++ b/nixos/modules/services/web-apps/lemmy.nix @@ -77,6 +77,11 @@ in }; }; + secretFile = mkOption { + type = with types; nullOr path; + default = null; + description = lib.mdDoc "Path to a secret JSON configuration file which is merged at runtime with the one generated from {option}`services.lemmy.settings`."; + }; }; config = @@ -197,11 +202,14 @@ in } ]; - systemd.services.lemmy = { + systemd.services.lemmy = let + configFile = settingsFormat.generate "config.hjson" cfg.settings; + mergedConfig = "/run/lemmy/config.hjson"; + in { description = "Lemmy server"; environment = { - LEMMY_CONFIG_LOCATION = "${settingsFormat.generate "config.hjson" cfg.settings}"; + LEMMY_CONFIG_LOCATION = if cfg.secretFile == null then configFile else mergedConfig; LEMMY_DATABASE_URL = if cfg.database.uri != null then cfg.database.uri else (mkIf (cfg.database.createLocally) "postgres:///lemmy?host=/run/postgresql&user=lemmy"); }; @@ -216,10 +224,24 @@ in requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ]; + path = mkIf (cfg.secretFile != null) [ pkgs.jq ]; + + # merge the two configs and prevent others from reading the result + # if somehow $CREDENTIALS_DIRECTORY is not set we fail + preStart = mkIf (cfg.secretFile != null) '' + set -u + umask 177 + jq --slurp '.[0] * .[1]' ${lib.escapeShellArg configFile} "$CREDENTIALS_DIRECTORY/secretFile" > ${lib.escapeShellArg mergedConfig} + ''; + serviceConfig = { DynamicUser = true; RuntimeDirectory = "lemmy"; ExecStart = "${cfg.server.package}/bin/lemmy_server"; + LoadCredential = mkIf (cfg.secretFile != null) "secretFile:${toString cfg.secretFile}"; + PrivateTmp = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; }; }; diff --git a/nixos/modules/services/web-apps/snipe-it.nix b/nixos/modules/services/web-apps/snipe-it.nix index 93b0aafab64..e861a418519 100644 --- a/nixos/modules/services/web-apps/snipe-it.nix +++ b/nixos/modules/services/web-apps/snipe-it.nix @@ -15,6 +15,8 @@ let tlsEnabled = cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME; + inherit (snipe-it.passthru) phpPackage; + # shell script for local administration artisan = pkgs.writeScriptBin "snipe-it" '' #! ${pkgs.runtimeShell} @@ -23,7 +25,7 @@ let if [[ "$USER" != ${user} ]]; then sudo='exec /run/wrappers/bin/sudo -u ${user}' fi - $sudo ${pkgs.php}/bin/php artisan $* + $sudo ${phpPackage}/bin/php artisan $* ''; in { options.services.snipe-it = { @@ -340,8 +342,7 @@ in { }; services.phpfpm.pools.snipe-it = { - inherit user group; - phpPackage = pkgs.php81; + inherit user group phpPackage; phpOptions = '' post_max_size = ${cfg.maxUploadSize} upload_max_filesize = ${cfg.maxUploadSize} @@ -450,7 +451,7 @@ in { rm "${cfg.dataDir}"/bootstrap/cache/*.php || true # migrate db - ${pkgs.php}/bin/php artisan migrate --force + ${phpPackage}/bin/php artisan migrate --force # A placeholder file for invalid barcodes invalid_barcode_location="${cfg.dataDir}/public/uploads/barcodes/invalid_barcode.gif" diff --git a/nixos/modules/services/web-servers/caddy/default.nix b/nixos/modules/services/web-servers/caddy/default.nix index 70715a23725..5cc9ef6dd6d 100644 --- a/nixos/modules/services/web-servers/caddy/default.nix +++ b/nixos/modules/services/web-servers/caddy/default.nix @@ -41,6 +41,10 @@ let in "${if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then Caddyfile-formatted else Caddyfile}/Caddyfile"; + etcConfigFile = "caddy/caddy_config"; + + configPath = "/etc/${etcConfigFile}"; + acmeHosts = unique (catAttrs "useACMEHost" acmeVHosts); mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix; @@ -155,11 +159,16 @@ in description = lib.mdDoc '' Override the configuration file used by Caddy. By default, NixOS generates one automatically. + + The configuration file is exposed at {file}`${configPath}`. ''; }; adapter = mkOption { - default = null; + default = if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null; + defaultText = literalExpression '' + if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null + ''; example = literalExpression "nginx"; type = with types; nullOr str; description = lib.mdDoc '' @@ -275,6 +284,21 @@ in ''; }; + enableReload = mkOption { + default = true; + type = types.bool; + description = lib.mdDoc '' + Reload Caddy instead of restarting it when configuration file changes. + + Note that enabling this option requires the [admin API](https://caddyserver.com/docs/caddyfile/options#admin) + to not be turned off. + + If you enable this option, consider setting [`grace_period`](https://caddyserver.com/docs/caddyfile/options#grace-period) + to a non-infinite value in {option}`services.caddy.globalConfig` + to prevent Caddy waiting for active connections to finish, + which could delay the reload essentially indefinitely. + ''; + }; }; # implementation @@ -311,13 +335,16 @@ in wantedBy = [ "multi-user.target" ]; startLimitIntervalSec = 14400; startLimitBurst = 10; + reloadTriggers = optional cfg.enableReload cfg.configFile; - serviceConfig = { + serviceConfig = let + runOptions = ''--config ${configPath} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}''; + in { # https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= # If the empty string is assigned to this option, the list of commands to start is reset, prior assignments of this option will have no effect. - ExecStart = [ "" ''${cfg.package}/bin/caddy run --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"} ${optionalString cfg.resume "--resume"}'' ]; - ExecReload = [ "" ''${cfg.package}/bin/caddy reload --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"} --force'' ]; - ExecStartPre = ''${cfg.package}/bin/caddy validate --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}''; + ExecStart = [ "" ''${cfg.package}/bin/caddy run ${runOptions} ${optionalString cfg.resume "--resume"}'' ]; + # Validating the configuration before applying it ensures we’ll get a proper error that will be reported when switching to the configuration + ExecReload = [ "" ''${cfg.package}/bin/caddy reload ${runOptions} --force'' ]; User = cfg.user; Group = cfg.group; ReadWriteDirectories = cfg.dataDir; @@ -353,5 +380,6 @@ in in listToAttrs certCfg; + environment.etc.${etcConfigFile}.source = cfg.configFile; }; } diff --git a/nixos/modules/services/web-servers/static-web-server.nix b/nixos/modules/services/web-servers/static-web-server.nix new file mode 100644 index 00000000000..07187f00fec --- /dev/null +++ b/nixos/modules/services/web-servers/static-web-server.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.static-web-server; + toml = pkgs.formats.toml {}; + configFilePath = toml.generate "config.toml" cfg.configuration; +in { + options = { + services.static-web-server = { + enable = lib.mkEnableOption (lib.mdDoc ''Static Web Server''); + listen = lib.mkOption { + default = "[::]:8787"; + type = lib.types.str; + description = lib.mdDoc '' + The "ListenStream" used in static-web-server.socket. + This is equivalent to SWS's "host" and "port" options. + See here for specific syntax: <https://www.freedesktop.org/software/systemd/man/systemd.socket.html#ListenStream=> + ''; + }; + root = lib.mkOption { + type = lib.types.path; + description = lib.mdDoc '' + The location of files for SWS to serve. Equivalent to SWS's "root" config value. + NOTE: This folder must exist before starting SWS. + ''; + }; + configuration = lib.mkOption { + default = { }; + type = toml.type; + example = { + general = { log-level = "error"; directory-listing = true; }; + }; + description = lib.mdDoc '' + Configuration for Static Web Server. See + <https://static-web-server.net/configuration/config-file/>. + NOTE: Don't set "host", "port", or "root" here. They will be ignored. + Use the top-level "listen" and "root" options instead. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ pkgs.static-web-server ]; + systemd.packages = [ pkgs.static-web-server ]; + # Have to set wantedBy since systemd.packages ignores the "Install" section + systemd.sockets.static-web-server = { + wantedBy = [ "sockets.target" ]; + # Start with empty string to reset upstream option + listenStreams = [ "" cfg.listen ]; + }; + systemd.services.static-web-server = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + # Remove upstream sample environment file; use config.toml exclusively + EnvironmentFile = [ "" ]; + ExecStart = [ "" "${pkgs.static-web-server}/bin/static-web-server --fd 0 --config-file ${configFilePath} --root ${cfg.root}" ]; + # Supplementary groups doesn't work unless we create the group ourselves + SupplementaryGroups = [ "" ]; + # If the user is serving files from their home dir, override ProtectHome to allow that + ProtectHome = if lib.hasPrefix "/home" cfg.root then "tmpfs" else "true"; + BindReadOnlyPaths = cfg.root; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ mac-chaffee ]; +} diff --git a/nixos/modules/services/web-servers/ttyd.nix b/nixos/modules/services/web-servers/ttyd.nix index e0a8b5179e0..3b1d87ccb48 100644 --- a/nixos/modules/services/web-servers/ttyd.nix +++ b/nixos/modules/services/web-servers/ttyd.nix @@ -78,11 +78,12 @@ in clientOptions = mkOption { type = types.attrsOf types.str; default = {}; - example = literalExpression ''{ - fontSize = "16"; - fontFamily = "Fira Code"; - - }''; + example = literalExpression '' + { + fontSize = "16"; + fontFamily = "Fira Code"; + } + ''; description = lib.mdDoc '' Attribute set of client options for xtermjs. <https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/> diff --git a/nixos/modules/services/x11/desktop-managers/cde.nix b/nixos/modules/services/x11/desktop-managers/cde.nix index e0b4fb0e7bf..ad4b5d27f9d 100644 --- a/nixos/modules/services/x11/desktop-managers/cde.nix +++ b/nixos/modules/services/x11/desktop-managers/cde.nix @@ -36,7 +36,7 @@ in { name = "cmsd"; protocol = "udp"; user = "root"; - server = "${pkgs.cdesktopenv}/opt/dt/bin/rpc.cmsd"; + server = "${pkgs.cdesktopenv}/bin/rpc.cmsd"; extraConfig = '' type = RPC UNLISTED rpc_number = 100068 @@ -64,7 +64,7 @@ in { services.xserver.desktopManager.session = [ { name = "CDE"; start = '' - exec ${pkgs.cdesktopenv}/opt/dt/bin/Xsession + exec ${pkgs.cdesktopenv}/bin/Xsession ''; }]; }; diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix index 0db4cca520f..e87ae5ae812 100644 --- a/nixos/modules/services/x11/desktop-managers/pantheon.nix +++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix @@ -315,7 +315,6 @@ in environment.systemPackages = with pkgs.pantheon; [ contractor file-roller-contract - gnome-bluetooth-contract ]; environment.pathsToLink = [ diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix index f8f82bda3fa..676d08b93e2 100644 --- a/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -207,7 +207,9 @@ in # conflicts display-manager.service, then when nixos-rebuild # switch starts multi-user.target, display-manager.service is # stopped so plymouth-quit.service can be started.) - systemd.services.plymouth-quit.wantedBy = lib.mkForce []; + systemd.services.plymouth-quit = mkIf config.boot.plymouth.enable { + wantedBy = lib.mkForce []; + }; systemd.services.display-manager.serviceConfig = { # Restart = "always"; - already defined in xserver.nix diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 6d2321be8ef..53d9c99b47d 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -725,7 +725,7 @@ in systemd.defaultUnit = mkIf cfg.autorun "graphical.target"; systemd.services.display-manager = - { description = "X11 Server"; + { description = "Display Manager"; after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ]; diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index d88f88f9fda..ad8c609e150 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -2,6 +2,7 @@ with utils.systemdUtils.unitOptions; with utils.systemdUtils.lib; +with utils.systemdUtils.network.units; with lib; let @@ -1897,7 +1898,7 @@ let bridgeVLANOptions = { options = { - bridgeMDBConfig = mkOption { + bridgeVLANConfig = mkOption { default = {}; example = { VLAN = 20; }; type = types.addCheck (types.attrsOf unitOption) check.network.sectionBridgeVLAN; @@ -2388,17 +2389,6 @@ let ''; }; - bridgeVLANConfig = mkOption { - default = {}; - example = { VLAN = "10-20"; }; - type = types.addCheck (types.attrsOf unitOption) check.network.sectionBridgeVLAN; - description = lib.mdDoc '' - Each attribute in this set specifies an option in the - `[BridgeVLAN]` section of the unit. See - {manpage}`systemd.network(5)` for details. - ''; - }; - bridgeVLANs = mkOption { default = []; example = [ { bridgeVLANConfig = { VLAN = "10-20"; }; } ]; @@ -2615,95 +2605,6 @@ let }; }; - commonMatchText = def: optionalString (def.matchConfig != { }) '' - [Match] - ${attrsToSection def.matchConfig} - ''; - - linkToUnit = name: def: - { inherit (def) enable; - text = commonMatchText def - + '' - [Link] - ${attrsToSection def.linkConfig} - '' - + def.extraConfig; - }; - - netdevToUnit = name: def: - { inherit (def) enable; - text = commonMatchText def - + '' - [NetDev] - ${attrsToSection def.netdevConfig} - '' - + optionalString (def.vlanConfig != { }) '' - [VLAN] - ${attrsToSection def.vlanConfig} - '' - + optionalString (def.macvlanConfig != { }) '' - [MACVLAN] - ${attrsToSection def.macvlanConfig} - '' - + optionalString (def.vxlanConfig != { }) '' - [VXLAN] - ${attrsToSection def.vxlanConfig} - '' - + optionalString (def.tunnelConfig != { }) '' - [Tunnel] - ${attrsToSection def.tunnelConfig} - '' - + optionalString (def.fooOverUDPConfig != { }) '' - [FooOverUDP] - ${attrsToSection def.fooOverUDPConfig} - '' - + optionalString (def.peerConfig != { }) '' - [Peer] - ${attrsToSection def.peerConfig} - '' - + optionalString (def.tunConfig != { }) '' - [Tun] - ${attrsToSection def.tunConfig} - '' - + optionalString (def.tapConfig != { }) '' - [Tap] - ${attrsToSection def.tapConfig} - '' - + optionalString (def.l2tpConfig != { }) '' - [L2TP] - ${attrsToSection def.l2tpConfig} - '' - + flip concatMapStrings def.l2tpSessions (x: '' - [L2TPSession] - ${attrsToSection x.l2tpSessionConfig} - '') - + optionalString (def.wireguardConfig != { }) '' - [WireGuard] - ${attrsToSection def.wireguardConfig} - '' - + flip concatMapStrings def.wireguardPeers (x: '' - [WireGuardPeer] - ${attrsToSection x.wireguardPeerConfig} - '') - + optionalString (def.bondConfig != { }) '' - [Bond] - ${attrsToSection def.bondConfig} - '' - + optionalString (def.xfrmConfig != { }) '' - [Xfrm] - ${attrsToSection def.xfrmConfig} - '' - + optionalString (def.vrfConfig != { }) '' - [VRF] - ${attrsToSection def.vrfConfig} - '' - + optionalString (def.batmanAdvancedConfig != { }) '' - [BatmanAdvanced] - ${attrsToSection def.batmanAdvancedConfig} - '' - + def.extraConfig; - }; - renderConfig = def: { text = '' [Network] @@ -2718,235 +2619,6 @@ let ${attrsToSection def.dhcpV6Config} ''; }; - networkToUnit = name: def: - { inherit (def) enable; - text = commonMatchText def - + optionalString (def.linkConfig != { }) '' - [Link] - ${attrsToSection def.linkConfig} - '' - + '' - [Network] - '' - + attrsToSection def.networkConfig - + optionalString (def.address != [ ]) '' - ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)} - '' - + optionalString (def.gateway != [ ]) '' - ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)} - '' - + optionalString (def.dns != [ ]) '' - ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)} - '' - + optionalString (def.ntp != [ ]) '' - ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)} - '' - + optionalString (def.bridge != [ ]) '' - ${concatStringsSep "\n" (map (s: "Bridge=${s}") def.bridge)} - '' - + optionalString (def.bond != [ ]) '' - ${concatStringsSep "\n" (map (s: "Bond=${s}") def.bond)} - '' - + optionalString (def.vrf != [ ]) '' - ${concatStringsSep "\n" (map (s: "VRF=${s}") def.vrf)} - '' - + optionalString (def.vlan != [ ]) '' - ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)} - '' - + optionalString (def.macvlan != [ ]) '' - ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)} - '' - + optionalString (def.vxlan != [ ]) '' - ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)} - '' - + optionalString (def.tunnel != [ ]) '' - ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)} - '' - + optionalString (def.xfrm != [ ]) '' - ${concatStringsSep "\n" (map (s: "Xfrm=${s}") def.xfrm)} - '' - + '' - - '' - + flip concatMapStrings def.addresses (x: '' - [Address] - ${attrsToSection x.addressConfig} - '') - + flip concatMapStrings def.routingPolicyRules (x: '' - [RoutingPolicyRule] - ${attrsToSection x.routingPolicyRuleConfig} - '') - + flip concatMapStrings def.routes (x: '' - [Route] - ${attrsToSection x.routeConfig} - '') - + optionalString (def.dhcpV4Config != { }) '' - [DHCPv4] - ${attrsToSection def.dhcpV4Config} - '' - + optionalString (def.dhcpV6Config != { }) '' - [DHCPv6] - ${attrsToSection def.dhcpV6Config} - '' - + optionalString (def.dhcpPrefixDelegationConfig != { }) '' - [DHCPPrefixDelegation] - ${attrsToSection def.dhcpPrefixDelegationConfig} - '' - + optionalString (def.ipv6AcceptRAConfig != { }) '' - [IPv6AcceptRA] - ${attrsToSection def.ipv6AcceptRAConfig} - '' - + optionalString (def.dhcpServerConfig != { }) '' - [DHCPServer] - ${attrsToSection def.dhcpServerConfig} - '' - + optionalString (def.ipv6SendRAConfig != { }) '' - [IPv6SendRA] - ${attrsToSection def.ipv6SendRAConfig} - '' - + flip concatMapStrings def.ipv6Prefixes (x: '' - [IPv6Prefix] - ${attrsToSection x.ipv6PrefixConfig} - '') - + flip concatMapStrings def.ipv6RoutePrefixes (x: '' - [IPv6RoutePrefix] - ${attrsToSection x.ipv6RoutePrefixConfig} - '') - + flip concatMapStrings def.dhcpServerStaticLeases (x: '' - [DHCPServerStaticLease] - ${attrsToSection x.dhcpServerStaticLeaseConfig} - '') - + optionalString (def.bridgeConfig != { }) '' - [Bridge] - ${attrsToSection def.bridgeConfig} - '' - + flip concatMapStrings def.bridgeFDBs (x: '' - [BridgeFDB] - ${attrsToSection x.bridgeFDBConfig} - '') - + flip concatMapStrings def.bridgeMDBs (x: '' - [BridgeMDB] - ${attrsToSection x.bridgeMDBConfig} - '') - + optionalString (def.lldpConfig != { }) '' - [LLDP] - ${attrsToSection def.lldpConfig} - '' - + optionalString (def.canConfig != { }) '' - [CAN] - ${attrsToSection def.canConfig} - '' - + optionalString (def.ipoIBConfig != { }) '' - [IPoIB] - ${attrsToSection def.ipoIBConfig} - '' - + optionalString (def.qdiscConfig != { }) '' - [QDisc] - ${attrsToSection def.qdiscConfig} - '' - + optionalString (def.networkEmulatorConfig != { }) '' - [NetworkEmulator] - ${attrsToSection def.networkEmulatorConfig} - '' - + optionalString (def.tokenBucketFilterConfig != { }) '' - [TokenBucketFilter] - ${attrsToSection def.tokenBucketFilterConfig} - '' - + optionalString (def.pieConfig != { }) '' - [PIE] - ${attrsToSection def.pieConfig} - '' - + optionalString (def.flowQueuePIEConfig != { }) '' - [FlowQueuePIE] - ${attrsToSection def.flowQueuePIEConfig} - '' - + optionalString (def.stochasticFairBlueConfig != { }) '' - [StochasticFairBlue] - ${attrsToSection def.stochasticFairBlueConfig} - '' - + optionalString (def.stochasticFairnessQueueingConfig != { }) '' - [StochasticFairnessQueueing] - ${attrsToSection def.stochasticFairnessQueueingConfig} - '' - + optionalString (def.bfifoConfig != { }) '' - [BFIFO] - ${attrsToSection def.bfifoConfig} - '' - + optionalString (def.pfifoConfig != { }) '' - [PFIFO] - ${attrsToSection def.pfifoConfig} - '' - + optionalString (def.pfifoHeadDropConfig != { }) '' - [PFIFOHeadDrop] - ${attrsToSection def.pfifoHeadDropConfig} - '' - + optionalString (def.pfifoFastConfig != { }) '' - [PFIFOFast] - ${attrsToSection def.pfifoFastConfig} - '' - + optionalString (def.cakeConfig != { }) '' - [CAKE] - ${attrsToSection def.cakeConfig} - '' - + optionalString (def.controlledDelayConfig != { }) '' - [ControlledDelay] - ${attrsToSection def.controlledDelayConfig} - '' - + optionalString (def.deficitRoundRobinSchedulerConfig != { }) '' - [DeficitRoundRobinScheduler] - ${attrsToSection def.deficitRoundRobinSchedulerConfig} - '' - + optionalString (def.deficitRoundRobinSchedulerClassConfig != { }) '' - [DeficitRoundRobinSchedulerClass] - ${attrsToSection def.deficitRoundRobinSchedulerClassConfig} - '' - + optionalString (def.enhancedTransmissionSelectionConfig != { }) '' - [EnhancedTransmissionSelection] - ${attrsToSection def.enhancedTransmissionSelectionConfig} - '' - + optionalString (def.genericRandomEarlyDetectionConfig != { }) '' - [GenericRandomEarlyDetection] - ${attrsToSection def.genericRandomEarlyDetectionConfig} - '' - + optionalString (def.fairQueueingControlledDelayConfig != { }) '' - [FairQueueingControlledDelay] - ${attrsToSection def.fairQueueingControlledDelayConfig} - '' - + optionalString (def.fairQueueingConfig != { }) '' - [FairQueueing] - ${attrsToSection def.fairQueueingConfig} - '' - + optionalString (def.trivialLinkEqualizerConfig != { }) '' - [TrivialLinkEqualizer] - ${attrsToSection def.trivialLinkEqualizerConfig} - '' - + optionalString (def.hierarchyTokenBucketConfig != { }) '' - [HierarchyTokenBucket] - ${attrsToSection def.hierarchyTokenBucketConfig} - '' - + optionalString (def.hierarchyTokenBucketClassConfig != { }) '' - [HierarchyTokenBucketClass] - ${attrsToSection def.hierarchyTokenBucketClassConfig} - '' - + optionalString (def.heavyHitterFilterConfig != { }) '' - [HeavyHitterFilter] - ${attrsToSection def.heavyHitterFilterConfig} - '' - + optionalString (def.quickFairQueueingConfig != { }) '' - [QuickFairQueueing] - ${attrsToSection def.quickFairQueueingConfig} - '' - + optionalString (def.quickFairQueueingConfigClass != { }) '' - [QuickFairQueueingClass] - ${attrsToSection def.quickFairQueueingConfigClass} - '' - + flip concatMapStrings def.bridgeVLANs (x: '' - [BridgeVLAN] - ${attrsToSection x.bridgeVLANConfig} - '') - + def.extraConfig; - }; - mkUnitFiles = prefix: cfg: listToAttrs (map (name: { name = "${prefix}systemd/network/${name}"; value.source = "${cfg.units.${name}.unit}/${name}"; @@ -3059,11 +2731,14 @@ let }; - commonConfig = config: let cfg = config.systemd.network; in mkMerge [ + commonConfig = config: let + cfg = config.systemd.network; + mkUnit = f: def: { inherit (def) enable; text = f def; }; + in mkMerge [ # .link units are honored by udev, no matter if systemd-networkd is enabled or not. { - systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links; + systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (mkUnit linkToUnit v)) cfg.links; systemd.network.wait-online.extraArgs = [ "--timeout=${toString cfg.wait-online.timeout}" ] @@ -3073,8 +2748,8 @@ let (mkIf config.systemd.network.enable { - systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs - // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks; + systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (mkUnit netdevToUnit v)) cfg.netdevs + // mapAttrs' (n: v: nameValuePair "${n}.network" (mkUnit networkToUnit v)) cfg.networks; # systemd-networkd is socket-activated by kernel netlink route change # messages. It is important to have systemd buffer those on behalf of @@ -3165,7 +2840,7 @@ let (mkIf cfg.enable { - systemd.package = pkgs.systemdStage1Network; + systemd.package = mkDefault pkgs.systemdStage1Network; # For networkctl systemd.dbus.enable = mkDefault true; diff --git a/nixos/modules/tasks/bcache.nix b/nixos/modules/tasks/bcache.nix index 408ddc02373..35b922dc8a1 100644 --- a/nixos/modules/tasks/bcache.nix +++ b/nixos/modules/tasks/bcache.nix @@ -1,8 +1,12 @@ { config, lib, pkgs, ... }: { - options.boot.initrd.services.bcache.enable = (lib.mkEnableOption (lib.mdDoc "bcache support in the initrd")) // { - visible = false; # only works with systemd stage 1 + options.boot.initrd.services.bcache.enable = lib.mkEnableOption (lib.mdDoc "bcache support in the initrd") // { + description = lib.mdDoc '' + *This will only be used when systemd is used in stage 1.* + + Whether to enable bcache support in the initrd. + ''; }; config = { diff --git a/nixos/modules/tasks/filesystems/bcachefs.nix b/nixos/modules/tasks/filesystems/bcachefs.nix index 851c0978133..19ef188ce78 100644 --- a/nixos/modules/tasks/filesystems/bcachefs.nix +++ b/nixos/modules/tasks/filesystems/bcachefs.nix @@ -6,6 +6,15 @@ let bootFs = filterAttrs (n: fs: (fs.fsType == "bcachefs") && (utils.fsNeededForBoot fs)) config.fileSystems; + mountCommand = pkgs.runCommand "mount.bcachefs" {} '' + mkdir -p $out/bin + cat > $out/bin/mount.bcachefs <<EOF + #!/bin/sh + exec "/bin/bcachefs" mount "\$@" + EOF + chmod +x $out/bin/mount.bcachefs + ''; + commonFunctions = '' prompt() { local name="$1" @@ -58,13 +67,12 @@ in boot.initrd.systemd.extraBin = { "bcachefs" = "${pkgs.bcachefs-tools}/bin/bcachefs"; - "mount.bcachefs" = pkgs.runCommand "mount.bcachefs" {} '' - cp -pdv ${pkgs.bcachefs-tools}/bin/.mount.bcachefs.sh-wrapped $out - ''; + "mount.bcachefs" = "${mountCommand}/bin/mount.bcachefs"; }; boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) '' copy_bin_and_libs ${pkgs.bcachefs-tools}/bin/bcachefs + copy_bin_and_libs ${mountCommand}/bin/mount.bcachefs ''; boot.initrd.extraUtilsCommandsTest = '' $out/bin/bcachefs version diff --git a/nixos/modules/tasks/lvm.nix b/nixos/modules/tasks/lvm.nix index a14f26c02e4..325a5aa45b1 100644 --- a/nixos/modules/tasks/lvm.nix +++ b/nixos/modules/tasks/lvm.nix @@ -25,8 +25,12 @@ in { boot.vdo.enable = mkEnableOption (lib.mdDoc "support for booting from VDOLVs"); }; - options.boot.initrd.services.lvm.enable = (mkEnableOption (lib.mdDoc "enable booting from LVM2 in the initrd")) // { - visible = false; + options.boot.initrd.services.lvm.enable = mkEnableOption (lib.mdDoc "booting from LVM2 in the initrd") // { + description = lib.mdDoc '' + *This will only be used when systemd is used in stage 1.* + + Whether to enable booting from LVM2 in the initrd. + ''; }; config = mkMerge [ @@ -40,12 +44,13 @@ in { systemd.packages = [ cfg.package ]; services.udev.packages = [ cfg.package.out ]; - + }) + (mkIf config.boot.initrd.services.lvm.enable { # We need lvm2 for the device-mapper rules - boot.initrd.services.udev.packages = lib.mkIf config.boot.initrd.services.lvm.enable [ cfg.package ]; + boot.initrd.services.udev.packages = [ cfg.package ]; # The device-mapper rules want to call tools from lvm2 - boot.initrd.systemd.initrdBin = lib.mkIf config.boot.initrd.services.lvm.enable [ cfg.package ]; - boot.initrd.services.udev.binPackages = lib.mkIf config.boot.initrd.services.lvm.enable [ cfg.package ]; + boot.initrd.systemd.initrdBin = [ cfg.package ]; + boot.initrd.services.udev.binPackages = [ cfg.package ]; }) (mkIf cfg.dmeventd.enable { systemd.sockets."dm-event".wantedBy = [ "sockets.target" ]; diff --git a/nixos/modules/tasks/swraid.nix b/nixos/modules/tasks/swraid.nix index 7832bbf9201..1c3f1db1509 100644 --- a/nixos/modules/tasks/swraid.nix +++ b/nixos/modules/tasks/swraid.nix @@ -5,8 +5,12 @@ in { options.boot.initrd.services.swraid = { - enable = (lib.mkEnableOption (lib.mdDoc "swraid support using mdadm")) // { - visible = false; # only has effect when the new stage 1 is in place + enable = lib.mkEnableOption (lib.mdDoc "swraid support using mdadm") // { + description = '' + *This will only be used when systemd is used in stage 1.* + + Whether to enable swraid support using mdadm. + ''; }; mdadmConf = lib.mkOption { diff --git a/nixos/modules/virtualisation/proxmox-image.nix b/nixos/modules/virtualisation/proxmox-image.nix index b5d4ecd0268..1074b02d0eb 100644 --- a/nixos/modules/virtualisation/proxmox-image.nix +++ b/nixos/modules/virtualisation/proxmox-image.nix @@ -98,10 +98,12 @@ with lib; qemuExtraConf = mkOption { type = with types; attrsOf (oneOf [ str int ]); default = {}; - example = literalExpression ''{ - cpu = "host"; - onboot = 1; - }''; + example = literalExpression '' + { + cpu = "host"; + onboot = 1; + } + ''; description = lib.mdDoc '' Additional options appended to qemu-server.conf ''; |