Skip to content

Commit

Permalink
nixos/systemd-sysusers: only create systemusers
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
nikstur committed Jul 21, 2024
1 parent 2441226 commit d43e323
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 42 deletions.
27 changes: 17 additions & 10 deletions nixos/modules/system/boot/systemd/sysusers.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,7 +18,7 @@ let
in
''u ${username} ${uid}:${opts.group} "${opts.description}" ${opts.home} ${utils.toShellPath opts.shell}''
)
userCfg.users)
systemUsers)
}
# Groups
Expand All @@ -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))
)
}
'';
Expand Down Expand Up @@ -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 [
({
Expand All @@ -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'
Expand All @@ -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}" {
Expand All @@ -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))
;
};
};
Expand Down
36 changes: 20 additions & 16 deletions nixos/tests/systemd-sysusers-immutable.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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

{
Expand All @@ -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;
};
};
};
Expand All @@ -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"
'';
}
36 changes: 20 additions & 16 deletions nixos/tests/systemd-sysusers-mutable.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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

{
Expand All @@ -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;
};
};
};
Expand All @@ -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"
Expand All @@ -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"
'';
}

0 comments on commit d43e323

Please sign in to comment.