diff options
author | Alyssa Ross <hi@alyssa.is> | 2023-12-07 14:04:47 +0100 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2023-12-07 14:04:47 +0100 |
commit | ba08e4e99b00a6916c4360de7288a7bfcef85328 (patch) | |
tree | 347c8b6ad50fcaafc08e50f1307a861378650440 /nixpkgs/nixos | |
parent | 190fd93d11701ad81af757be6260df9635bdb41a (diff) | |
parent | 2c7f3c0fb7c08a0814627611d9d7d45ab6d75335 (diff) | |
download | nixlib-ba08e4e99b00a6916c4360de7288a7bfcef85328.tar nixlib-ba08e4e99b00a6916c4360de7288a7bfcef85328.tar.gz nixlib-ba08e4e99b00a6916c4360de7288a7bfcef85328.tar.bz2 nixlib-ba08e4e99b00a6916c4360de7288a7bfcef85328.tar.lz nixlib-ba08e4e99b00a6916c4360de7288a7bfcef85328.tar.xz nixlib-ba08e4e99b00a6916c4360de7288a7bfcef85328.tar.zst nixlib-ba08e4e99b00a6916c4360de7288a7bfcef85328.zip |
Merge branch 'nixos-unstable' of https://github.com/NixOS/nixpkgs
Diffstat (limited to 'nixpkgs/nixos')
45 files changed, 1610 insertions, 239 deletions
diff --git a/nixpkgs/nixos/doc/manual/configuration/luks-file-systems.section.md b/nixpkgs/nixos/doc/manual/configuration/luks-file-systems.section.md index b5d0407d1659..7615b95aef42 100644 --- a/nixpkgs/nixos/doc/manual/configuration/luks-file-systems.section.md +++ b/nixpkgs/nixos/doc/manual/configuration/luks-file-systems.section.md @@ -42,8 +42,12 @@ boot.loader.grub.enableCryptodisk = true; ## FIDO2 {#sec-luks-file-systems-fido2} -NixOS also supports unlocking your LUKS-Encrypted file system using a -FIDO2 compatible token. In the following example, we will create a new +NixOS also supports unlocking your LUKS-Encrypted file system using a FIDO2 +compatible token. + +### Without systemd in initrd {#sec-luks-file-systems-fido2-legacy} + +In the following example, we will create a new FIDO2 credential and add it as a new key to our existing device `/dev/sda2`: @@ -75,3 +79,37 @@ as [Trezor](https://trezor.io/). ```nix boot.initrd.luks.devices."/dev/sda2".fido2.passwordLess = true; ``` + +### systemd Stage 1 {#sec-luks-file-systems-fido2-systemd} + +If systemd stage 1 is enabled, it handles unlocking of LUKS-enrypted volumes +during boot. The following example enables systemd stage1 and adds support for +unlocking the existing LUKS2 volume `root` using any enrolled FIDO2 compatible +tokens. + +```nix +boot.initrd = { + luks.devices.root = { + crypttabExtraOpts = [ "fido2-device=auto" ]; + device = "/dev/sda2"; + }; + systemd.enable = true; +}; +``` + +All tokens that should be used for unlocking the LUKS2-encrypted volume must +first be enrolled using [systemd-cryptenroll](https://www.freedesktop.org/software/systemd/man/systemd-cryptenroll.html). +In the following example, a new key slot for the first discovered token is +added to the LUKS volume. + +```ShellSession +# systemd-cryptenroll --fido2-device=auto /dev/sda2 +``` + +Existing key slots are left intact, unless `--wipe-slot=` is specified. It is +recommened to add a recovery key that should be stored in a secure physical +location and can be entered wherever a password would be entered. + +```ShellSession +# systemd-cryptenroll --recovery-key /dev/sda2 +``` diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md index 760c58d5050e..e693067561a4 100644 --- a/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md @@ -71,7 +71,9 @@ Make sure to also check the many updates in the [Nixpkgs library](#sec-release-2 - `services.mastodon` doesn't support providing a TCP port to its `streaming` component anymore, as upstream implemented parallelization by running multiple instances instead of running multiple processes in one instance. - Please create a PR if you are interested in this feature. + Please create a PR if you are interested in this feature.\ + Due to this, the desired number of such instances + {option}`services.mastodon.streamingProcesses` now needs to be declared explicitly. - The `services.hostapd` module was rewritten to support `passwordFile` like options, WPA3-SAE, and management of multiple interfaces. This breaks diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md index 749ebc5cb13b..9191a204a7a1 100644 --- a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md @@ -8,15 +8,20 @@ In addition to numerous new and upgraded packages, this release has the followin <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> -- Create the first release note entry in this section! +- `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment. ## New Services {#sec-release-24.05-new-services} <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> +- [Guix](https://guix.gnu.org), a functional package manager inspired by Nix. Available as [services.guix](#opt-services.guix.enable). + - [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable). - [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable). +The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server softwares. + +- [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable). ## Backward Incompatibilities {#sec-release-24.05-incompatibilities} @@ -29,6 +34,10 @@ In addition to numerous new and upgraded packages, this release has the followin <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> +- `addDriverRunpath` has been added to facilitate the deprecation of the old `addOpenGLRunpath` setuphook. This change is motivated by the evolution of the setuphook to include all hardware acceleration. + +- Cinnamon has been updated to 6.0. Please beware that the [Wayland session](https://blog.linuxmint.com/?p=4591) is still experimental in this release. + - Programs written in [Nim](https://nim-lang.org/) are built with libraries selected by lockfiles. The `nimPackages` and `nim2Packages` sets have been removed. See https://nixos.org/manual/nixpkgs/unstable#nim for more information. @@ -38,4 +47,11 @@ In addition to numerous new and upgraded packages, this release has the followin existing process, but will need to start that process from gdb (so it is a child). Or you can set `boot.kernel.sysctl."kernel.yama.ptrace_scope"` to 0. +- Gitea 1.21 upgrade has several breaking changes, including: + - Custom themes and other assets that were previously stored in `custom/public/*` now belong in `custom/public/assets/*` + - New instances of Gitea using MySQL now ignore the `[database].CHARSET` config option and always use the `utf8mb4` charset, existing instances should migrate via the `gitea doctor convert` CLI command. + - The `hardware.pulseaudio` module now sets permission of pulse user home directory to 755 when running in "systemWide" mode. It fixes [issue 114399](https://github.com/NixOS/nixpkgs/issues/114399). + +- QtMultimedia has changed its default backend to `QT_MEDIA_BACKEND=ffmpeg` (previously `gstreamer` on Linux or `darwin` on MacOS). + The previous native backends remain available but are now minimally maintained. Refer to [upstream documentation](https://doc.qt.io/qt-6/qtmultimedia-index.html#ffmpeg-as-the-default-backend) for further details about each platform. diff --git a/nixpkgs/nixos/modules/config/mysql.nix b/nixpkgs/nixos/modules/config/mysql.nix index 95c9ba76663e..4f72d22c4f0e 100644 --- a/nixpkgs/nixos/modules/config/mysql.nix +++ b/nixpkgs/nixos/modules/config/mysql.nix @@ -6,6 +6,8 @@ let cfg = config.users.mysql; in { + meta.maintainers = [ maintainers.netali ]; + options = { users.mysql = { enable = mkEnableOption (lib.mdDoc "Authentication against a MySQL/MariaDB database"); @@ -358,7 +360,7 @@ in user = "root"; group = "root"; mode = "0600"; - # password will be added from password file in activation script + # password will be added from password file in systemd oneshot text = '' users.host=${cfg.host} users.db_user=${cfg.user} @@ -423,34 +425,45 @@ in mode = "0600"; user = config.services.nscd.user; group = config.services.nscd.group; - # password will be added from password file in activation script + # password will be added from password file in systemd oneshot text = '' username ${cfg.user} ''; }; - # preStart script to append the password from the password file - # to the configuration files. It also fixes the owner of the - # libnss-mysql-root.cfg because it is changed to root after the - # password is appended. - systemd.services.mysql.preStart = '' - if [[ -r ${cfg.passwordFile} ]]; then - org_umask=$(umask) - umask 0077 + systemd.services.mysql-auth-pw-init = { + description = "Adds the mysql password to the mysql auth config files"; + + before = [ "nscd.service" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "oneshot"; + User = "root"; + Group = "root"; + }; - conf_nss="$(mktemp)" - cp /etc/libnss-mysql-root.cfg $conf_nss - printf 'password %s\n' "$(cat ${cfg.passwordFile})" >> $conf_nss - mv -fT "$conf_nss" /etc/libnss-mysql-root.cfg - chown ${config.services.nscd.user}:${config.services.nscd.group} /etc/libnss-mysql-root.cfg + restartTriggers = [ + config.environment.etc."security/pam_mysql.conf".source + config.environment.etc."libnss-mysql.cfg".source + config.environment.etc."libnss-mysql-root.cfg".source + ]; - conf_pam="$(mktemp)" - cp /etc/security/pam_mysql.conf $conf_pam - printf 'users.db_passwd=%s\n' "$(cat ${cfg.passwordFile})" >> $conf_pam - mv -fT "$conf_pam" /etc/security/pam_mysql.conf + script = '' + if [[ -r ${cfg.passwordFile} ]]; then + umask 0077 + conf_nss="$(mktemp)" + cp /etc/libnss-mysql-root.cfg $conf_nss + printf 'password %s\n' "$(cat ${cfg.passwordFile})" >> $conf_nss + mv -fT "$conf_nss" /etc/libnss-mysql-root.cfg + chown ${config.services.nscd.user}:${config.services.nscd.group} /etc/libnss-mysql-root.cfg - umask $org_umask - fi - ''; + conf_pam="$(mktemp)" + cp /etc/security/pam_mysql.conf $conf_pam + printf 'users.db_passwd=%s\n' "$(cat ${cfg.passwordFile})" >> $conf_pam + mv -fT "$conf_pam" /etc/security/pam_mysql.conf + fi + ''; + }; }; } diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix index 2b06951cfb72..43bf477cb215 100644 --- a/nixpkgs/nixos/modules/module-list.nix +++ b/nixpkgs/nixos/modules/module-list.nix @@ -442,6 +442,7 @@ ./services/databases/surrealdb.nix ./services/databases/victoriametrics.nix ./services/desktops/accountsservice.nix + ./services/desktops/ayatana-indicators.nix ./services/desktops/bamf.nix ./services/desktops/blueman.nix ./services/desktops/cpupower-gui.nix @@ -684,6 +685,7 @@ ./services/misc/gollum.nix ./services/misc/gpsd.nix ./services/misc/greenclip.nix + ./services/misc/guix ./services/misc/headphones.nix ./services/misc/heisenbridge.nix ./services/misc/homepage-dashboard.nix @@ -1424,6 +1426,7 @@ ./system/activation/bootspec.nix ./system/activation/top-level.nix ./system/boot/binfmt.nix + ./system/boot/clevis.nix ./system/boot/emergency-mode.nix ./system/boot/grow-partition.nix ./system/boot/initrd-network.nix diff --git a/nixpkgs/nixos/modules/programs/screen.nix b/nixpkgs/nixos/modules/programs/screen.nix index 68de9e52d7be..41bfb5d7809a 100644 --- a/nixpkgs/nixos/modules/programs/screen.nix +++ b/nixpkgs/nixos/modules/programs/screen.nix @@ -1,33 +1,41 @@ { config, lib, pkgs, ... }: let - inherit (lib) mkOption mkIf types; cfg = config.programs.screen; in { - ###### interface - options = { programs.screen = { + enable = lib.mkEnableOption (lib.mdDoc "screen, a basic terminal multiplexer"); + + package = lib.mkPackageOptionMD pkgs "screen" { }; - screenrc = mkOption { - default = ""; - description = lib.mdDoc '' - The contents of /etc/screenrc file. + screenrc = lib.mkOption { + type = with lib.types; nullOr lines; + example = '' + defscrollback 10000 + startup_message off ''; - type = types.lines; + description = lib.mdDoc "The contents of {file}`/etc/screenrc` file"; }; }; }; - ###### implementation - - config = mkIf (cfg.screenrc != "") { - environment.etc.screenrc.text = cfg.screenrc; - - environment.systemPackages = [ pkgs.screen ]; + config = { + # TODO: Added in 24.05, remove before 24.11 + assertions = [ + { + assertion = cfg.screenrc != null -> cfg.enable; + message = "`programs.screen.screenrc` has been configured, but `programs.screen.enable` is not true"; + } + ]; + } // lib.mkIf cfg.enable { + environment.etc.screenrc = { + enable = cfg.screenrc != null; + text = cfg.screenrc; + }; + environment.systemPackages = [ cfg.package ]; security.pam.services.screen = {}; }; - } diff --git a/nixpkgs/nixos/modules/programs/tsm-client.nix b/nixpkgs/nixos/modules/programs/tsm-client.nix index 6cb225d102de..45d436221ee3 100644 --- a/nixpkgs/nixos/modules/programs/tsm-client.nix +++ b/nixpkgs/nixos/modules/programs/tsm-client.nix @@ -1,193 +1,144 @@ -{ config, lib, pkgs, ... }: +{ config, lib, options, pkgs, ... }: # XXX migration code for freeform settings: `options` can be removed in 2025 +let optionsGlobal = options; in let - inherit (builtins) length map; - inherit (lib.attrsets) attrNames filterAttrs hasAttr mapAttrs mapAttrsToList optionalAttrs; + inherit (lib.attrsets) attrNames attrValues mapAttrsToList removeAttrs; + inherit (lib.lists) all allUnique concatLists elem isList map; inherit (lib.modules) mkDefault mkIf; - inherit (lib.options) literalExpression mkEnableOption mkOption mkPackageOption; - inherit (lib.strings) concatLines optionalString toLower; - inherit (lib.types) addCheck attrsOf lines nonEmptyStr nullOr package path port str strMatching submodule; + inherit (lib.options) mkEnableOption mkOption mkPackageOption; + inherit (lib.strings) concatLines match optionalString toLower; + inherit (lib.trivial) isInt; + inherit (lib.types) addCheck attrsOf coercedTo either enum int lines listOf nonEmptyStr nullOr oneOf path port singleLineStr strMatching submodule; - # Checks if given list of strings contains unique - # elements when compared without considering case. - # Type: checkIUnique :: [string] -> bool - # Example: checkIUnique ["foo" "Foo"] => false - checkIUnique = lst: - let - lenUniq = l: length (lib.lists.unique l); - in - lenUniq lst == lenUniq (map toLower lst); + scalarType = + # see the option's description below for the + # handling/transformation of each possible type + oneOf [ (enum [ true null ]) int path singleLineStr ]; # TSM rejects servername strings longer than 64 chars. - servernameType = strMatching ".{1,64}"; + servernameType = strMatching "[^[:space:]]{1,64}"; serverOptions = { name, config, ... }: { - options.name = mkOption { + freeformType = attrsOf (either scalarType (listOf scalarType)); + # Client system-options file directives are explained here: + # https://www.ibm.com/docs/en/storage-protect/8.1.20?topic=commands-processing-options + options.servername = mkOption { type = servernameType; + default = name; example = "mainTsmServer"; description = lib.mdDoc '' Local name of the IBM TSM server, - must be uncapitalized and no longer than 64 chars. - The value will be used for the - `server` - directive in {file}`dsm.sys`. + must not contain space or more than 64 chars. ''; }; - options.server = mkOption { + options.tcpserveraddress = mkOption { type = nonEmptyStr; example = "tsmserver.company.com"; description = lib.mdDoc '' Host/domain name or IP address of the IBM TSM server. - The value will be used for the - `tcpserveraddress` - directive in {file}`dsm.sys`. ''; }; - options.port = mkOption { + options.tcpport = mkOption { type = addCheck port (p: p<=32767); default = 1500; # official default description = lib.mdDoc '' TCP port of the IBM TSM server. - The value will be used for the - `tcpport` - directive in {file}`dsm.sys`. TSM does not support ports above 32767. ''; }; - options.node = mkOption { + options.nodename = mkOption { type = nonEmptyStr; example = "MY-TSM-NODE"; description = lib.mdDoc '' Target node name on the IBM TSM server. - The value will be used for the - `nodename` - directive in {file}`dsm.sys`. ''; }; options.genPasswd = mkEnableOption (lib.mdDoc '' automatic client password generation. - This option influences the - `passwordaccess` - directive in {file}`dsm.sys`. + This option does *not* cause a line in + {file}`dsm.sys` by itself, but generates a + corresponding `passwordaccess` directive. The password will be stored in the directory - given by the option {option}`passwdDir`. + given by the option {option}`passworddir`. *Caution*: If this option is enabled and the server forces to renew the password (e.g. on first connection), a random password will be generated and stored ''); - options.passwdDir = mkOption { - type = path; + options.passwordaccess = mkOption { + type = enum [ "generate" "prompt" ]; + visible = false; + }; + options.passworddir = mkOption { + type = nullOr path; + default = null; example = "/home/alice/tsm-password"; description = lib.mdDoc '' Directory that holds the TSM node's password information. - The value will be used for the - `passworddir` - directive in {file}`dsm.sys`. ''; }; - options.includeExclude = mkOption { - type = lines; - default = ""; + options.inclexcl = mkOption { + type = coercedTo lines + (pkgs.writeText "inclexcl.dsm.sys") + (nullOr path); + default = null; example = '' exclude.dir /nix/store include.encrypt /home/.../* ''; description = lib.mdDoc '' - `include.*` and - `exclude.*` directives to be - used when sending files to the IBM TSM server. - The lines will be written into a file that the - `inclexcl` - directive in {file}`dsm.sys` points to. - ''; - }; - options.extraConfig = mkOption { - # TSM option keys are case insensitive; - # we have to ensure there are no keys that - # differ only by upper and lower case. - type = addCheck - (attrsOf (nullOr str)) - (attrs: checkIUnique (attrNames attrs)); - default = {}; - example.compression = "yes"; - example.passwordaccess = null; - description = lib.mdDoc '' - Additional key-value pairs for the server stanza. - Values must be strings, or `null` - for the key not to be used in the stanza - (e.g. to overrule values generated by other options). - ''; - }; - options.text = mkOption { - type = lines; - example = literalExpression - ''lib.modules.mkAfter "compression no"''; - description = lib.mdDoc '' - Additional text lines for the server stanza. - This option can be used if certion configuration keys - must be used multiple times or ordered in a certain way - as the {option}`extraConfig` option can't - control the order of lines in the resulting stanza. - Note that the `server` - line at the beginning of the stanza is - not part of this option's value. + Text lines with `include.*` and `exclude.*` directives + to be used when sending files to the IBM TSM server, + or an absolute path pointing to a file with such lines. ''; }; - options.stanza = mkOption { - type = str; - internal = true; - visible = false; - description = lib.mdDoc "Server stanza text generated from the options."; - }; - config.name = mkDefault name; - # Client system-options file directives are explained here: - # https://www.ibm.com/docs/en/spectrum-protect/8.1.13?topic=commands-processing-options - config.extraConfig = - mapAttrs (lib.trivial.const mkDefault) ( - { - commmethod = "v6tcpip"; # uses v4 or v6, based on dns lookup result - tcpserveraddress = config.server; - tcpport = builtins.toString config.port; - nodename = config.node; - passwordaccess = if config.genPasswd then "generate" else "prompt"; - passworddir = ''"${config.passwdDir}"''; - } // optionalAttrs (config.includeExclude!="") { - inclexcl = ''"${pkgs.writeText "inclexcl.dsm.sys" config.includeExclude}"''; - } - ); - config.text = - let - attrset = filterAttrs (k: v: v!=null) config.extraConfig; - mkLine = k: v: k + optionalString (v!="") " ${v}"; - lines = mapAttrsToList mkLine attrset; - in - concatLines lines; - config.stanza = '' - server ${config.name} - ${config.text} - ''; + config.commmethod = mkDefault "v6tcpip"; # uses v4 or v6, based on dns lookup result + config.passwordaccess = if config.genPasswd then "generate" else "prompt"; + # XXX migration code for freeform settings, these can be removed in 2025: + options.warnings = optionsGlobal.warnings; + options.assertions = optionsGlobal.assertions; + imports = let inherit (lib.modules) mkRemovedOptionModule mkRenamedOptionModule; in [ + (mkRemovedOptionModule [ "extraConfig" ] "Please just add options directly to the server attribute set, cf. the description of `programs.tsmClient.servers`.") + (mkRemovedOptionModule [ "text" ] "Please just add options directly to the server attribute set, cf. the description of `programs.tsmClient.servers`.") + (mkRenamedOptionModule [ "name" ] [ "servername" ]) + (mkRenamedOptionModule [ "server" ] [ "tcpserveraddress" ]) + (mkRenamedOptionModule [ "port" ] [ "tcpport" ]) + (mkRenamedOptionModule [ "node" ] [ "nodename" ]) + (mkRenamedOptionModule [ "passwdDir" ] [ "passworddir" ]) + (mkRenamedOptionModule [ "includeExclude" ] [ "inclexcl" ]) + ]; }; options.programs.tsmClient = { enable = mkEnableOption (lib.mdDoc '' - IBM Spectrum Protect (Tivoli Storage Manager, TSM) + IBM Storage Protect (Tivoli Storage Manager, TSM) client command line applications with a client system-options file "dsm.sys" ''); servers = mkOption { - type = attrsOf (submodule [ serverOptions ]); + type = attrsOf (submodule serverOptions); default = {}; example.mainTsmServer = { - server = "tsmserver.company.com"; - node = "MY-TSM-NODE"; - extraConfig.compression = "yes"; + tcpserveraddress = "tsmserver.company.com"; + nodename = "MY-TSM-NODE"; + compression = "yes"; }; description = lib.mdDoc '' Server definitions ("stanzas") for the client system-options file. + The name of each entry will be used for + the internal `servername` by default. + Each attribute will be transformed into a line + with a key-value pair within the server's stanza. + Integers as values will be + canonically turned into strings. + The boolean value `true` will be turned + into a line with just the attribute's name. + The value `null` will not generate a line. + A list as values generates an entry for + each value, according to the rules above. ''; }; defaultServername = mkOption { @@ -222,45 +173,107 @@ let to add paths to the client system-options file. ''; }; - wrappedPackage = mkOption { - type = package; - readOnly = true; - description = lib.mdDoc '' - The TSM client derivation, wrapped with the path - to the client system-options file "dsm.sys". - This option is to provide the effective derivation + wrappedPackage = mkPackageOption pkgs "tsm-client" { + default = null; + extraDescription = '' + This option is to provide the effective derivation, + wrapped with the path to the + client system-options file "dsm.sys". + It should not be changed, but exists for other modules that want to call TSM executables. ''; - }; + } // { readOnly = true; }; }; cfg = config.programs.tsmClient; + servernames = map (s: s.servername) (attrValues cfg.servers); - assertions = [ - { - assertion = checkIUnique (mapAttrsToList (k: v: v.name) cfg.servers); + assertions = + [ + { + assertion = allUnique (map toLower servernames); + message = '' + TSM server names + (option `programs.tsmClient.servers`) + contain duplicate name + (note that server names are case insensitive). + ''; + } + { + assertion = (cfg.defaultServername!=null)->(elem cfg.defaultServername servernames); + message = '' + TSM default server name + `programs.tsmClient.defaultServername="${cfg.defaultServername}"` + not found in server names in + `programs.tsmClient.servers`. + ''; + } + ] ++ (mapAttrsToList (name: serverCfg: { + assertion = all (key: null != match "[^[:space:]]+" key) (attrNames serverCfg); message = '' - TSM servernames contain duplicate name - (note that case doesn't matter!) + TSM server setting names in + `programs.tsmClient.servers.${name}.*` + contain spaces, but that's not allowed. + ''; + }) cfg.servers) ++ (mapAttrsToList (name: serverCfg: { + assertion = allUnique (map toLower (attrNames serverCfg)); + message = '' + TSM server setting names in + `programs.tsmClient.servers.${name}.*` + contain duplicate names + (note that setting names are case insensitive). + ''; + }) cfg.servers) + # XXX migration code for freeform settings, this can be removed in 2025: + ++ (enrichMigrationInfos "assertions" (addText: { assertion, message }: { inherit assertion; message = addText message; })); + + makeDsmSysLines = key: value: + # Turn a key-value pair from the server options attrset + # into zero (value==null), one (scalar value) or + # more (value is list) configuration stanza lines. + if isList value then map (makeDsmSysLines key) value else # recurse into list + if value == null then [ ] else # skip `null` value + [ (" ${key}${ + if value == true then "" else # just output key if value is `true` + if isInt value then " ${builtins.toString value}" else + if path.check value then " \"${value}\"" else # enclose path in ".." + if singleLineStr.check value then " ${value}" else + throw "assertion failed: cannot convert type" # should never happen + }") ]; + + makeDsmSysStanza = {servername, ... }@serverCfg: + let + # drop special values that should not go into server config block + attrs = removeAttrs serverCfg [ "servername" "genPasswd" + # XXX migration code for freeform settings, these can be removed in 2025: + "assertions" "warnings" + "extraConfig" "text" + "name" "server" "port" "node" "passwdDir" "includeExclude" + ]; + in + '' + servername ${servername} + ${concatLines (concatLists (mapAttrsToList makeDsmSysLines attrs))} ''; - } - { - assertion = (cfg.defaultServername!=null)->(hasAttr cfg.defaultServername cfg.servers); - message = "TSM defaultServername not found in list of servers"; - } - ]; dsmSysText = '' - **** IBM Spectrum Protect (Tivoli Storage Manager) + **** IBM Storage Protect (Tivoli Storage Manager) **** client system-options file "dsm.sys". **** Do not edit! **** This file is generated by NixOS configuration. ${optionalString (cfg.defaultServername!=null) "defaultserver ${cfg.defaultServername}"} - ${concatLines (mapAttrsToList (k: v: v.stanza) cfg.servers)} + ${concatLines (map makeDsmSysStanza (attrValues cfg.servers))} ''; + # XXX migration code for freeform settings, this can be removed in 2025: + enrichMigrationInfos = what: how: concatLists ( + mapAttrsToList + (name: serverCfg: map (how (text: "In `programs.tsmClient.servers.${name}`: ${text}")) serverCfg."${what}") + cfg.servers + ); + in { @@ -275,6 +288,8 @@ in dsmSysApi = dsmSysCli; }; environment.systemPackages = [ cfg.wrappedPackage ]; + # XXX migration code for freeform settings, this can be removed in 2025: + warnings = enrichMigrationInfos "warnings" (addText: addText); }; meta.maintainers = [ lib.maintainers.yarny ]; diff --git a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix index d3c6f3104fc5..82067d8ade34 100644 --- a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix +++ b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix @@ -17,8 +17,8 @@ let compressCmd = getAttr cfg.compression { "none" = "cat"; - "gzip" = "${pkgs.gzip}/bin/gzip -c -${toString cfg.compressionLevel}"; - "zstd" = "${pkgs.zstd}/bin/zstd -c -${toString cfg.compressionLevel}"; + "gzip" = "${pkgs.gzip}/bin/gzip -c -${toString cfg.compressionLevel} --rsyncable"; + "zstd" = "${pkgs.zstd}/bin/zstd -c -${toString cfg.compressionLevel} --rsyncable"; }; mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}"; @@ -178,4 +178,5 @@ in { }) ]; + meta.maintainers = with lib.maintainers; [ Scrumplex ]; } diff --git a/nixpkgs/nixos/modules/services/backup/tsm.nix b/nixpkgs/nixos/modules/services/backup/tsm.nix index c4de0b16d47d..6798b18b3af7 100644 --- a/nixpkgs/nixos/modules/services/backup/tsm.nix +++ b/nixpkgs/nixos/modules/services/backup/tsm.nix @@ -3,6 +3,7 @@ let inherit (lib.attrsets) hasAttr; + inherit (lib.meta) getExe'; inherit (lib.modules) mkDefault mkIf; inherit (lib.options) mkEnableOption mkOption; inherit (lib.types) nonEmptyStr nullOr; @@ -10,7 +11,7 @@ let options.services.tsmBackup = { enable = mkEnableOption (lib.mdDoc '' automatic backups with the - IBM Spectrum Protect (Tivoli Storage Manager, TSM) client. + IBM Storage Protect (Tivoli Storage Manager, TSM) client. This also enables {option}`programs.tsmClient.enable` ''); @@ -78,10 +79,10 @@ in config = mkIf cfg.enable { inherit assertions; programs.tsmClient.enable = true; - programs.tsmClient.servers.${cfg.servername}.passwdDir = + programs.tsmClient.servers.${cfg.servername}.passworddir = mkDefault "/var/lib/tsm-backup/password"; systemd.services.tsm-backup = { - description = "IBM Spectrum Protect (Tivoli Storage Manager) Backup"; + description = "IBM Storage Protect (Tivoli Storage Manager) Backup"; # DSM_LOG needs a trailing slash to have it treated as a directory. # `/var/log` would be littered with TSM log files otherwise. environment.DSM_LOG = "/var/log/tsm-backup/"; @@ -89,12 +90,12 @@ in environment.HOME = "/var/lib/tsm-backup"; serviceConfig = { # for exit status description see - # https://www.ibm.com/docs/en/spectrum-protect/8.1.13?topic=clients-client-return-codes + # https://www.ibm.com/docs/en/storage-protect/8.1.20?topic=clients-client-return-codes SuccessExitStatus = "4 8"; # The `-se` option must come after the command. # The `-optfile` option suppresses a `dsm.opt`-not-found warning. ExecStart = - "${cfgPrg.wrappedPackage}/bin/dsmc ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null"; + "${getExe' cfgPrg.wrappedPackage "dsmc"} ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null"; LogsDirectory = "tsm-backup"; StateDirectory = "tsm-backup"; StateDirectoryMode = "0750"; diff --git a/nixpkgs/nixos/modules/services/continuous-integration/buildbot/worker.nix b/nixpkgs/nixos/modules/services/continuous-integration/buildbot/worker.nix index b906788209b1..2a836c24dda3 100644 --- a/nixpkgs/nixos/modules/services/continuous-integration/buildbot/worker.nix +++ b/nixpkgs/nixos/modules/services/continuous-integration/buildbot/worker.nix @@ -128,9 +128,7 @@ in { ''; }; - package = mkPackageOption pkgs "python3Packages.buildbot-worker" { - example = "python2Packages.buildbot-worker"; - }; + package = mkPackageOption pkgs "buildbot-worker" { }; packages = mkOption { default = with pkgs; [ git ]; diff --git a/nixpkgs/nixos/modules/services/desktops/ayatana-indicators.nix b/nixpkgs/nixos/modules/services/desktops/ayatana-indicators.nix new file mode 100644 index 000000000000..abc687bbd43d --- /dev/null +++ b/nixpkgs/nixos/modules/services/desktops/ayatana-indicators.nix @@ -0,0 +1,58 @@ +{ config +, pkgs +, lib +, ... +}: + +let + cfg = config.services.ayatana-indicators; +in +{ + options.services.ayatana-indicators = { + enable = lib.mkEnableOption (lib.mdDoc '' + Ayatana Indicators, a continuation of Canonical's Application Indicators + ''); + + packages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + example = lib.literalExpression "with pkgs; [ ayatana-indicator-messages ]"; + description = lib.mdDoc '' + List of packages containing Ayatana Indicator services + that should be brought up by the SystemD "ayatana-indicators" user target. + + Packages specified here must have passthru.ayatana-indicators set correctly. + + If, how, and where these indicators are displayed will depend on your DE. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + environment = { + systemPackages = cfg.packages; + + pathsToLink = [ + "/share/ayatana" + ]; + }; + + # libayatana-common's ayatana-indicators.target with explicit Wants & Before to bring up requested indicator services + systemd.user.targets."ayatana-indicators" = + let + indicatorServices = lib.lists.flatten + (map + (pkg: + (map (ind: "${ind}.service") pkg.passthru.ayatana-indicators)) + cfg.packages); + in + { + description = "Target representing the lifecycle of the Ayatana Indicators. Each indicator should be bound to it in its individual service file"; + partOf = [ "graphical-session.target" ]; + wants = indicatorServices; + before = indicatorServices; + }; + }; + + meta.maintainers = with lib.maintainers; [ OPNA2608 ]; +} diff --git a/nixpkgs/nixos/modules/services/hardware/thinkfan.nix b/nixpkgs/nixos/modules/services/hardware/thinkfan.nix index 8fa7b456f20e..cca35f492b8e 100644 --- a/nixpkgs/nixos/modules/services/hardware/thinkfan.nix +++ b/nixpkgs/nixos/modules/services/hardware/thinkfan.nix @@ -217,6 +217,8 @@ in { systemd.services = { thinkfan.environment.THINKFAN_ARGS = escapeShellArgs ([ "-c" configFile ] ++ cfg.extraArgs); + thinkfan.serviceConfig.Restart = "on-failure"; + thinkfan.serviceConfig.RestartSec = "30s"; # must be added manually, see issue #81138 thinkfan.wantedBy = [ "multi-user.target" ]; diff --git a/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix b/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix index 54fd3e17292f..6aa0ae9eba47 100644 --- a/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix +++ b/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix @@ -455,10 +455,10 @@ in { ln -s /etc/home-assistant/configuration.yaml "${cfg.configDir}/configuration.yaml" ''; copyLovelaceConfig = if cfg.lovelaceConfigWritable then '' + rm -f "${cfg.configDir}/ui-lovelace.yaml" cp --no-preserve=mode ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml" '' else '' - rm -f "${cfg.configDir}/ui-lovelace.yaml" - ln -s /etc/home-assistant/ui-lovelace.yaml "${cfg.configDir}/ui-lovelace.yaml" + ln -fs /etc/home-assistant/ui-lovelace.yaml "${cfg.configDir}/ui-lovelace.yaml" ''; copyCustomLovelaceModules = if cfg.customLovelaceModules != [] then '' mkdir -p "${cfg.configDir}/www" diff --git a/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix b/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix index d153ffc2ace8..c79cd799b4d0 100644 --- a/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix +++ b/nixpkgs/nixos/modules/services/matrix/appservice-irc.nix @@ -214,7 +214,7 @@ in { RestrictRealtime = true; PrivateMounts = true; SystemCallFilter = [ - "@system-service @pkey" + "@system-service @pkey @chown" "~@privileged @resources" ]; SystemCallArchitectures = "native"; diff --git a/nixpkgs/nixos/modules/services/misc/ankisyncd.nix b/nixpkgs/nixos/modules/services/misc/ankisyncd.nix index e4de46e19a8f..f5acfbb0ee96 100644 --- a/nixpkgs/nixos/modules/services/misc/ankisyncd.nix +++ b/nixpkgs/nixos/modules/services/misc/ankisyncd.nix @@ -46,6 +46,12 @@ in }; config = mkIf cfg.enable { + warnings = [ + '' + `services.ankisyncd` has been replaced by `services.anki-sync-server` and will be removed after + 24.05 because anki-sync-server(-rs and python) are not maintained. + '' + ]; networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; systemd.services.ankisyncd = { diff --git a/nixpkgs/nixos/modules/services/misc/guix/default.nix b/nixpkgs/nixos/modules/services/misc/guix/default.nix new file mode 100644 index 000000000000..00e84dc74554 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/guix/default.nix @@ -0,0 +1,394 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.services.guix; + + package = cfg.package.override { inherit (cfg) stateDir storeDir; }; + + guixBuildUser = id: { + name = "guixbuilder${toString id}"; + group = cfg.group; + extraGroups = [ cfg.group ]; + createHome = false; + description = "Guix build user ${toString id}"; + isSystemUser = true; + }; + + guixBuildUsers = numberOfUsers: + builtins.listToAttrs (map + (user: { + name = user.name; + value = user; + }) + (builtins.genList guixBuildUser numberOfUsers)); + + # A set of Guix user profiles to be linked at activation. + guixUserProfiles = { + # The current Guix profile that is created through `guix pull`. + "current-guix" = "\${XDG_CONFIG_HOME}/guix/current"; + + # The default Guix profile similar to $HOME/.nix-profile from Nix. + "guix-profile" = "$HOME/.guix-profile"; + }; + + # All of the Guix profiles to be used. + guixProfiles = lib.attrValues guixUserProfiles; + + serviceEnv = { + GUIX_LOCPATH = "${cfg.stateDir}/guix/profiles/per-user/root/guix-profile/lib/locale"; + LC_ALL = "C.UTF-8"; + }; +in +{ + meta.maintainers = with lib.maintainers; [ foo-dogsquared ]; + + options.services.guix = with lib; { + enable = mkEnableOption "Guix build daemon service"; + + group = mkOption { + type = types.str; + default = "guixbuild"; + example = "guixbuild"; + description = '' + The group of the Guix build user pool. + ''; + }; + + nrBuildUsers = mkOption { + type = types.ints.unsigned; + description = '' + Number of Guix build users to be used in the build pool. + ''; + default = 10; + example = 20; + }; + + extraArgs = mkOption { + type = with types; listOf str; + default = [ ]; + example = [ "--max-jobs=4" "--debug" ]; + description = '' + Extra flags to pass to the Guix daemon service. + ''; + }; + + package = mkPackageOption pkgs "guix" { + extraDescription = '' + It should contain {command}`guix-daemon` and {command}`guix` + executable. + ''; + }; + + storeDir = mkOption { + type = types.path; + default = "/gnu/store"; + description = '' + The store directory where the Guix service will serve to/from. Take + note Guix cannot take advantage of substitutes if you set it something + other than {file}`/gnu/store` since most of the cached builds are + assumed to be in there. + + ::: {.warning} + This will also recompile all packages because the normal cache no + longer applies. + ::: + ''; + }; + + stateDir = mkOption { + type = types.path; + default = "/var"; + description = '' + The state directory where Guix service will store its data such as its + user-specific profiles, cache, and state files. + + ::: {.warning} + Changing it to something other than the default will rebuild the + package. + ::: + ''; + example = "/gnu/var"; + }; + + publish = { + enable = mkEnableOption "substitute server for your Guix store directory"; + + generateKeyPair = mkOption { + type = types.bool; + description = '' + Whether to generate signing keys in {file}`/etc/guix` which are + required to initialize a substitute server. Otherwise, + `--public-key=$FILE` and `--private-key=$FILE` can be passed in + {option}`services.guix.publish.extraArgs`. + ''; + default = true; + example = false; + }; + + port = mkOption { + type = types.port; + default = 8181; + example = 8200; + description = '' + Port of the substitute server to listen on. + ''; + }; + + user = mkOption { + type = types.str; + default = "guix-publish"; + description = '' + Name of the user to change once the server is up. + ''; + }; + + extraArgs = mkOption { + type = with types; listOf str; + description = '' + Extra flags to pass to the substitute server. + ''; + default = []; + example = [ + "--compression=zstd:6" + "--discover=no" + ]; + }; + }; + + gc = { + enable = mkEnableOption "automatic garbage collection service for Guix"; + + extraArgs = mkOption { + type = with types; listOf str; + default = [ ]; + description = '' + List of arguments to be passed to {command}`guix gc`. + + When given no option, it will try to collect all garbage which is + often inconvenient so it is recommended to set [some + options](https://guix.gnu.org/en/manual/en/html_node/Invoking-guix-gc.html). + ''; + example = [ + "--delete-generations=1m" + "--free-space=10G" + "--optimize" + ]; + }; + + dates = lib.mkOption { + type = types.str; + default = "03:15"; + example = "weekly"; + description = '' + How often the garbage collection occurs. This takes the time format + from {manpage}`systemd.time(7)`. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + environment.systemPackages = [ package ]; + + users.users = guixBuildUsers cfg.nrBuildUsers; + users.groups.${cfg.group} = { }; + + # Guix uses Avahi (through guile-avahi) both for the auto-discovering and + # advertising substitute servers in the local network. + services.avahi.enable = lib.mkDefault true; + services.avahi.publish.enable = lib.mkDefault true; + services.avahi.publish.userServices = lib.mkDefault true; + + # It's similar to Nix daemon so there's no question whether or not this + # should be sandboxed. + systemd.services.guix-daemon = { + environment = serviceEnv; + script = '' + ${lib.getExe' package "guix-daemon"} \ + --build-users-group=${cfg.group} \ + ${lib.escapeShellArgs cfg.extraArgs} + ''; + serviceConfig = { + OOMPolicy = "continue"; + RemainAfterExit = "yes"; + Restart = "always"; + TasksMax = 8192; + }; + unitConfig.RequiresMountsFor = [ + cfg.storeDir + cfg.stateDir + ]; + wantedBy = [ "multi-user.target" ]; + }; + + # This is based from Nix daemon socket unit from upstream Nix package. + # Guix build daemon has support for systemd-style socket activation. + systemd.sockets.guix-daemon = { + description = "Guix daemon socket"; + before = [ "multi-user.target" ]; + listenStreams = [ "${cfg.stateDir}/guix/daemon-socket/socket" ]; + unitConfig = { + RequiresMountsFor = [ + cfg.storeDir + cfg.stateDir + ]; + ConditionPathIsReadWrite = "${cfg.stateDir}/guix/daemon-socket"; + }; + wantedBy = [ "socket.target" ]; + }; + + systemd.mounts = [{ + description = "Guix read-only store directory"; + before = [ "guix-daemon.service" ]; + what = cfg.storeDir; + where = cfg.storeDir; + type = "none"; + options = "bind,ro"; + + unitConfig.DefaultDependencies = false; + wantedBy = [ "guix-daemon.service" ]; + }]; + + # Make transferring files from one store to another easier with the usual + # case being of most substitutes from the official Guix CI instance. + system.activationScripts.guix-authorize-keys = '' + for official_server_keys in ${package}/share/guix/*.pub; do + ${lib.getExe' package "guix"} archive --authorize < $official_server_keys + done + ''; + + # Link the usual Guix profiles to the home directory. This is useful in + # ephemeral setups where only certain part of the filesystem is + # persistent (e.g., "Erase my darlings"-type of setup). + system.userActivationScripts.guix-activate-user-profiles.text = let + linkProfileToPath = acc: profile: location: let + guixProfile = "${cfg.stateDir}/guix/profiles/per-user/\${USER}/${profile}"; + in acc + '' + [ -d "${guixProfile}" ] && ln -sf "${guixProfile}" "${location}" + ''; + + activationScript = lib.foldlAttrs linkProfileToPath "" guixUserProfiles; + in '' + # Don't export this please! It is only expected to be used for this + # activation script and nothing else. + XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config} + + # Linking the usual Guix profiles into the home directory. + ${activationScript} + ''; + + # GUIX_LOCPATH is basically LOCPATH but for Guix libc which in turn used by + # virtually every Guix-built packages. This is so that Guix-installed + # applications wouldn't use incompatible locale data and not touch its host + # system. + environment.sessionVariables.GUIX_LOCPATH = lib.makeSearchPath "lib/locale" guixProfiles; + + # What Guix profiles export is very similar to Nix profiles so it is + # acceptable to list it here. Also, it is more likely that the user would + # want to use packages explicitly installed from Guix so we're putting it + # first. + environment.profiles = lib.mkBefore guixProfiles; + } + + (lib.mkIf cfg.publish.enable { + systemd.services.guix-publish = { + description = "Guix remote store"; + environment = serviceEnv; + + # Mounts will be required by the daemon service anyways so there's no + # need add RequiresMountsFor= or something similar. + requires = [ "guix-daemon.service" ]; + after = [ "guix-daemon.service" ]; + partOf = [ "guix-daemon.service" ]; + + preStart = lib.mkIf cfg.publish.generateKeyPair '' + # Generate the keypair if it's missing. + [ -f "/etc/guix/signing-key.sec" ] && [ -f "/etc/guix/signing-key.pub" ] || \ + ${lib.getExe' package "guix"} archive --generate-key || { + rm /etc/guix/signing-key.*; + ${lib.getExe' package "guix"} archive --generate-key; + } + ''; + script = '' + ${lib.getExe' package "guix"} publish \ + --user=${cfg.publish.user} --port=${builtins.toString cfg.publish.port} \ + ${lib.escapeShellArgs cfg.publish.extraArgs} + ''; + + serviceConfig = { + Restart = "always"; + RestartSec = 10; + + ProtectClock = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + SystemCallFilter = [ + "@system-service" + "@debug" + "@setuid" + ]; + + RestrictNamespaces = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + + # While the permissions can be set, it is assumed to be taken by Guix + # daemon service which it has already done the setup. + ConfigurationDirectory = "guix"; + + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + CapabilityBoundingSet = [ + "CAP_NET_BIND_SERVICE" + "CAP_SETUID" + "CAP_SETGID" + ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + + users.users.guix-publish = lib.mkIf (cfg.publish.user == "guix-publish") { + description = "Guix publish user"; + group = config.users.groups.guix-publish.name; + isSystemUser = true; + }; + users.groups.guix-publish = {}; + }) + + (lib.mkIf cfg.gc.enable { + # This service should be handled by root to collect all garbage by all + # users. + systemd.services.guix-gc = { + description = "Guix garbage collection"; + startAt = cfg.gc.dates; + script = '' + ${lib.getExe' package "guix"} gc ${lib.escapeShellArgs cfg.gc.extraArgs} + ''; + + serviceConfig = { + Type = "oneshot"; + + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateNetworks = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelTunables = true; + SystemCallFilter = [ + "@default" + "@file-system" + "@basic-io" + "@system-service" + ]; + }; + }; + + systemd.timers.guix-gc.timerConfig.Persistent = true; + }) + ]); +} diff --git a/nixpkgs/nixos/modules/services/misc/preload.nix b/nixpkgs/nixos/modules/services/misc/preload.nix index 19b2531087dd..d26e2c3d383e 100644 --- a/nixpkgs/nixos/modules/services/misc/preload.nix +++ b/nixpkgs/nixos/modules/services/misc/preload.nix @@ -19,7 +19,7 @@ in { serviceConfig = { EnvironmentFile = "${cfg.package}/etc/conf.d/preload"; - ExecStart = "${getExe cfg.package} --foreground $PRELOAD_OPTS"; + ExecStart = "${getExe cfg.package} -l '' --foreground $PRELOAD_OPTS"; Type = "simple"; # Only preload data during CPU idle time IOSchedulingClass = 3; diff --git a/nixpkgs/nixos/modules/services/misc/redmine.nix b/nixpkgs/nixos/modules/services/misc/redmine.nix index e3941d2e29de..b517170cda21 100644 --- a/nixpkgs/nixos/modules/services/misc/redmine.nix +++ b/nixpkgs/nixos/modules/services/misc/redmine.nix @@ -264,9 +264,12 @@ in { assertion = cfg.database.passwordFile != null || cfg.database.socket != null; message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set"; } - { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user && cfg.database.user == cfg.database.name; + { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user; message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true"; } + { assertion = pgsqlLocal -> cfg.database.user == cfg.database.name; + message = "services.redmine.database.user and services.redmine.database.name must be the same when using a local postgresql database"; + } { assertion = cfg.database.createLocally -> cfg.database.socket != null; message = "services.redmine.database.socket must be set if services.redmine.database.createLocally is set to true"; } diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix index db5c4d15be66..b36a09c60920 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix @@ -55,12 +55,12 @@ in RuntimeDirectory = "prometheus-mongodb-exporter"; ExecStart = '' ${getExe pkgs.prometheus-mongodb-exporter} \ - --mongodb.uri=${cfg.uri} + --mongodb.uri="${cfg.uri}" \ ${if cfg.collectAll then "--collect-all" else concatMapStringsSep " " (x: "--collect.${x}") cfg.collector} \ - --collector.collstats=${concatStringsSep "," cfg.collStats} \ - --collector.indexstats=${concatStringsSep "," cfg.indexStats} \ - --web.listen-address=${cfg.listenAddress}:${toString cfg.port} \ - --web.telemetry-path=${cfg.telemetryPath} \ + ${optionalString (length cfg.collStats > 0) "--mongodb.collstats-colls=${concatStringsSep "," cfg.collStats}"} \ + ${optionalString (length cfg.indexStats > 0) "--mongodb.indexstats-colls=${concatStringsSep "," cfg.indexStats}"} \ + --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \ + --web.telemetry-path="${cfg.telemetryPath}" \ ${escapeShellArgs cfg.extraFlags} ''; }; diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/default.nix b/nixpkgs/nixos/modules/services/networking/keepalived/default.nix index 29fbea5545c3..429a47c3962c 100644 --- a/nixpkgs/nixos/modules/services/networking/keepalived/default.nix +++ b/nixpkgs/nixos/modules/services/networking/keepalived/default.nix @@ -150,6 +150,14 @@ in ''; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to automatically allow VRRP and AH packets in the firewall. + ''; + }; + enableScriptSecurity = mkOption { type = types.bool; default = false; @@ -282,6 +290,19 @@ in assertions = flatten (map vrrpInstanceAssertions vrrpInstances); + networking.firewall = lib.mkIf cfg.openFirewall { + extraCommands = '' + # Allow VRRP and AH packets + ip46tables -A nixos-fw -p vrrp -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ip46tables -A nixos-fw -p ah -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ''; + + extraStopCommands = '' + ip46tables -D nixos-fw -p vrrp -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ip46tables -D nixos-fw -p ah -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ''; + }; + systemd.timers.keepalived-boot-delay = { description = "Keepalive Daemon delay to avoid instant transition to MASTER state"; after = [ "network.target" "network-online.target" "syslog.target" ]; diff --git a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix index 8686506b1c28..7b00ce35eb1a 100644 --- a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix +++ b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix @@ -229,7 +229,7 @@ in { streamingProcesses = lib.mkOption { description = lib.mdDoc '' Number of processes used by the mastodon-streaming service. - Recommended is the amount of your CPU cores minus one. + Please define this explicitly, recommended is the amount of your CPU cores minus one. ''; type = lib.types.ints.positive; example = 3; diff --git a/nixpkgs/nixos/modules/services/web-apps/netbox.nix b/nixpkgs/nixos/modules/services/web-apps/netbox.nix index 3b9434e3d345..88d40b3abc52 100644 --- a/nixpkgs/nixos/modules/services/web-apps/netbox.nix +++ b/nixpkgs/nixos/modules/services/web-apps/netbox.nix @@ -317,7 +317,7 @@ in { serviceConfig = defaultServiceConfig // { ExecStart = '' - ${pkgs.python3Packages.gunicorn}/bin/gunicorn netbox.wsgi \ + ${pkg.gunicorn}/bin/gunicorn netbox.wsgi \ --bind ${cfg.listenAddress}:${toString cfg.port} \ --pythonpath ${pkg}/opt/netbox/netbox ''; diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix index d775042fed16..de78f05a98ca 100644 --- a/nixpkgs/nixos/modules/services/web-apps/node-red.nix +++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix @@ -19,7 +19,7 @@ in options.services.node-red = { enable = mkEnableOption (lib.mdDoc "the Node-RED service"); - package = mkPackageOption pkgs "node-red" { }; + package = mkPackageOption pkgs "nodePackages.node-red" { }; openFirewall = mkOption { type = types.bool; diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix index a882bb140d21..e9cadf219468 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix @@ -200,8 +200,7 @@ in }) ]; - # https://salsa.debian.org/cinnamon-team/cinnamon/-/commit/f87c64f8d35ba406eb11ad442989a0716f6620cf# - xdg.portal.config.x-cinnamon.default = mkDefault [ "xapp" "gtk" ]; + xdg.portal.configPackages = mkDefault [ pkgs.cinnamon.cinnamon-common ]; # Override GSettings schemas environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-overrides}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas"; diff --git a/nixpkgs/nixos/modules/system/boot/clevis.md b/nixpkgs/nixos/modules/system/boot/clevis.md new file mode 100644 index 000000000000..91eb728a919e --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/clevis.md @@ -0,0 +1,51 @@ +# Clevis {#module-boot-clevis} + +[Clevis](https://github.com/latchset/clevis) +is a framework for automated decryption of resources. +Clevis allows for secure unattended disk decryption during boot, using decryption policies that must be satisfied for the data to decrypt. + + +## Create a JWE file containing your secret {#module-boot-clevis-create-secret} + +The first step is to embed your secret in a [JWE](https://en.wikipedia.org/wiki/JSON_Web_Encryption) file. +JWE files have to be created through the clevis command line. 3 types of policies are supported: + +1) TPM policies + +Secrets are pinned against the presence of a TPM2 device, for example: +``` +echo hi | clevis encrypt tpm2 '{}' > hi.jwe +``` +2) Tang policies + +Secrets are pinned against the presence of a Tang server, for example: +``` +echo hi | clevis encrypt tang '{"url": "http://tang.local"}' > hi.jwe +``` + +3) Shamir Secret Sharing + +Using Shamir's Secret Sharing ([sss](https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing)), secrets are pinned using a combination of the two preceding policies. For example: +``` +echo hi | clevis encrypt sss \ +'{"t": 2, "pins": {"tpm2": {"pcr_ids": "0"}, "tang": {"url": "http://tang.local"}}}' \ +> hi.jwe +``` + +For more complete documentation on how to generate a secret with clevis, see the [clevis documentation](https://github.com/latchset/clevis). + + +## Activate unattended decryption of a resource at boot {#module-boot-clevis-activate} + +In order to activate unattended decryption of a resource at boot, enable the `clevis` module: + +``` +boot.initrd.clevis.enable = true; +``` + +Then, specify the device you want to decrypt using a given clevis secret. Clevis will automatically try to decrypt the device at boot and will fallback to interactive unlocking if the decryption policy is not fulfilled. +``` +boot.initrd.clevis.devices."/dev/nvme0n1p1".secretFile = ./nvme0n1p1.jwe; +``` + +Only `bcachefs`, `zfs` and `luks` encrypted devices are supported at this time. diff --git a/nixpkgs/nixos/modules/system/boot/clevis.nix b/nixpkgs/nixos/modules/system/boot/clevis.nix new file mode 100644 index 000000000000..0c72590f9385 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/clevis.nix @@ -0,0 +1,107 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.boot.initrd.clevis; + systemd = config.boot.initrd.systemd; + supportedFs = [ "zfs" "bcachefs" ]; +in +{ + meta.maintainers = with maintainers; [ julienmalka camillemndn ]; + meta.doc = ./clevis.md; + + options = { + boot.initrd.clevis.enable = mkEnableOption (lib.mdDoc "Clevis in initrd"); + + + boot.initrd.clevis.package = mkOption { + type = types.package; + default = pkgs.clevis; + defaultText = "pkgs.clevis"; + description = lib.mdDoc "Clevis package"; + }; + + boot.initrd.clevis.devices = mkOption { + description = "Encrypted devices that need to be unlocked at boot using Clevis"; + default = { }; + type = types.attrsOf (types.submodule ({ + options.secretFile = mkOption { + description = lib.mdDoc "Clevis JWE file used to decrypt the device at boot, in concert with the chosen pin (one of TPM2, Tang server, or SSS)."; + type = types.path; + }; + })); + }; + + boot.initrd.clevis.useTang = mkOption { + description = "Whether the Clevis JWE file used to decrypt the devices uses a Tang server as a pin."; + default = false; + type = types.bool; + }; + + }; + + config = mkIf cfg.enable { + + # Implementation of clevis unlocking for the supported filesystems are located directly in the respective modules. + + + assertions = (attrValues (mapAttrs + (device: _: { + assertion = (any (fs: fs.device == device && (elem fs.fsType supportedFs)) config.system.build.fileSystems) || (hasAttr device config.boot.initrd.luks.devices); + message = '' + No filesystem or LUKS device with the name ${device} is declared in your configuration.''; + }) + cfg.devices)); + + + warnings = + if cfg.useTang && !config.boot.initrd.network.enable && !config.boot.initrd.systemd.network.enable + then [ "In order to use a Tang pinned secret you must configure networking in initrd" ] + else [ ]; + + boot.initrd = { + extraUtilsCommands = mkIf (!systemd.enable) '' + copy_bin_and_libs ${pkgs.jose}/bin/jose + copy_bin_and_libs ${pkgs.curl}/bin/curl + copy_bin_and_libs ${pkgs.bash}/bin/bash + + copy_bin_and_libs ${pkgs.tpm2-tools}/bin/.tpm2-wrapped + mv $out/bin/{.tpm2-wrapped,tpm2} + cp {${pkgs.tpm2-tss},$out}/lib/libtss2-tcti-device.so.0 + + copy_bin_and_libs ${cfg.package}/bin/.clevis-wrapped + mv $out/bin/{.clevis-wrapped,clevis} + + for BIN in ${cfg.package}/bin/clevis-decrypt*; do + copy_bin_and_libs $BIN + done + + for BIN in $out/bin/clevis{,-decrypt{,-null,-tang,-tpm2}}; do + sed -i $BIN -e 's,${pkgs.bash},,' -e 's,${pkgs.coreutils},,' + done + + sed -i $out/bin/clevis-decrypt-tpm2 -e 's,tpm2_,tpm2 ,' + ''; + + secrets = lib.mapAttrs' (name: value: nameValuePair "/etc/clevis/${name}.jwe" value.secretFile) cfg.devices; + + systemd = { + extraBin = mkIf systemd.enable { + clevis = "${cfg.package}/bin/clevis"; + curl = "${pkgs.curl}/bin/curl"; + }; + + storePaths = mkIf systemd.enable [ + cfg.package + "${pkgs.jose}/bin/jose" + "${pkgs.curl}/bin/curl" + "${pkgs.tpm2-tools}/bin/tpm2_createprimary" + "${pkgs.tpm2-tools}/bin/tpm2_flushcontext" + "${pkgs.tpm2-tools}/bin/tpm2_load" + "${pkgs.tpm2-tools}/bin/tpm2_unseal" + ]; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/luksroot.nix b/nixpkgs/nixos/modules/system/boot/luksroot.nix index ca560d63f3bd..8bd9e71cb3a9 100644 --- a/nixpkgs/nixos/modules/system/boot/luksroot.nix +++ b/nixpkgs/nixos/modules/system/boot/luksroot.nix @@ -1,9 +1,11 @@ -{ config, options, lib, pkgs, ... }: +{ config, options, lib, utils, pkgs, ... }: with lib; let luks = config.boot.initrd.luks; + clevis = config.boot.initrd.clevis; + systemd = config.boot.initrd.systemd; kernelPackages = config.boot.kernelPackages; defaultPrio = (mkOptionDefault {}).priority; @@ -594,7 +596,7 @@ in ''; type = with types; attrsOf (submodule ( - { name, ... }: { options = { + { config, name, ... }: { options = { name = mkOption { visible = false; @@ -894,6 +896,19 @@ in ''; }; }; + + config = mkIf (clevis.enable && (hasAttr name clevis.devices)) { + preOpenCommands = mkIf (!systemd.enable) '' + mkdir -p /clevis-${name} + mount -t ramfs none /clevis-${name} + clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted + ''; + keyFile = "/clevis-${name}/decrypted"; + fallbackToPassword = !systemd.enable; + postOpenCommands = mkIf (!systemd.enable) '' + umount /clevis-${name} + ''; + }; })); }; @@ -1081,6 +1096,35 @@ in boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands); boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands); + boot.initrd.systemd.services = let devicesWithClevis = filterAttrs (device: _: (hasAttr device clevis.devices)) luks.devices; in + mkIf (clevis.enable && systemd.enable) ( + (mapAttrs' + (name: _: nameValuePair "cryptsetup-clevis-${name}" { + wantedBy = [ "systemd-cryptsetup@${utils.escapeSystemdPath name}.service" ]; + before = [ + "systemd-cryptsetup@${utils.escapeSystemdPath name}.service" + "initrd-switch-root.target" + "shutdown.target" + ]; + wants = [ "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target"; + after = [ "systemd-modules-load.service" "systemd-udev-settle.service" ] ++ optional clevis.useTang "network-online.target"; + script = '' + mkdir -p /clevis-${name} + mount -t ramfs none /clevis-${name} + umask 277 + clevis decrypt < /etc/clevis/${name}.jwe > /clevis-${name}/decrypted + ''; + conflicts = [ "initrd-switch-root.target" "shutdown.target" ]; + unitConfig.DefaultDependencies = "no"; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "${config.boot.initrd.systemd.package.util-linux}/bin/umount /clevis-${name}"; + }; + }) + devicesWithClevis) + ); + environment.systemPackages = [ pkgs.cryptsetup ]; }; } diff --git a/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix b/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix index f28fd5cde9c1..639ff87841b6 100644 --- a/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix +++ b/nixpkgs/nixos/modules/tasks/filesystems/bcachefs.nix @@ -57,7 +57,15 @@ let # bcachefs does not support mounting devices with colons in the path, ergo we don't (see #49671) firstDevice = fs: lib.head (lib.splitString ":" fs.device); - openCommand = name: fs: '' + openCommand = name: fs: if config.boot.initrd.clevis.enable && (lib.hasAttr (firstDevice fs) config.boot.initrd.clevis.devices) then '' + if clevis decrypt < /etc/clevis/${firstDevice fs}.jwe | bcachefs unlock ${firstDevice fs} + then + printf "unlocked ${name} using clevis\n" + else + printf "falling back to interactive unlocking...\n" + tryUnlock ${name} ${firstDevice fs} + fi + '' else '' tryUnlock ${name} ${firstDevice fs} ''; diff --git a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix index 72bc79f31b68..784040f0ce9e 100644 --- a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix @@ -17,6 +17,9 @@ let cfgZED = config.services.zfs.zed; selectModulePackage = package: config.boot.kernelPackages.${package.kernelModuleAttribute}; + clevisDatasets = map (e: e.device) (filter (e: e.device != null && (hasAttr e.device config.boot.initrd.clevis.devices) && e.fsType == "zfs" && (fsNeededForBoot e)) config.system.build.fileSystems); + + inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems; inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems; @@ -120,12 +123,12 @@ let # but don't *require* it, because mounts shouldn't be killed if it's stopped. # In the future, hopefully someone will complete this: # https://github.com/zfsonlinux/zfs/pull/4943 - wants = [ "systemd-udev-settle.service" ]; + wants = [ "systemd-udev-settle.service" ] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target"; after = [ "systemd-udev-settle.service" "systemd-modules-load.service" "systemd-ask-password-console.service" - ]; + ] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target"; requiredBy = getPoolMounts prefix pool ++ [ "zfs-import.target" ]; before = getPoolMounts prefix pool ++ [ "zfs-import.target" ]; unitConfig = { @@ -154,6 +157,9 @@ let poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool. fi if poolImported "${pool}"; then + ${optionalString config.boot.initrd.clevis.enable (concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem} || true ") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets))} + + ${optionalString keyLocations.hasKeys '' ${keyLocations.command} | while IFS=$'\t' read ds kl ks; do { @@ -623,6 +629,9 @@ in fi poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool. fi + + ${optionalString config.boot.initrd.clevis.enable (concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem}") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets))} + ${if isBool cfgZfs.requestEncryptionCredentials then optionalString cfgZfs.requestEncryptionCredentials '' zfs load-key -a diff --git a/nixpkgs/nixos/tests/all-terminfo.nix b/nixpkgs/nixos/tests/all-terminfo.nix index dd47c66ee1c1..2f5e56f09f26 100644 --- a/nixpkgs/nixos/tests/all-terminfo.nix +++ b/nixpkgs/nixos/tests/all-terminfo.nix @@ -10,7 +10,11 @@ import ./make-test-python.nix ({ pkgs, ... }: rec { let o = builtins.tryEval drv; in - o.success && lib.isDerivation o.value && o.value ? outputs && builtins.elem "terminfo" o.value.outputs; + o.success && + lib.isDerivation o.value && + o.value ? outputs && + builtins.elem "terminfo" o.value.outputs && + !o.value.meta.broken; terminfos = lib.filterAttrs infoFilter pkgs; excludedTerminfos = lib.filterAttrs (_: drv: !(builtins.elem drv.terminfo config.environment.systemPackages)) terminfos; includedOuts = lib.filterAttrs (_: drv: builtins.elem drv.out config.environment.systemPackages) terminfos; diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix index 1ed0f760c9a2..e0572e3bed9c 100644 --- a/nixpkgs/nixos/tests/all-tests.nix +++ b/nixpkgs/nixos/tests/all-tests.nix @@ -135,6 +135,7 @@ in { authelia = handleTest ./authelia.nix {}; avahi = handleTest ./avahi.nix {}; avahi-with-resolved = handleTest ./avahi.nix { networkd = true; }; + ayatana-indicators = handleTest ./ayatana-indicators.nix {}; babeld = handleTest ./babeld.nix {}; bazarr = handleTest ./bazarr.nix {}; bcachefs = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bcachefs.nix {}; @@ -187,6 +188,7 @@ in { chrony = handleTestOn ["aarch64-linux" "x86_64-linux"] ./chrony.nix {}; chrony-ptp = handleTestOn ["aarch64-linux" "x86_64-linux"] ./chrony-ptp.nix {}; cinnamon = handleTest ./cinnamon.nix {}; + cinnamon-wayland = handleTest ./cinnamon-wayland.nix {}; cjdns = handleTest ./cjdns.nix {}; clickhouse = handleTest ./clickhouse.nix {}; cloud-init = handleTest ./cloud-init.nix {}; @@ -357,6 +359,7 @@ in { grow-partition = runTest ./grow-partition.nix; grub = handleTest ./grub.nix {}; guacamole-server = handleTest ./guacamole-server.nix {}; + guix = handleTest ./guix {}; gvisor = handleTest ./gvisor.nix {}; hadoop = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop; }; hadoop_3_2 = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop_3_2; }; diff --git a/nixpkgs/nixos/tests/auth-mysql.nix b/nixpkgs/nixos/tests/auth-mysql.nix index 0ed4b050a69a..77a69eb1cd58 100644 --- a/nixpkgs/nixos/tests/auth-mysql.nix +++ b/nixpkgs/nixos/tests/auth-mysql.nix @@ -84,7 +84,7 @@ in getpwuid = '' SELECT name, 'x', uid, gid, name, CONCAT('/home/', name), "/run/current-system/sw/bin/bash" \ FROM users \ - WHERE id=%1$u \ + WHERE uid=%1$u \ LIMIT 1 ''; getspnam = '' @@ -140,6 +140,7 @@ in machine.wait_for_unit("multi-user.target") machine.wait_for_unit("mysql.service") + machine.wait_until_succeeds("cat /etc/security/pam_mysql.conf | grep users.db_passwd") machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'") with subtest("Local login"): diff --git a/nixpkgs/nixos/tests/ayatana-indicators.nix b/nixpkgs/nixos/tests/ayatana-indicators.nix new file mode 100644 index 000000000000..bc7ff75f390f --- /dev/null +++ b/nixpkgs/nixos/tests/ayatana-indicators.nix @@ -0,0 +1,71 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: let + user = "alice"; +in { + name = "ayatana-indicators"; + + meta = { + maintainers = with lib.maintainers; [ OPNA2608 ]; + }; + + nodes.machine = { config, ... }: { + imports = [ + ./common/auto.nix + ./common/user-account.nix + ]; + + test-support.displayManager.auto = { + enable = true; + inherit user; + }; + + services.xserver = { + enable = true; + desktopManager.mate.enable = true; + displayManager.defaultSession = lib.mkForce "mate"; + }; + + services.ayatana-indicators = { + enable = true; + packages = with pkgs; [ + ayatana-indicator-messages + ]; + }; + + # Services needed by some indicators + services.accounts-daemon.enable = true; # messages + }; + + # TODO session indicator starts up in a semi-broken state, but works fine after a restart. maybe being started before graphical session is truly up & ready? + testScript = { nodes, ... }: let + runCommandPerIndicatorService = command: lib.strings.concatMapStringsSep "\n" command nodes.machine.systemd.user.targets."ayatana-indicators".wants; + in '' + start_all() + machine.wait_for_x() + + # Desktop environment should reach graphical-session.target + machine.wait_for_unit("graphical-session.target", "${user}") + + # MATE relies on XDG autostart to bring up the indicators. + # Not sure *when* XDG autostart fires them up, and awaiting pgrep success seems to misbehave? + machine.sleep(10) + + # Now check if all indicators were brought up successfully, and kill them for later + '' + (runCommandPerIndicatorService (service: let serviceExec = builtins.replaceStrings [ "." ] [ "-" ] service; in '' + machine.succeed("pgrep -f ${serviceExec}") + machine.succeed("pkill -f ${serviceExec}") + '')) + '' + + # Ayatana target is the preferred way of starting up indicators on SystemD session, the graphical session is responsible for starting this if it supports them. + # Mate currently doesn't do this, so start it manually for checking (https://github.com/mate-desktop/mate-indicator-applet/issues/63) + machine.systemctl("start ayatana-indicators.target", "${user}") + machine.wait_for_unit("ayatana-indicators.target", "${user}") + + # Let all indicator services do their startups, potential post-launch crash & restart cycles so we can properly check for failures + # Not sure if there's a better way of awaiting this without false-positive potential + machine.sleep(10) + + # Now check if all indicator services were brought up successfully + '' + runCommandPerIndicatorService (service: '' + machine.wait_for_unit("${service}", "${user}") + ''); +}) diff --git a/nixpkgs/nixos/tests/cinnamon-wayland.nix b/nixpkgs/nixos/tests/cinnamon-wayland.nix new file mode 100644 index 000000000000..58dddbbb0866 --- /dev/null +++ b/nixpkgs/nixos/tests/cinnamon-wayland.nix @@ -0,0 +1,71 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "cinnamon-wayland"; + + meta.maintainers = lib.teams.cinnamon.members; + + nodes.machine = { nodes, ... }: { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.desktopManager.cinnamon.enable = true; + services.xserver.displayManager = { + autoLogin.enable = true; + autoLogin.user = nodes.machine.users.users.alice.name; + defaultSession = "cinnamon-wayland"; + }; + }; + + enableOCR = true; + + testScript = { nodes, ... }: + let + user = nodes.machine.users.users.alice; + env = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${toString user.uid}/bus"; + su = command: "su - ${user.name} -c '${env} ${command}'"; + + # Call javascript in cinnamon (the shell), returns a tuple (success, output), + # where `success` is true if the dbus call was successful and `output` is what + # the javascript evaluates to. + eval = name: su "gdbus call --session -d org.Cinnamon -o /org/Cinnamon -m org.Cinnamon.Eval ${name}"; + in + '' + machine.wait_for_unit("display-manager.service") + + with subtest("Wait for wayland server"): + machine.wait_for_file("/run/user/${toString user.uid}/wayland-0") + + with subtest("Check that logging in has given the user ownership of devices"): + machine.succeed("getfacl -p /dev/snd/timer | grep -q ${user.name}") + + with subtest("Wait for the Cinnamon shell"): + # Correct output should be (true, '2') + # https://github.com/linuxmint/cinnamon/blob/5.4.0/js/ui/main.js#L183-L187 + machine.wait_until_succeeds("${eval "Main.runState"} | grep -q 'true,..2'") + + with subtest("Check if Cinnamon components actually start"): + for i in ["csd-media-keys", "xapp-sn-watcher", "nemo-desktop"]: + machine.wait_until_succeeds(f"pgrep -f {i}") + machine.wait_until_succeeds("journalctl -b --grep 'Loaded applet menu@cinnamon.org'") + machine.wait_until_succeeds("journalctl -b --grep 'calendar@cinnamon.org: Calendar events supported'") + + with subtest("Open Cinnamon Settings"): + machine.succeed("${su "cinnamon-settings themes >&2 &"}") + machine.wait_until_succeeds("${eval "global.display.focus_window.wm_class"} | grep -i 'cinnamon-settings'") + machine.wait_for_text('(Style|Appearance|Color)') + machine.sleep(2) + machine.screenshot("cinnamon_settings") + + with subtest("Check if screensaver works"): + # This is not supported at the moment. + # https://trello.com/b/HHs01Pab/cinnamon-wayland + machine.execute("${su "cinnamon-screensaver-command -l >&2 &"}") + machine.wait_until_succeeds("journalctl -b --grep 'Cinnamon Screensaver is unavailable on Wayland'") + + with subtest("Open GNOME Terminal"): + machine.succeed("${su "dbus-launch gnome-terminal"}") + machine.wait_until_succeeds("${eval "global.display.focus_window.wm_class"} | grep -i 'gnome-terminal'") + machine.sleep(2) + + with subtest("Check if Cinnamon has ever coredumped"): + machine.fail("coredumpctl --json=short | grep -E 'cinnamon|nemo'") + ''; +}) diff --git a/nixpkgs/nixos/tests/guix/basic.nix b/nixpkgs/nixos/tests/guix/basic.nix new file mode 100644 index 000000000000..7f90bdeeb1e0 --- /dev/null +++ b/nixpkgs/nixos/tests/guix/basic.nix @@ -0,0 +1,38 @@ +# Take note the Guix store directory is empty. Also, we're trying to prevent +# Guix from trying to downloading substitutes because of the restricted +# access (assuming it's in a sandboxed environment). +# +# So this test is what it is: a basic test while trying to use Guix as much as +# we possibly can (including the API) without triggering its download alarm. + +import ../make-test-python.nix ({ lib, pkgs, ... }: { + name = "guix-basic"; + meta.maintainers = with lib.maintainers; [ foo-dogsquared ]; + + nodes.machine = { config, ... }: { + environment.etc."guix/scripts".source = ./scripts; + services.guix.enable = true; + }; + + testScript = '' + import pathlib + + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("guix-daemon.service") + + # Can't do much here since the environment has restricted network access. + with subtest("Guix basic package management"): + machine.succeed("guix build --dry-run --verbosity=0 hello") + machine.succeed("guix show hello") + + # This is to see if the Guix API is usable and mostly working. + with subtest("Guix API scripting"): + scripts_dir = pathlib.Path("/etc/guix/scripts") + + text_msg = "Hello there, NixOS!" + text_store_file = machine.succeed(f"guix repl -- {scripts_dir}/create-file-to-store.scm '{text_msg}'") + assert machine.succeed(f"cat {text_store_file}") == text_msg + + machine.succeed(f"guix repl -- {scripts_dir}/add-existing-files-to-store.scm {scripts_dir}") + ''; +}) diff --git a/nixpkgs/nixos/tests/guix/default.nix b/nixpkgs/nixos/tests/guix/default.nix new file mode 100644 index 000000000000..a017668c05a7 --- /dev/null +++ b/nixpkgs/nixos/tests/guix/default.nix @@ -0,0 +1,8 @@ +{ system ? builtins.currentSystem +, pkgs ? import ../../.. { inherit system; } +}: + +{ + basic = import ./basic.nix { inherit system pkgs; }; + publish = import ./publish.nix { inherit system pkgs; }; +} diff --git a/nixpkgs/nixos/tests/guix/publish.nix b/nixpkgs/nixos/tests/guix/publish.nix new file mode 100644 index 000000000000..6dbe8f99ebd6 --- /dev/null +++ b/nixpkgs/nixos/tests/guix/publish.nix @@ -0,0 +1,95 @@ +# Testing out the substitute server with two machines in a local network. As a +# bonus, we'll also test a feature of the substitute server being able to +# advertise its service to the local network with Avahi. + +import ../make-test-python.nix ({ pkgs, lib, ... }: let + publishPort = 8181; + inherit (builtins) toString; +in { + name = "guix-publish"; + + meta.maintainers = with lib.maintainers; [ foo-dogsquared ]; + + nodes = let + commonConfig = { config, ... }: { + # We'll be using '--advertise' flag with the + # substitute server which requires Avahi. + services.avahi = { + enable = true; + nssmdns = true; + publish = { + enable = true; + userServices = true; + }; + }; + }; + in { + server = { config, lib, pkgs, ... }: { + imports = [ commonConfig ]; + + services.guix = { + enable = true; + publish = { + enable = true; + port = publishPort; + + generateKeyPair = true; + extraArgs = [ "--advertise" ]; + }; + }; + + networking.firewall.allowedTCPPorts = [ publishPort ]; + }; + + client = { config, lib, pkgs, ... }: { + imports = [ commonConfig ]; + + services.guix = { + enable = true; + + extraArgs = [ + # Force to only get all substitutes from the local server. We don't + # have anything in the Guix store directory and we cannot get + # anything from the official substitute servers anyways. + "--substitute-urls='http://server.local:${toString publishPort}'" + + # Enable autodiscovery of the substitute servers in the local + # network. This machine shouldn't need to import the signing key from + # the substitute server since it is automatically done anyways. + "--discover=yes" + ]; + }; + }; + }; + + testScript = '' + import pathlib + + start_all() + + scripts_dir = pathlib.Path("/etc/guix/scripts") + + for machine in machines: + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("guix-daemon.service") + machine.wait_for_unit("avahi-daemon.service") + + server.wait_for_unit("guix-publish.service") + server.wait_for_open_port(${toString publishPort}) + server.succeed("curl http://localhost:${toString publishPort}/") + + # Now it's the client turn to make use of it. + substitute_server = "http://server.local:${toString publishPort}" + client.wait_for_unit("network-online.target") + response = client.succeed(f"curl {substitute_server}") + assert "Guix Substitute Server" in response + + # Authorizing the server to be used as a substitute server. + client.succeed(f"curl -O {substitute_server}/signing-key.pub") + client.succeed("guix archive --authorize < ./signing-key.pub") + + # Since we're using the substitute server with the `--advertise` flag, we + # might as well check it. + client.succeed("avahi-browse --resolve --terminate _guix_publish._tcp | grep '_guix_publish._tcp'") + ''; +}) diff --git a/nixpkgs/nixos/tests/guix/scripts/add-existing-files-to-store.scm b/nixpkgs/nixos/tests/guix/scripts/add-existing-files-to-store.scm new file mode 100644 index 000000000000..fa47320b6a51 --- /dev/null +++ b/nixpkgs/nixos/tests/guix/scripts/add-existing-files-to-store.scm @@ -0,0 +1,52 @@ +;; A simple script that adds each file given from the command-line into the +;; store and checks them if it's the same. +(use-modules (guix) + (srfi srfi-1) + (ice-9 ftw) + (rnrs io ports)) + +;; This is based from tests/derivations.scm from Guix source code. +(define* (directory-contents dir #:optional (slurp get-bytevector-all)) + "Return an alist representing the contents of DIR" + (define prefix-len (string-length dir)) + (sort (file-system-fold (const #t) + (lambda (path stat result) + (alist-cons (string-drop path prefix-len) + (call-with-input-file path slurp) + result)) + (lambda (path stat result) result) + (lambda (path stat result) result) + (lambda (path stat result) result) + (lambda (path stat errno result) result) + '() + dir) + (lambda (e1 e2) + (string<? (car e1) (car e2))))) + +(define* (check-if-same store drv path) + "Check if the given path and its store item are the same" + (let* ((filetype (stat:type (stat drv)))) + (case filetype + ((regular) + (and (valid-path? store drv) + (equal? (call-with-input-file path get-bytevector-all) + (call-with-input-file drv get-bytevector-all)))) + ((directory) + (and (valid-path? store drv) + (equal? (directory-contents path) + (directory-contents drv)))) + (else #f)))) + +(define* (add-and-check-item-to-store store path) + "Add PATH to STORE and check if the contents are the same" + (let* ((store-item (add-to-store store + (basename path) + #t "sha256" path)) + (is-same (check-if-same store store-item path))) + (if (not is-same) + (exit 1)))) + +(with-store store + (map (lambda (path) + (add-and-check-item-to-store store (readlink* path))) + (cdr (command-line)))) diff --git a/nixpkgs/nixos/tests/guix/scripts/create-file-to-store.scm b/nixpkgs/nixos/tests/guix/scripts/create-file-to-store.scm new file mode 100644 index 000000000000..467e4c4fd53f --- /dev/null +++ b/nixpkgs/nixos/tests/guix/scripts/create-file-to-store.scm @@ -0,0 +1,8 @@ +;; A script that creates a store item with the given text and prints the +;; resulting store item path. +(use-modules (guix)) + +(with-store store + (display (add-text-to-store store "guix-basic-test-text" + (string-join + (cdr (command-line)))))) diff --git a/nixpkgs/nixos/tests/input-remapper.nix b/nixpkgs/nixos/tests/input-remapper.nix index 1b0350063f7f..2ef55a01b290 100644 --- a/nixpkgs/nixos/tests/input-remapper.nix +++ b/nixpkgs/nixos/tests/input-remapper.nix @@ -46,7 +46,8 @@ import ./make-test-python.nix ({ pkgs, ... }: machine.execute("su - sybil -c input-remapper-gtk >&2 &") machine.wait_for_text("Input Remapper") - machine.wait_for_text("Preset") - machine.wait_for_text("Change Key") + machine.wait_for_text("Device") + machine.wait_for_text("Presets") + machine.wait_for_text("Editor") ''; }) diff --git a/nixpkgs/nixos/tests/installer-systemd-stage-1.nix b/nixpkgs/nixos/tests/installer-systemd-stage-1.nix index 1b4c92b584b9..d0c01a779ef1 100644 --- a/nixpkgs/nixos/tests/installer-systemd-stage-1.nix +++ b/nixpkgs/nixos/tests/installer-systemd-stage-1.nix @@ -32,6 +32,10 @@ stratisRoot swraid zfsroot + clevisLuks + clevisLuksFallback + clevisZfs + clevisZfsFallback ; } diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix index e9ec28749850..f7fc168eba8c 100644 --- a/nixpkgs/nixos/tests/installer.nix +++ b/nixpkgs/nixos/tests/installer.nix @@ -12,6 +12,7 @@ let # The configuration to install. makeConfig = { bootLoader, grubDevice, grubIdentifier, grubUseEfi , extraConfig, forceGrubReinstallCount ? 0, flake ? false + , clevisTest }: pkgs.writeText "configuration.nix" '' { config, lib, pkgs, modulesPath, ... }: @@ -52,6 +53,15 @@ let boot.initrd.secrets."/etc/secret" = ./secret; + ${optionalString clevisTest '' + boot.kernelParams = [ "console=tty0" "ip=192.168.1.1:::255.255.255.0::eth1:none" ]; + boot.initrd = { + availableKernelModules = [ "tpm_tis" ]; + clevis = { enable = true; useTang = true; }; + network.enable = true; + }; + ''} + users.users.alice = { isNormalUser = true; home = "/home/alice"; @@ -71,7 +81,7 @@ let # partitions and filesystems. testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi, grubIdentifier , postInstallCommands, preBootCommands, postBootCommands, extraConfig - , testSpecialisationConfig, testFlakeSwitch + , testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest }: let iface = "virtio"; isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); @@ -79,12 +89,16 @@ let in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then '' machine.succeed("true") '' else '' + import subprocess + tpm_folder = os.environ['NIX_BUILD_TOP'] def assemble_qemu_flags(): flags = "-cpu max" ${if (system == "x86_64-linux" || system == "i686-linux") then ''flags += " -m 1024"'' else ''flags += " -m 768 -enable-kvm -machine virt,gic-version=host"'' } + ${optionalString clevisTest ''flags += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"''} + ${optionalString clevisTest ''flags += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""''} return flags @@ -110,8 +124,45 @@ let def create_machine_named(name): return create_machine({**default_flags, "name": name}) + class Tpm: + def __init__(self): + self.start() + + def start(self): + self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", + "socket", + "--tpmstate", f"dir={tpm_folder}/swtpm", + "--ctrl", f"type=unixio,path={tpm_folder}/swtpm-sock", + "--tpm2" + ]) + + # Check whether starting swtpm failed + try: + exit_code = self.proc.wait(timeout=0.2) + if exit_code is not None and exit_code != 0: + raise Exception("failed to start swtpm") + except subprocess.TimeoutExpired: + pass + + """Check whether the swtpm process exited due to an error""" + def check(self): + exit_code = self.proc.poll() + if exit_code is not None and exit_code != 0: + raise Exception("swtpm process died") + + + os.mkdir(f"{tpm_folder}/swtpm") + tpm = Tpm() + tpm.check() + + start_all() + ${optionalString clevisTest '' + tang.wait_for_unit("sockets.target") + tang.wait_for_unit("network-online.target") + machine.wait_for_unit("network-online.target") + ''} + machine.wait_for_unit("multi-user.target") - machine.start() with subtest("Assert readiness of login prompt"): machine.succeed("echo hello") @@ -127,13 +178,23 @@ let machine.copy_from_host( "${ makeConfig { inherit bootLoader grubDevice grubIdentifier - grubUseEfi extraConfig; + grubUseEfi extraConfig clevisTest; } }", "/mnt/etc/nixos/configuration.nix", ) machine.copy_from_host("${pkgs.writeText "secret" "secret"}", "/mnt/etc/nixos/secret") + ${optionalString clevisTest '' + with subtest("Create the Clevis secret with Tang"): + machine.wait_for_unit("network-online.target") + machine.succeed('echo -n password | clevis encrypt sss \'{"t": 2, "pins": {"tpm2": {}, "tang": {"url": "http://192.168.1.2"}}}\' -y > /mnt/etc/nixos/clevis-secret.jwe')''} + + ${optionalString clevisFallbackTest '' + with subtest("Shutdown Tang to check fallback to interactive prompt"): + tang.shutdown() + ''} + with subtest("Perform the installation"): machine.succeed("nixos-install < /dev/null >&2") @@ -200,7 +261,7 @@ let machine.copy_from_host_via_shell( "${ makeConfig { inherit bootLoader grubDevice grubIdentifier - grubUseEfi extraConfig; + grubUseEfi extraConfig clevisTest; forceGrubReinstallCount = 1; } }", @@ -229,7 +290,7 @@ let machine.copy_from_host_via_shell( "${ makeConfig { inherit bootLoader grubDevice grubIdentifier - grubUseEfi extraConfig; + grubUseEfi extraConfig clevisTest; forceGrubReinstallCount = 2; } }", @@ -303,7 +364,7 @@ let """) machine.copy_from_host_via_shell( "${makeConfig { - inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig; + inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig clevisTest; forceGrubReinstallCount = 1; flake = true; }}", @@ -379,6 +440,8 @@ let , enableOCR ? false, meta ? {} , testSpecialisationConfig ? false , testFlakeSwitch ? false + , clevisTest ? false + , clevisFallbackTest ? false }: makeTest { inherit enableOCR; @@ -416,13 +479,13 @@ let virtualisation.rootDevice = "/dev/vdb"; virtualisation.bootLoaderDevice = "/dev/vda"; virtualisation.qemu.diskInterface = "virtio"; - - # We don't want to have any networking in the guest whatsoever. - # Also, if any vlans are enabled, the guest will reboot - # (with a different configuration for legacy reasons), - # and spend 5 minutes waiting for the vlan interface to show up - # (which will never happen). - virtualisation.vlans = []; + virtualisation.qemu.options = mkIf (clevisTest) [ + "-chardev socket,id=chrtpm,path=$NIX_BUILD_TOP/swtpm-sock" + "-tpmdev emulator,id=tpm0,chardev=chrtpm" + "-device tpm-tis,tpmdev=tpm0" + ]; + # We don't want to have any networking in the guest apart from the clevis tests. + virtualisation.vlans = mkIf (!clevisTest) []; boot.loader.systemd-boot.enable = mkIf (bootLoader == "systemd-boot") true; @@ -471,7 +534,7 @@ let in [ (pkgs.grub2.override { inherit zfsSupport; }) (pkgs.grub2_efi.override { inherit zfsSupport; }) - ]); + ]) ++ optionals clevisTest [ pkgs.klibc ]; nix.settings = { substituters = mkForce []; @@ -480,12 +543,21 @@ let }; }; + } // optionalAttrs clevisTest { + tang = { + services.tang = { + enable = true; + listenStream = [ "80" ]; + ipAddressAllow = [ "192.168.1.0/24" ]; + }; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; }; testScript = testScriptFun { inherit bootLoader createPartitions postInstallCommands preBootCommands postBootCommands grubDevice grubIdentifier grubUseEfi extraConfig - testSpecialisationConfig testFlakeSwitch; + testSpecialisationConfig testFlakeSwitch clevisTest clevisFallbackTest; }; }; @@ -586,6 +658,145 @@ let zfs = super.zfs.overrideAttrs(_: {meta.platforms = [];});} )]; }; + + mkClevisBcachefsTest = { fallback ? false }: makeInstallerTest "clevis-bcachefs${optionalString fallback "-fallback"}" { + clevisTest = true; + clevisFallbackTest = fallback; + enableOCR = fallback; + extraInstallerConfig = { + imports = [ no-zfs-module ]; + boot.supportedFilesystems = [ "bcachefs" ]; + environment.systemPackages = with pkgs; [ keyutils clevis ]; + }; + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" + + " mkpart primary linux-swap 100M 1024M" + + " mkpart primary 1024M -1s", + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "keyctl link @u @s", + "echo -n password | mkfs.bcachefs -L root --encrypted /dev/vda3", + "echo -n password | bcachefs unlock /dev/vda3", + "echo -n password | mount -t bcachefs /dev/vda3 /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + "udevadm settle") + ''; + extraConfig = '' + boot.initrd.clevis.devices."/dev/vda3".secretFile = "/etc/nixos/clevis-secret.jwe"; + + # We override what nixos-generate-config has generated because we do + # not know the UUID in advance. + fileSystems."/" = lib.mkForce { device = "/dev/vda3"; fsType = "bcachefs"; }; + ''; + preBootCommands = '' + tpm = Tpm() + tpm.check() + '' + optionalString fallback '' + machine.start() + machine.wait_for_text("enter passphrase for") + machine.send_chars("password\n") + ''; + }; + + mkClevisLuksTest = { fallback ? false }: makeInstallerTest "clevis-luks${optionalString fallback "-fallback"}" { + clevisTest = true; + clevisFallbackTest = fallback; + enableOCR = fallback; + extraInstallerConfig = { + environment.systemPackages = with pkgs; [ clevis ]; + }; + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" + + " mkpart primary linux-swap 100M 1024M" + + " mkpart primary 1024M -1s", + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "modprobe dm_mod dm_crypt", + "echo -n password | cryptsetup luksFormat -q /dev/vda3 -", + "echo -n password | cryptsetup luksOpen --key-file - /dev/vda3 crypt-root", + "mkfs.ext3 -L nixos /dev/mapper/crypt-root", + "mount LABEL=nixos /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + "udevadm settle") + ''; + extraConfig = '' + boot.initrd.clevis.devices."crypt-root".secretFile = "/etc/nixos/clevis-secret.jwe"; + ''; + preBootCommands = '' + tpm = Tpm() + tpm.check() + '' + optionalString fallback '' + machine.start() + ${if systemdStage1 then '' + machine.wait_for_text("Please enter") + '' else '' + machine.wait_for_text("Passphrase for") + ''} + machine.send_chars("password\n") + ''; + }; + + mkClevisZfsTest = { fallback ? false }: makeInstallerTest "clevis-zfs${optionalString fallback "-fallback"}" { + clevisTest = true; + clevisFallbackTest = fallback; + enableOCR = fallback; + extraInstallerConfig = { + boot.supportedFilesystems = [ "zfs" ]; + environment.systemPackages = with pkgs; [ clevis ]; + }; + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" + + " mkpart primary linux-swap 100M 1024M" + + " mkpart primary 1024M -1s", + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "zpool create -O mountpoint=legacy rpool /dev/vda3", + "echo -n password | zfs create" + + " -o encryption=aes-256-gcm -o keyformat=passphrase rpool/root", + "mount -t zfs rpool/root /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount LABEL=boot /mnt/boot", + "udevadm settle") + ''; + extraConfig = '' + boot.initrd.clevis.devices."rpool/root".secretFile = "/etc/nixos/clevis-secret.jwe"; + boot.zfs.requestEncryptionCredentials = true; + + + # Using by-uuid overrides the default of by-id, and is unique + # to the qemu disks, as they don't produce by-id paths for + # some reason. + boot.zfs.devNodes = "/dev/disk/by-uuid/"; + networking.hostId = "00000000"; + ''; + preBootCommands = '' + tpm = Tpm() + tpm.check() + '' + optionalString fallback '' + machine.start() + ${if systemdStage1 then '' + machine.wait_for_text("Enter key for rpool/root") + '' else '' + machine.wait_for_text("Key load error") + ''} + machine.send_chars("password\n") + ''; + }; + in { # !!! `parted mkpart' seems to silently create overlapping partitions. @@ -1175,6 +1386,13 @@ in { ) ''; }; +} // { + clevisBcachefs = mkClevisBcachefsTest { }; + clevisBcachefsFallback = mkClevisBcachefsTest { fallback = true; }; + clevisLuks = mkClevisLuksTest { }; + clevisLuksFallback = mkClevisLuksTest { fallback = true; }; + clevisZfs = mkClevisZfsTest { }; + clevisZfsFallback = mkClevisZfsTest { fallback = true; }; } // optionalAttrs systemdStage1 { stratisRoot = makeInstallerTest "stratisRoot" { createPartitions = '' diff --git a/nixpkgs/nixos/tests/teleport.nix b/nixpkgs/nixos/tests/teleport.nix index cdf762b12844..d68917c6c7ac 100644 --- a/nixpkgs/nixos/tests/teleport.nix +++ b/nixpkgs/nixos/tests/teleport.nix @@ -9,7 +9,8 @@ with import ../lib/testing-python.nix { inherit system pkgs; }; let packages = with pkgs; { "default" = teleport; - "11" = teleport_11; + "12" = teleport_12; + "13" = teleport_13; }; minimal = package: { diff --git a/nixpkgs/nixos/tests/terminal-emulators.nix b/nixpkgs/nixos/tests/terminal-emulators.nix index 2306c03c18e7..efaac03e1983 100644 --- a/nixpkgs/nixos/tests/terminal-emulators.nix +++ b/nixpkgs/nixos/tests/terminal-emulators.nix @@ -23,9 +23,8 @@ with pkgs.lib; let tests = { alacritty.pkg = p: p.alacritty; - # times out after spending many hours - #contour.pkg = p: p.contour; - #contour.cmd = "contour $command"; + contour.pkg = p: p.contour; + contour.cmd = "contour early-exit-threshold 0 execute $command"; cool-retro-term.pkg = p: p.cool-retro-term; cool-retro-term.colourTest = false; # broken by gloss effect diff --git a/nixpkgs/nixos/tests/tsm-client-gui.nix b/nixpkgs/nixos/tests/tsm-client-gui.nix index e11501da53d0..c9632546db6e 100644 --- a/nixpkgs/nixos/tests/tsm-client-gui.nix +++ b/nixpkgs/nixos/tests/tsm-client-gui.nix @@ -18,9 +18,9 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { defaultServername = "testserver"; servers.testserver = { # 192.0.0.8 is a "dummy address" according to RFC 7600 - server = "192.0.0.8"; - node = "SOME-NODE"; - passwdDir = "/tmp"; + tcpserveraddress = "192.0.0.8"; + nodename = "SOME-NODE"; + passworddir = "/tmp"; }; }; }; |