From d43e323b4aa9ddace802bd12f3fcc34b7701f53e Mon Sep 17 00:00:00 2001 From: nikstur Date: Fri, 19 Jul 2024 17:03:22 +0200 Subject: [PATCH] nixos/systemd-sysusers: only create systemusers systemd-sysusers cannot create normal users (i.e. with a UID > 1000). Thus we stop trying an explitily only use systemd-sysusers when there are no normal users on the system (e.g. appliances). --- .../modules/system/boot/systemd/sysusers.nix | 27 ++++++++------ nixos/tests/systemd-sysusers-immutable.nix | 36 ++++++++++--------- nixos/tests/systemd-sysusers-mutable.nix | 36 ++++++++++--------- 3 files changed, 57 insertions(+), 42 deletions(-) diff --git a/nixos/modules/system/boot/systemd/sysusers.nix b/nixos/modules/system/boot/systemd/sysusers.nix index 476251e14045..e81fa33f66e5 100644 --- a/nixos/modules/system/boot/systemd/sysusers.nix +++ b/nixos/modules/system/boot/systemd/sysusers.nix @@ -5,6 +5,8 @@ let cfg = config.systemd.sysusers; userCfg = config.users; + systemUsers = lib.filterAttrs (_username: opts: !opts.isNormalUser) userCfg.users; + sysusersConfig = pkgs.writeTextDir "00-nixos.conf" '' # Type Name ID GECOS Home directory Shell @@ -16,7 +18,7 @@ let in ''u ${username} ${uid}:${opts.group} "${opts.description}" ${opts.home} ${utils.toShellPath opts.shell}'' ) - userCfg.users) + systemUsers) } # Groups @@ -35,15 +37,15 @@ let ${lib.concatLines ( (lib.mapAttrsToList (username: opts: "echo -n '${opts.initialHashedPassword}' > 'passwd.hashed-password.${username}'") - (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) userCfg.users)) + (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) systemUsers)) ++ (lib.mapAttrsToList (username: opts: "echo -n '${opts.initialPassword}' > 'passwd.plaintext-password.${username}'") - (lib.filterAttrs (_username: opts: opts.initialPassword != null) userCfg.users)) + (lib.filterAttrs (_username: opts: opts.initialPassword != null) systemUsers)) ++ (lib.mapAttrsToList (username: opts: "cat '${opts.hashedPasswordFile}' > 'passwd.hashed-password.${username}'") - (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) userCfg.users)) + (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) systemUsers)) ) } ''; @@ -90,7 +92,12 @@ in assertion = config.users.mutableUsers -> config.system.etc.overlay.enable; message = "config.users.mutableUsers requires config.system.etc.overlay.enable."; } - ]; + ] ++ lib.mapAttrsToList + (username: opts: { + assertion = !opts.isNormalUser; + message = "systemd-sysusers doesn't create normal users. You can currently only use it to create system users."; + }) + userCfg.users; systemd = lib.mkMerge [ ({ @@ -105,7 +112,7 @@ in group = opts.group; }; }) - (lib.filterAttrs (_username: opts: opts.home != "/var/empty") userCfg.users); + (lib.filterAttrs (_username: opts: opts.home != "/var/empty") systemUsers); # Create uid/gid marker files for those without an explicit id tmpfiles.settings.nixos-uid = lib.mapAttrs' @@ -114,7 +121,7 @@ in user = username; }; }) - (lib.filterAttrs (_username: opts: opts.uid == null) userCfg.users); + (lib.filterAttrs (_username: opts: opts.uid == null) systemUsers); tmpfiles.settings.nixos-gid = lib.mapAttrs' (groupname: opts: lib.nameValuePair "/var/lib/nixos/gid/${groupname}" { @@ -140,14 +147,14 @@ in serviceConfig = { LoadCredential = lib.mapAttrsToList (username: opts: "passwd.hashed-password.${username}:${opts.hashedPasswordFile}") - (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) userCfg.users); + (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) systemUsers); SetCredential = (lib.mapAttrsToList (username: opts: "passwd.hashed-password.${username}:${opts.initialHashedPassword}") - (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) userCfg.users)) + (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) systemUsers)) ++ (lib.mapAttrsToList (username: opts: "passwd.plaintext-password.${username}:${opts.initialPassword}") - (lib.filterAttrs (_username: opts: opts.initialPassword != null) userCfg.users)) + (lib.filterAttrs (_username: opts: opts.initialPassword != null) systemUsers)) ; }; }; diff --git a/nixos/tests/systemd-sysusers-immutable.nix b/nixos/tests/systemd-sysusers-immutable.nix index 42cbf84d175e..1d76fa71b5b9 100644 --- a/nixos/tests/systemd-sysusers-immutable.nix +++ b/nixos/tests/systemd-sysusers-immutable.nix @@ -2,8 +2,8 @@ let rootPassword = "$y$j9T$p6OI0WN7.rSfZBOijjRdR.$xUOA2MTcB48ac.9Oc5fz8cxwLv1mMqabnn333iOzSA6"; - normaloPassword = "$y$j9T$3aiOV/8CADAK22OK2QT3/0$67OKd50Z4qTaZ8c/eRWHLIM.o3ujtC1.n9ysmJfv639"; - newNormaloPassword = "mellow"; + sysuserPassword = "$y$j9T$3aiOV/8CADAK22OK2QT3/0$67OKd50Z4qTaZ8c/eRWHLIM.o3ujtC1.n9ysmJfv639"; + newSysuserPassword = "mellow"; in { @@ -19,15 +19,19 @@ in # Override the empty root password set by the test instrumentation users.users.root.hashedPasswordFile = lib.mkForce null; users.users.root.initialHashedPassword = rootPassword; - users.users.normalo = { - isNormalUser = true; - initialHashedPassword = normaloPassword; + users.users.sysuser = { + isSystemUser = true; + group = "wheel"; + home = "/sysuser"; + initialHashedPassword = sysuserPassword; }; specialisation.new-generation.configuration = { - users.users.new-normalo = { - isNormalUser = true; - initialPassword = newNormaloPassword; + users.users.new-sysuser = { + isSystemUser = true; + group = "wheel"; + home = "/new-sysuser"; + initialPassword = newSysuserPassword; }; }; }; @@ -47,18 +51,18 @@ in print(machine.succeed("getent passwd root")) assert "${rootPassword}" in machine.succeed("getent shadow root"), "root user password is not correct" - with subtest("normalo user is created"): - print(machine.succeed("getent passwd normalo")) - assert machine.succeed("stat -c '%U' /home/normalo") == "normalo\n" - assert "${normaloPassword}" in machine.succeed("getent shadow normalo"), "normalo user password is not correct" + with subtest("sysuser user is created"): + print(machine.succeed("getent passwd sysuser")) + assert machine.succeed("stat -c '%U' /sysuser") == "sysuser\n" + assert "${sysuserPassword}" in machine.succeed("getent shadow sysuser"), "sysuser user password is not correct" machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") - with subtest("new-normalo user is created after switching to new generation"): - print(machine.succeed("getent passwd new-normalo")) - print(machine.succeed("getent shadow new-normalo")) - assert machine.succeed("stat -c '%U' /home/new-normalo") == "new-normalo\n" + with subtest("new-sysuser user is created after switching to new generation"): + print(machine.succeed("getent passwd new-sysuser")) + print(machine.succeed("getent shadow new-sysuser")) + assert machine.succeed("stat -c '%U' /new-sysuser") == "new-sysuser\n" ''; } diff --git a/nixos/tests/systemd-sysusers-mutable.nix b/nixos/tests/systemd-sysusers-mutable.nix index e69cfe23a59a..dd98013df976 100644 --- a/nixos/tests/systemd-sysusers-mutable.nix +++ b/nixos/tests/systemd-sysusers-mutable.nix @@ -2,8 +2,8 @@ let rootPassword = "$y$j9T$p6OI0WN7.rSfZBOijjRdR.$xUOA2MTcB48ac.9Oc5fz8cxwLv1mMqabnn333iOzSA6"; - normaloPassword = "hello"; - newNormaloPassword = "$y$j9T$p6OI0WN7.rSfZBOijjRdR.$xUOA2MTcB48ac.9Oc5fz8cxwLv1mMqabnn333iOzSA6"; + sysuserPassword = "hello"; + newSysuserPassword = "$y$j9T$p6OI0WN7.rSfZBOijjRdR.$xUOA2MTcB48ac.9Oc5fz8cxwLv1mMqabnn333iOzSA6"; in { @@ -24,15 +24,19 @@ in # Override the empty root password set by the test instrumentation users.users.root.hashedPasswordFile = lib.mkForce null; users.users.root.initialHashedPassword = rootPassword; - users.users.normalo = { - isNormalUser = true; - initialPassword = normaloPassword; + users.users.sysuser = { + isSystemUser = true; + group = "wheel"; + home = "/sysuser"; + initialPassword = sysuserPassword; }; specialisation.new-generation.configuration = { - users.users.new-normalo = { - isNormalUser = true; - initialHashedPassword = newNormaloPassword; + users.users.new-sysuser = { + isSystemUser = true; + group = "wheel"; + home = "/new-sysuser"; + initialHashedPassword = newSysuserPassword; }; }; }; @@ -43,7 +47,7 @@ in with subtest("systemd-sysusers.service contains the credentials"): sysusers_service = machine.succeed("systemctl cat systemd-sysusers.service") print(sysusers_service) - assert "SetCredential=passwd.plaintext-password.normalo:${normaloPassword}" in sysusers_service + assert "SetCredential=passwd.plaintext-password.sysuser:${sysuserPassword}" in sysusers_service with subtest("Correct mode on the password files"): assert machine.succeed("stat -c '%a' /etc/passwd") == "644\n" @@ -55,17 +59,17 @@ in print(machine.succeed("getent passwd root")) assert "${rootPassword}" in machine.succeed("getent shadow root"), "root user password is not correct" - with subtest("normalo user is created"): - print(machine.succeed("getent passwd normalo")) - assert machine.succeed("stat -c '%U' /home/normalo") == "normalo\n" + with subtest("sysuser user is created"): + print(machine.succeed("getent passwd sysuser")) + assert machine.succeed("stat -c '%U' /sysuser") == "sysuser\n" machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") - with subtest("new-normalo user is created after switching to new generation"): - print(machine.succeed("getent passwd new-normalo")) - assert machine.succeed("stat -c '%U' /home/new-normalo") == "new-normalo\n" - assert "${newNormaloPassword}" in machine.succeed("getent shadow new-normalo"), "new-normalo user password is not correct" + with subtest("new-sysuser user is created after switching to new generation"): + print(machine.succeed("getent passwd new-sysuser")) + assert machine.succeed("stat -c '%U' /new-sysuser") == "new-sysuser\n" + assert "${newSysuserPassword}" in machine.succeed("getent shadow new-sysuser"), "new-sysuser user password is not correct" ''; }