From ad63319ca56d4ed6d1b017d86ad35fc0f3821c41 Mon Sep 17 00:00:00 2001 From: nikstur Date: Wed, 7 Aug 2024 14:56:05 +0200 Subject: [PATCH] nixos/tests/userborn: init --- nixos/tests/all-tests.nix | 5 + nixos/tests/userborn-immutable-etc.nix | 70 +++++++++++++ nixos/tests/userborn-immutable-users.nix | 75 +++++++++++++ nixos/tests/userborn-mutable-etc.nix | 70 +++++++++++++ nixos/tests/userborn-mutable-users.nix | 76 ++++++++++++++ nixos/tests/userborn.nix | 127 +++++++++++++++++++++++ 6 files changed, 423 insertions(+) create mode 100644 nixos/tests/userborn-immutable-etc.nix create mode 100644 nixos/tests/userborn-immutable-users.nix create mode 100644 nixos/tests/userborn-mutable-etc.nix create mode 100644 nixos/tests/userborn-mutable-users.nix create mode 100644 nixos/tests/userborn.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 0cd52cbec9a3..7faa7d4748e0 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1057,6 +1057,11 @@ in { uptime-kuma = handleTest ./uptime-kuma.nix {}; urn-timer = handleTest ./urn-timer.nix {}; usbguard = handleTest ./usbguard.nix {}; + userborn = runTest ./userborn.nix; + userborn-mutable-users = runTest ./userborn-mutable-users.nix; + userborn-immutable-users = runTest ./userborn-immutable-users.nix; + userborn-mutable-etc = runTest ./userborn-mutable-etc.nix; + userborn-immutable-etc = runTest ./userborn-immutable-etc.nix; user-activation-scripts = handleTest ./user-activation-scripts.nix {}; user-expiry = runTest ./user-expiry.nix; user-home-mode = handleTest ./user-home-mode.nix {}; diff --git a/nixos/tests/userborn-immutable-etc.nix b/nixos/tests/userborn-immutable-etc.nix new file mode 100644 index 000000000000..e95fba23063b --- /dev/null +++ b/nixos/tests/userborn-immutable-etc.nix @@ -0,0 +1,70 @@ +{ lib, ... }: + +let + normaloHashedPassword = "$y$j9T$IEWqhKtWg.r.8fVkSEF56.$iKNxdMC6hOAQRp6eBtYvBk4c7BGpONXeZMqc8I/LM46"; + + common = { + services.userborn.enable = true; + boot.initrd.systemd.enable = true; + system.etc.overlay = { + enable = true; + mutable = false; + }; + }; +in + +{ + + name = "userborn-immutable-etc"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = + { config, ... }: + { + imports = [ common ]; + + users = { + users = { + normalo = { + isNormalUser = true; + hashedPassword = normaloHashedPassword; + }; + }; + }; + + specialisation.new-generation = { + inheritParentConfig = false; + configuration = { + nixpkgs = { + inherit (config.nixpkgs) hostPlatform; + }; + imports = [ common ]; + + users.users = { + new-normalo = { + isNormalUser = true; + }; + }; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("userborn.service") + + with subtest("normalo user is created"): + assert "${normaloHashedPassword}" in machine.succeed("getent shadow normalo"), "normalo user password is not correct" + + + machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") + + + with subtest("normalo user is disabled"): + print(machine.succeed("getent shadow normalo")) + assert "!*" in machine.succeed("getent shadow normalo"), "normalo user is not disabled" + + with subtest("new-normalo user is created after switching to new generation"): + print(machine.succeed("getent passwd new-normalo")) + ''; +} diff --git a/nixos/tests/userborn-immutable-users.nix b/nixos/tests/userborn-immutable-users.nix new file mode 100644 index 000000000000..887d2d312eb7 --- /dev/null +++ b/nixos/tests/userborn-immutable-users.nix @@ -0,0 +1,75 @@ +{ lib, ... }: + +let + normaloHashedPassword = "$y$j9T$IEWqhKtWg.r.8fVkSEF56.$iKNxdMC6hOAQRp6eBtYvBk4c7BGpONXeZMqc8I/LM46"; + + common = { + services.userborn.enable = true; + users.mutableUsers = false; + }; +in + +{ + + name = "userborn-immutable-users"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = + { config, ... }: + { + imports = [ common ]; + + users = { + users = { + normalo = { + isNormalUser = true; + hashedPassword = normaloHashedPassword; + }; + }; + }; + + specialisation.new-generation = { + inheritParentConfig = false; + configuration = { + nixpkgs = { + inherit (config.nixpkgs) hostPlatform; + }; + imports = [ common ]; + + users.users = { + new-normalo = { + isNormalUser = true; + }; + }; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("userborn.service") + + with subtest("normalo user is created"): + assert "${normaloHashedPassword}" in machine.succeed("getent shadow normalo"), "normalo user password is not correct" + + with subtest("Fail to add new user manually"): + machine.fail("useradd manual-normalo") + + with subtest("Fail to add delete user manually"): + machine.fail("userdel normalo") + + + machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") + + + with subtest("normalo user is disabled"): + print(machine.succeed("getent shadow normalo")) + assert "!*" in machine.succeed("getent shadow normalo"), "normalo user is not disabled" + + with subtest("new-normalo user is created after switching to new generation"): + print(machine.succeed("getent passwd new-normalo")) + + with subtest("Still fail to add new user manually"): + machine.fail("useradd again-normalo") + ''; +} diff --git a/nixos/tests/userborn-mutable-etc.nix b/nixos/tests/userborn-mutable-etc.nix new file mode 100644 index 000000000000..6199b84ce71d --- /dev/null +++ b/nixos/tests/userborn-mutable-etc.nix @@ -0,0 +1,70 @@ +{ lib, ... }: + +let + normaloHashedPassword = "$y$j9T$IEWqhKtWg.r.8fVkSEF56.$iKNxdMC6hOAQRp6eBtYvBk4c7BGpONXeZMqc8I/LM46"; + + common = { + services.userborn.enable = true; + boot.initrd.systemd.enable = true; + system.etc.overlay = { + enable = true; + mutable = true; + }; + }; +in + +{ + + name = "userborn-mutable-etc"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = + { config, ... }: + { + imports = [ common ]; + + users = { + users = { + normalo = { + isNormalUser = true; + hashedPassword = normaloHashedPassword; + }; + }; + }; + + specialisation.new-generation = { + inheritParentConfig = false; + configuration = { + nixpkgs = { + inherit (config.nixpkgs) hostPlatform; + }; + imports = [ common ]; + + users.users = { + new-normalo = { + isNormalUser = true; + }; + }; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("userborn.service") + + with subtest("normalo user is created"): + assert "${normaloHashedPassword}" in machine.succeed("getent shadow normalo"), "normalo user password is not correct" + + + machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") + + + with subtest("normalo user is disabled"): + print(machine.succeed("getent shadow normalo")) + assert "!*" in machine.succeed("getent shadow normalo"), "normalo user is not disabled" + + with subtest("new-normalo user is created after switching to new generation"): + print(machine.succeed("getent passwd new-normalo")) + ''; +} diff --git a/nixos/tests/userborn-mutable-users.nix b/nixos/tests/userborn-mutable-users.nix new file mode 100644 index 000000000000..e2b9c3df4953 --- /dev/null +++ b/nixos/tests/userborn-mutable-users.nix @@ -0,0 +1,76 @@ +{ lib, ... }: + +let + normaloHashedPassword = "$y$j9T$IEWqhKtWg.r.8fVkSEF56.$iKNxdMC6hOAQRp6eBtYvBk4c7BGpONXeZMqc8I/LM46"; + + common = { + services.userborn.enable = true; + users.mutableUsers = true; + }; +in + +{ + + name = "userborn-mutable-users"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = + { config, ... }: + { + imports = [ common ]; + + users = { + mutableUsers = true; + users = { + normalo = { + isNormalUser = true; + hashedPassword = normaloHashedPassword; + }; + }; + }; + + specialisation.new-generation = { + inheritParentConfig = false; + configuration = { + nixpkgs = { + inherit (config.nixpkgs) hostPlatform; + }; + imports = [ common ]; + + users.users = { + new-normalo = { + isNormalUser = true; + }; + }; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("userborn.service") + + with subtest("normalo user is created"): + assert 1000 == int(machine.succeed("id --user normalo")), "normalo user doesn't have UID 1000" + assert "${normaloHashedPassword}" in machine.succeed("getent shadow normalo"), "normalo user password is not correct" + + with subtest("Add new user manually"): + machine.succeed("useradd manual-normalo") + assert 1001 == int(machine.succeed("id --user manual-normalo")), "manual-normalo user doesn't have UID 1001" + + with subtest("Delete manual--normalo user manually"): + machine.succeed("userdel manual-normalo") + + + machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") + + + with subtest("normalo user is disabled"): + print(machine.succeed("getent shadow normalo")) + assert "!*" in machine.succeed("getent shadow normalo"), "normalo user is not disabled" + + with subtest("new-normalo user is created after switching to new generation"): + print(machine.succeed("getent passwd new-normalo")) + assert 1001 == int(machine.succeed("id --user new-normalo")), "new-normalo user doesn't have UID 1001" + ''; +} diff --git a/nixos/tests/userborn.nix b/nixos/tests/userborn.nix new file mode 100644 index 000000000000..c89880a14a5c --- /dev/null +++ b/nixos/tests/userborn.nix @@ -0,0 +1,127 @@ +{ lib, ... }: + +let + # All passwords are "test" + rootHashedPasswordFile = "$y$j9T$6ueoTO5y7vvFsGvpQJEEa.$vubxgBiMnkTCtRtPD3hNiZHa7Nm1WsJeE9QomYqSRXB"; + updatedRootHashedPassword = "$y$j9T$pBCO9N1FRF1rSl6V15n9n/$1JmRLEYPO7TRCx43cvLO19u59WA/oqTEhmSR4wrhzr."; + + normaloPassword = "test"; + updatedNormaloHashedPassword = "$y$j9T$IEWqhKtWg.r.8fVkSEF56.$iKNxdMC6hOAQRp6eBtYvBk4c7BGpONXeZMqc8I/LM46"; + + sysuserInitialHashedPassword = "$y$j9T$Kb6jGrk41hudTZpNjazf11$iw7fZXrewC6JxRaGPz7/gPXDZ.Z1VWsupvy81Hi1XiD"; + updatedSysuserInitialHashedPassword = "$y$j9T$kUBVhgOdSjymSfwfRVja70$eqCwWzVsz0fI0Uc6JsdD2CYMCpfJcErqnIqva2JCi1D"; + + newNormaloHashedPassword = "$y$j9T$UFBMWbGjjVola0YE9YCcV/$jRSi5S6lzkcifbuqjMcyXLTwgOGm9BTQk/G/jYaxroC"; +in + +{ + + name = "userborn"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes.machine = { + services.userborn.enable = true; + + # Read this password file at runtime from outside the Nix store. + environment.etc."rootpw.secret".text = rootHashedPasswordFile; + + users = { + users = { + root = { + # Override the empty root password set by the test instrumentation. + hashedPasswordFile = lib.mkForce "/etc/rootpw.secret"; + }; + normalo = { + isNormalUser = true; + password = normaloPassword; + }; + sysuser = { + isSystemUser = true; + group = "sysusers"; + initialHashedPassword = sysuserInitialHashedPassword; + }; + }; + groups = { + sysusers = { }; + }; + }; + + specialisation.new-generation.configuration = { + users = { + users = { + root = { + # Forcing this to null simulates removing the config value in a new + # generation. + hashedPasswordFile = lib.mkOverride 9 null; + hashedPassword = updatedRootHashedPassword; + }; + normalo = { + hashedPassword = updatedNormaloHashedPassword; + }; + sysuser = { + initialHashedPassword = lib.mkForce updatedSysuserInitialHashedPassword; + }; + new-normalo = { + isNormalUser = true; + hashedPassword = newNormaloHashedPassword; + }; + }; + groups = { + new-group = { }; + }; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("userborn.service") + + with subtest("Correct mode on the password files"): + assert machine.succeed("stat -c '%a' /etc/passwd") == "644\n" + assert machine.succeed("stat -c '%a' /etc/group") == "644\n" + assert machine.succeed("stat -c '%a' /etc/shadow") == "0\n" + + with subtest("root user has correct password"): + print(machine.succeed("getent passwd root")) + assert "${rootHashedPasswordFile}" 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 1000 <= int(machine.succeed("id --user normalo")), "normalo user doesn't have a normal UID" + assert machine.succeed("stat -c '%U' /home/normalo") == "normalo\n" + + with subtest("system user is created with correct password"): + print(machine.succeed("getent passwd sysuser")) + assert 1000 > int(machine.succeed("id --user sysuser")), "sysuser user doesn't have a system UID" + assert "${sysuserInitialHashedPassword}" in machine.succeed("getent shadow sysuser"), "system user password is not correct" + + with subtest("sysusers group is created"): + print(machine.succeed("getent group sysusers")) + + + machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") + + + with subtest("root user password is updated"): + print(machine.succeed("getent passwd root")) + assert "${updatedRootHashedPassword}" in machine.succeed("getent shadow root"), "root user password is not updated" + + with subtest("normalo user password is updated"): + print(machine.succeed("getent passwd normalo")) + assert "${updatedNormaloHashedPassword}" in machine.succeed("getent shadow normalo"), "normalo user password is not updated" + + with subtest("system user password is NOT updated"): + print(machine.succeed("getent passwd sysuser")) + assert "${sysuserInitialHashedPassword}" in machine.succeed("getent shadow sysuser"), "sysuser user password is not updated" + + with subtest("new-normalo user is created after switching to new generation"): + print(machine.succeed("getent passwd new-normalo")) + assert 1000 <= int(machine.succeed("id --user new-normalo")), "new-normalo user doesn't have a normal UID" + assert machine.succeed("stat -c '%U' /home/new-normalo") == "new-normalo\n" + assert "${newNormaloHashedPassword}" in machine.succeed("getent shadow new-normalo"), "new-normalo user password is not correct" + + with subtest("new-group group is created after switching to new generation"): + print(machine.succeed("getent group new-group")) + ''; +}