mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-12-25 03:17:13 +00:00
b84fb1e5cd
This commit adds two new tests to show that the ordering of password overrides documentation in nixos/modules/config/user-groups.nix is correct. The override behavior differs depending on whether a system has systemd-sysusers enabled, so there are two tests.
172 lines
7.6 KiB
Nix
172 lines
7.6 KiB
Nix
let
|
|
password1 = "foobar";
|
|
password2 = "helloworld";
|
|
hashed_bcrypt = "$2b$05$8xIEflrk2RxQtcVXbGIxs.Vl0x7dF1/JSv3cyX6JJt0npzkTCWvxK"; # fnord
|
|
hashed_yeshash = "$y$j9T$d8Z4EAf8P1SvM/aDFbxMS0$VnTXMp/Hnc7QdCBEaLTq5ZFOAFo2/PM0/xEAFuOE88."; # fnord
|
|
hashed_sha512crypt = "$6$ymzs8WINZ5wGwQcV$VC2S0cQiX8NVukOLymysTPn4v1zJoJp3NGyhnqyv/dAf4NWZsBWYveQcj6gEJr4ZUjRBRjM0Pj1L8TCQ8hUUp0"; # meow
|
|
in
|
|
|
|
import ./make-test-python.nix (
|
|
{ pkgs, ... }:
|
|
{
|
|
name = "password-option-override-ordering";
|
|
meta = with pkgs.lib.maintainers; {
|
|
maintainers = [ fidgetingbits ];
|
|
};
|
|
|
|
nodes =
|
|
let
|
|
# The following users are expected to have the same behavior between immutable and mutable systems
|
|
# NOTE: Below given A -> B it implies B overrides A . Each entry below builds off the next
|
|
users = {
|
|
# mutable true/false: initialHashedPassword -> hashedPassword
|
|
fran = {
|
|
isNormalUser = true;
|
|
initialHashedPassword = hashed_yeshash;
|
|
hashedPassword = hashed_sha512crypt;
|
|
};
|
|
|
|
# mutable false: initialHashedPassword -> hashedPassword -> initialPassword
|
|
# mutable true: initialHashedPassword -> initialPassword -> hashedPassword
|
|
greg = {
|
|
isNormalUser = true;
|
|
hashedPassword = hashed_sha512crypt;
|
|
initialPassword = password1;
|
|
};
|
|
|
|
# mutable false: initialHashedPassword -> hashedPassword -> initialPassword -> password
|
|
# mutable true: initialHashedPassword -> initialPassword -> hashedPassword -> password
|
|
egon = {
|
|
isNormalUser = true;
|
|
initialPassword = password2;
|
|
password = password1;
|
|
};
|
|
|
|
# mutable true/false: hashedPassword -> password
|
|
# NOTE: minor duplication of test above, but to verify no initialXXX use is consistent
|
|
alice = {
|
|
isNormalUser = true;
|
|
hashedPassword = hashed_sha512crypt;
|
|
password = password1;
|
|
};
|
|
|
|
# mutable false: initialHashedPassword -> hashedPassword -> initialPassword -> password -> hashedPasswordFile
|
|
# mutable true: initialHashedPassword -> initialPassword -> hashedPassword -> password -> hashedPasswordFile
|
|
bob = {
|
|
isNormalUser = true;
|
|
hashedPassword = hashed_sha512crypt;
|
|
password = password1;
|
|
hashedPasswordFile = (pkgs.writeText "hashed_bcrypt" hashed_bcrypt).outPath; # Expect override of everything above
|
|
};
|
|
|
|
# Show hashedPassword -> password -> hashedPasswordFile -> initialPassword is false
|
|
# to explicitly show the following lib.trace warning in users-groups.nix (which was
|
|
# the wording prior to PR 310484) is in fact wrong:
|
|
# ```
|
|
# The user 'root' has multiple of the options
|
|
# `hashedPassword`, `password`, `hashedPasswordFile`, `initialPassword`
|
|
# & `initialHashedPassword` set to a non-null value.
|
|
# The options silently discard others by the order of precedence
|
|
# given above which can lead to surprising results. To resolve this warning,
|
|
# set at most one of the options above to a non-`null` value.
|
|
# ```
|
|
cat = {
|
|
isNormalUser = true;
|
|
hashedPassword = hashed_sha512crypt;
|
|
password = password1;
|
|
hashedPasswordFile = (pkgs.writeText "hashed_bcrypt" hashed_bcrypt).outPath;
|
|
initialPassword = password2; # lib.trace message implies this overrides everything above
|
|
};
|
|
|
|
# Show hashedPassword -> password -> hashedPasswordFile -> initialHashedPassword is false
|
|
# to also explicitly show the lib.trace explained above (see cat user) is wrong
|
|
dan = {
|
|
isNormalUser = true;
|
|
hashedPassword = hashed_sha512crypt;
|
|
initialPassword = password2;
|
|
password = password1;
|
|
hashedPasswordFile = (pkgs.writeText "hashed_bcrypt" hashed_bcrypt).outPath;
|
|
initialHashedPassword = hashed_yeshash; # lib.trace message implies this overrides everything above
|
|
};
|
|
};
|
|
|
|
mkTestMachine = mutable: {
|
|
environment.systemPackages = [ pkgs.shadow ];
|
|
users = {
|
|
mutableUsers = mutable;
|
|
inherit users;
|
|
};
|
|
};
|
|
in
|
|
{
|
|
immutable = mkTestMachine false;
|
|
mutable = mkTestMachine true;
|
|
};
|
|
|
|
testScript = ''
|
|
import crypt
|
|
|
|
def assert_password_match(machine, username, password):
|
|
shadow_entry = machine.succeed(f"getent shadow {username}")
|
|
print(shadow_entry)
|
|
hash = shadow_entry.split(":")[1]
|
|
seed = "$".join(hash.split("$")[:-1])
|
|
assert crypt.crypt(password, seed) == hash, f"{username} user password does not match"
|
|
|
|
with subtest("alice user has correct password"):
|
|
for machine in machines:
|
|
assert_password_match(machine, "alice", "${password1}")
|
|
assert "${hashed_sha512crypt}" not in machine.succeed("getent shadow alice"), f"{machine}: alice user password is not correct"
|
|
|
|
with subtest("bob user has correct password"):
|
|
for machine in machines:
|
|
print(machine.succeed("getent shadow bob"))
|
|
assert "${hashed_bcrypt}" in machine.succeed("getent shadow bob"), f"{machine}: bob user password is not correct"
|
|
|
|
with subtest("cat user has correct password"):
|
|
for machine in machines:
|
|
print(machine.succeed("getent shadow cat"))
|
|
assert "${hashed_bcrypt}" in machine.succeed("getent shadow cat"), f"{machine}: cat user password is not correct"
|
|
|
|
with subtest("dan user has correct password"):
|
|
for machine in machines:
|
|
print(machine.succeed("getent shadow dan"))
|
|
assert "${hashed_bcrypt}" in machine.succeed("getent shadow dan"), f"{machine}: dan user password is not correct"
|
|
|
|
with subtest("greg user has correct password"):
|
|
print(mutable.succeed("getent shadow greg"))
|
|
assert "${hashed_sha512crypt}" in mutable.succeed("getent shadow greg"), "greg user password is not correct"
|
|
|
|
assert_password_match(immutable, "greg", "${password1}")
|
|
assert "${hashed_sha512crypt}" not in immutable.succeed("getent shadow greg"), "greg user password is not correct"
|
|
|
|
for machine in machines:
|
|
machine.wait_for_unit("multi-user.target")
|
|
machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
|
|
|
def check_login(machine: Machine, tty_number: str, username: str, password: str):
|
|
machine.send_key(f"alt-f{tty_number}")
|
|
machine.wait_until_succeeds(f"[ $(fgconsole) = {tty_number} ]")
|
|
machine.wait_for_unit(f"getty@tty{tty_number}.service")
|
|
machine.wait_until_succeeds(f"pgrep -f 'agetty.*tty{tty_number}'")
|
|
machine.wait_until_tty_matches(tty_number, "login: ")
|
|
machine.send_chars(f"{username}\n")
|
|
machine.wait_until_tty_matches(tty_number, f"login: {username}")
|
|
machine.wait_until_succeeds("pgrep login")
|
|
machine.wait_until_tty_matches(tty_number, "Password: ")
|
|
machine.send_chars(f"{password}\n")
|
|
machine.send_chars(f"whoami > /tmp/{tty_number}\n")
|
|
machine.wait_for_file(f"/tmp/{tty_number}")
|
|
assert username in machine.succeed(f"cat /tmp/{tty_number}"), f"{machine}: {username} password is not correct"
|
|
|
|
with subtest("Test initialPassword override"):
|
|
for machine in machines:
|
|
check_login(machine, "2", "egon", "${password1}")
|
|
|
|
with subtest("Test initialHashedPassword override"):
|
|
for machine in machines:
|
|
check_login(machine, "3", "fran", "meow")
|
|
'';
|
|
}
|
|
)
|