diff --git a/nixos/modules/services/system/userborn.nix b/nixos/modules/services/system/userborn.nix index f9595fe72654..5649b438965b 100644 --- a/nixos/modules/services/system/userborn.nix +++ b/nixos/modules/services/system/userborn.nix @@ -38,6 +38,7 @@ let userbornStaticFiles = pkgs.runCommand "static-userborn" { } "mkdir -p $out; ${lib.getExe cfg.package} ${userbornConfigJson} $out"; + previousConfigPath = "/var/lib/userborn/previous-userborn.json"; immutableEtc = config.system.etc.overlay.enable && !config.system.etc.overlay.mutable; # The filenames created by userborn. @@ -155,6 +156,10 @@ in # This way we don't have to re-declare all the dependencies to other # services again. aliases = [ "systemd-sysusers.service" ]; + environment = { + USERBORN_MUTABLE_USERS = lib.boolToString userCfg.mutableUsers; + USERBORN_PREVIOUS_CONFIG = lib.mkIf userCfg.mutableUsers previousConfigPath; + }; unitConfig = { Description = "Manage Users and Groups"; @@ -165,6 +170,7 @@ in Type = "oneshot"; RemainAfterExit = true; TimeoutSec = "90s"; + StateDirectory = "userborn"; ExecStart = "${lib.getExe cfg.package} ${userbornConfigJson} ${cfg.passwordFilesLocation}"; @@ -179,13 +185,18 @@ in )) ]; - # Make the source files read-only after userborn has finished. - ExecStartPost = lib.mkIf (!userCfg.mutableUsers) ( - lib.map ( - file: - "${pkgs.util-linux}/bin/mount --bind -o ro ${cfg.passwordFilesLocation}/${file} ${cfg.passwordFilesLocation}/${file}" - ) passwordFiles - ); + ExecStartPost = + if userCfg.mutableUsers then + # Store the config somewhere for the next invocation + [ + "${pkgs.coreutils}/bin/ln -sf ${userbornConfigJson} ${previousConfigPath}" + ] + else + # Make the source files read-only after userborn has finished. + (lib.map ( + file: + "${pkgs.util-linux}/bin/mount --bind -o ro ${cfg.passwordFilesLocation}/${file} ${cfg.passwordFilesLocation}/${file}" + ) passwordFiles); }; }; }; diff --git a/nixos/tests/userborn-immutable-etc.nix b/nixos/tests/userborn-immutable-etc.nix index b4b1b56fc2fd..dc34537b9b51 100644 --- a/nixos/tests/userborn-immutable-etc.nix +++ b/nixos/tests/userborn-immutable-etc.nix @@ -21,7 +21,7 @@ in meta.maintainers = with lib.maintainers; [ nikstur ]; nodes.machine = - { config, ... }: + { pkgs, ... }: { imports = [ common ]; @@ -38,7 +38,7 @@ in inheritParentConfig = false; configuration = { nixpkgs = { - inherit (config.nixpkgs) hostPlatform; + inherit pkgs; }; imports = [ common ]; diff --git a/nixos/tests/userborn-immutable-users.nix b/nixos/tests/userborn-immutable-users.nix index 887d2d312eb7..46ea6d4a4be2 100644 --- a/nixos/tests/userborn-immutable-users.nix +++ b/nixos/tests/userborn-immutable-users.nix @@ -16,7 +16,7 @@ in meta.maintainers = with lib.maintainers; [ nikstur ]; nodes.machine = - { config, ... }: + { pkgs, ... }: { imports = [ common ]; @@ -33,7 +33,7 @@ in inheritParentConfig = false; configuration = { nixpkgs = { - inherit (config.nixpkgs) hostPlatform; + inherit pkgs; }; imports = [ common ]; diff --git a/nixos/tests/userborn-mutable-etc.nix b/nixos/tests/userborn-mutable-etc.nix index 6199b84ce71d..020c29a048b9 100644 --- a/nixos/tests/userborn-mutable-etc.nix +++ b/nixos/tests/userborn-mutable-etc.nix @@ -20,7 +20,7 @@ in meta.maintainers = with lib.maintainers; [ nikstur ]; nodes.machine = - { config, ... }: + { pkgs, ... }: { imports = [ common ]; @@ -37,7 +37,7 @@ in inheritParentConfig = false; configuration = { nixpkgs = { - inherit (config.nixpkgs) hostPlatform; + inherit pkgs; }; imports = [ common ]; diff --git a/nixos/tests/userborn-mutable-users.nix b/nixos/tests/userborn-mutable-users.nix index e2b9c3df4953..8f23afce9b9a 100644 --- a/nixos/tests/userborn-mutable-users.nix +++ b/nixos/tests/userborn-mutable-users.nix @@ -16,7 +16,7 @@ in meta.maintainers = with lib.maintainers; [ nikstur ]; nodes.machine = - { config, ... }: + { pkgs, ... }: { imports = [ common ]; @@ -34,7 +34,7 @@ in inheritParentConfig = false; configuration = { nixpkgs = { - inherit (config.nixpkgs) hostPlatform; + inherit pkgs; }; imports = [ common ]; @@ -42,6 +42,10 @@ in new-normalo = { isNormalUser = true; }; + mutable-to-declarative = { + isNormalUser = true; + description = "I'm now declaratively managed"; + }; }; }; }; @@ -54,16 +58,25 @@ in 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"): + with subtest("Add new user manual-normalo 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") - + with subtest("Add new user mutable-to-declarative manually"): + machine.succeed("useradd --comment 'I was created imperatively' mutable-to-declarative") + assert 1002 == int(machine.succeed("id --user mutable-to-declarative")), "mutable-to-declarative user doesn't have UID 1002" machine.succeed("/run/current-system/specialisation/new-generation/bin/switch-to-configuration switch") + with subtest("manual-normalo user is still enabled"): + manual_normalo_shadow = machine.succeed("getent shadow manual-normalo") + print(manual_normalo_shadow) + t.assertNotIn("!*", manual_normalo_shadow, "manual-normalo user is falsely disabled") + + with subtest("mutable-to-declarative user description has changed"): + mutable_to_declarative_passwd = machine.succeed("getent passwd mutable-to-declarative") + print(mutable_to_declarative_passwd) + t.assertIn("I'm now declaratively managed", mutable_to_declarative_passwd, "mutable-to-declarative user description is unchanged") with subtest("normalo user is disabled"): print(machine.succeed("getent shadow normalo")) @@ -71,6 +84,9 @@ in 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" + assert 1003 == int(machine.succeed("id --user new-normalo")), "new-normalo user doesn't have UID 1003" + + with subtest("Delete manual-normalo user manually"): + machine.succeed("userdel manual-normalo") ''; } diff --git a/pkgs/by-name/us/userborn/package.nix b/pkgs/by-name/us/userborn/package.nix index 331b44575634..0fa93590ebb3 100644 --- a/pkgs/by-name/us/userborn/package.nix +++ b/pkgs/by-name/us/userborn/package.nix @@ -9,18 +9,18 @@ rustPlatform.buildRustPackage rec { pname = "userborn"; - version = "0.4.0"; + version = "0.5.0"; src = fetchFromGitHub { owner = "nikstur"; repo = "userborn"; rev = version; - hash = "sha256-Zh2u7we/MAIM7varuJA4AmEWeSMuA/C+0NSIUJN7zTs="; + hash = "sha256-mXXakR75Iz6AFf/TYgIHE8SxOri2HyReYUYTT3lCEPA="; }; sourceRoot = "${src.name}/rust/userborn"; - cargoHash = "sha256-oLw/I8PEv75tz+KxbIJrwl8Wr0I/RzDh1SDZ6mRQpL8="; + cargoHash = "sha256-uAid5GsM9lasVQAYfeo9jwp4xg1MrXdJqtD0l6ME6OQ="; nativeBuildInputs = [ rustPlatform.bindgenHook ];