at master 7.4 kB view raw
1let 2 password1 = "foobar"; 3 password2 = "helloworld"; 4 hashed_bcrypt = "$2b$05$8xIEflrk2RxQtcVXbGIxs.Vl0x7dF1/JSv3cyX6JJt0npzkTCWvxK"; # fnord 5 hashed_yeshash = "$y$j9T$d8Z4EAf8P1SvM/aDFbxMS0$VnTXMp/Hnc7QdCBEaLTq5ZFOAFo2/PM0/xEAFuOE88."; # fnord 6 hashed_sha512crypt = "$6$ymzs8WINZ5wGwQcV$VC2S0cQiX8NVukOLymysTPn4v1zJoJp3NGyhnqyv/dAf4NWZsBWYveQcj6gEJr4ZUjRBRjM0Pj1L8TCQ8hUUp0"; # meow 7in 8 9{ pkgs, ... }: 10{ 11 name = "password-option-override-ordering"; 12 meta = with pkgs.lib.maintainers; { 13 maintainers = [ fidgetingbits ]; 14 }; 15 16 nodes = 17 let 18 # The following users are expected to have the same behavior between immutable and mutable systems 19 # NOTE: Below given A -> B it implies B overrides A . Each entry below builds off the next 20 users = { 21 # mutable true/false: initialHashedPassword -> hashedPassword 22 fran = { 23 isNormalUser = true; 24 initialHashedPassword = hashed_yeshash; 25 hashedPassword = hashed_sha512crypt; 26 }; 27 28 # mutable false: initialHashedPassword -> hashedPassword -> initialPassword 29 # mutable true: initialHashedPassword -> initialPassword -> hashedPassword 30 greg = { 31 isNormalUser = true; 32 hashedPassword = hashed_sha512crypt; 33 initialPassword = password1; 34 }; 35 36 # mutable false: initialHashedPassword -> hashedPassword -> initialPassword -> password 37 # mutable true: initialHashedPassword -> initialPassword -> hashedPassword -> password 38 egon = { 39 isNormalUser = true; 40 initialPassword = password2; 41 password = password1; 42 }; 43 44 # mutable true/false: hashedPassword -> password 45 # NOTE: minor duplication of test above, but to verify no initialXXX use is consistent 46 alice = { 47 isNormalUser = true; 48 hashedPassword = hashed_sha512crypt; 49 password = password1; 50 }; 51 52 # mutable false: initialHashedPassword -> hashedPassword -> initialPassword -> password -> hashedPasswordFile 53 # mutable true: initialHashedPassword -> initialPassword -> hashedPassword -> password -> hashedPasswordFile 54 bob = { 55 isNormalUser = true; 56 hashedPassword = hashed_sha512crypt; 57 password = password1; 58 hashedPasswordFile = (pkgs.writeText "hashed_bcrypt" hashed_bcrypt).outPath; # Expect override of everything above 59 }; 60 61 # Show hashedPassword -> password -> hashedPasswordFile -> initialPassword is false 62 # to explicitly show the following lib.trace warning in users-groups.nix (which was 63 # the wording prior to PR 310484) is in fact wrong: 64 # ``` 65 # The user 'root' has multiple of the options 66 # `hashedPassword`, `password`, `hashedPasswordFile`, `initialPassword` 67 # & `initialHashedPassword` set to a non-null value. 68 # The options silently discard others by the order of precedence 69 # given above which can lead to surprising results. To resolve this warning, 70 # set at most one of the options above to a non-`null` value. 71 # ``` 72 cat = { 73 isNormalUser = true; 74 hashedPassword = hashed_sha512crypt; 75 password = password1; 76 hashedPasswordFile = (pkgs.writeText "hashed_bcrypt" hashed_bcrypt).outPath; 77 initialPassword = password2; # lib.trace message implies this overrides everything above 78 }; 79 80 # Show hashedPassword -> password -> hashedPasswordFile -> initialHashedPassword is false 81 # to also explicitly show the lib.trace explained above (see cat user) is wrong 82 dan = { 83 isNormalUser = true; 84 hashedPassword = hashed_sha512crypt; 85 initialPassword = password2; 86 password = password1; 87 hashedPasswordFile = (pkgs.writeText "hashed_bcrypt" hashed_bcrypt).outPath; 88 initialHashedPassword = hashed_yeshash; # lib.trace message implies this overrides everything above 89 }; 90 }; 91 92 mkTestMachine = mutable: { 93 environment.systemPackages = [ pkgs.shadow ]; 94 users = { 95 mutableUsers = mutable; 96 inherit users; 97 }; 98 }; 99 in 100 { 101 immutable = mkTestMachine false; 102 mutable = mkTestMachine true; 103 }; 104 105 testScript = '' 106 import crypt 107 108 def assert_password_match(machine, username, password): 109 shadow_entry = machine.succeed(f"getent shadow {username}") 110 print(shadow_entry) 111 hash = shadow_entry.split(":")[1] 112 seed = "$".join(hash.split("$")[:-1]) 113 assert crypt.crypt(password, seed) == hash, f"{username} user password does not match" 114 115 with subtest("alice user has correct password"): 116 for machine in machines: 117 assert_password_match(machine, "alice", "${password1}") 118 assert "${hashed_sha512crypt}" not in machine.succeed("getent shadow alice"), f"{machine}: alice user password is not correct" 119 120 with subtest("bob user has correct password"): 121 for machine in machines: 122 print(machine.succeed("getent shadow bob")) 123 assert "${hashed_bcrypt}" in machine.succeed("getent shadow bob"), f"{machine}: bob user password is not correct" 124 125 with subtest("cat user has correct password"): 126 for machine in machines: 127 print(machine.succeed("getent shadow cat")) 128 assert "${hashed_bcrypt}" in machine.succeed("getent shadow cat"), f"{machine}: cat user password is not correct" 129 130 with subtest("dan user has correct password"): 131 for machine in machines: 132 print(machine.succeed("getent shadow dan")) 133 assert "${hashed_bcrypt}" in machine.succeed("getent shadow dan"), f"{machine}: dan user password is not correct" 134 135 with subtest("greg user has correct password"): 136 print(mutable.succeed("getent shadow greg")) 137 assert "${hashed_sha512crypt}" in mutable.succeed("getent shadow greg"), "greg user password is not correct" 138 139 assert_password_match(immutable, "greg", "${password1}") 140 assert "${hashed_sha512crypt}" not in immutable.succeed("getent shadow greg"), "greg user password is not correct" 141 142 for machine in machines: 143 machine.wait_for_unit("multi-user.target") 144 machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'") 145 146 def check_login(machine: Machine, tty_number: str, username: str, password: str): 147 machine.send_key(f"alt-f{tty_number}") 148 machine.wait_until_succeeds(f"[ $(fgconsole) = {tty_number} ]") 149 machine.wait_for_unit(f"getty@tty{tty_number}.service") 150 machine.wait_until_succeeds(f"pgrep -f 'agetty.*tty{tty_number}'") 151 machine.wait_until_tty_matches(tty_number, "login: ") 152 machine.send_chars(f"{username}\n") 153 machine.wait_until_tty_matches(tty_number, f"login: {username}") 154 machine.wait_until_succeeds("pgrep login") 155 machine.wait_until_tty_matches(tty_number, "Password: ") 156 machine.send_chars(f"{password}\n") 157 machine.send_chars(f"whoami > /tmp/{tty_number}\n") 158 machine.wait_for_file(f"/tmp/{tty_number}") 159 assert username in machine.succeed(f"cat /tmp/{tty_number}"), f"{machine}: {username} password is not correct" 160 161 with subtest("Test initialPassword override"): 162 for machine in machines: 163 check_login(machine, "2", "egon", "${password1}") 164 165 with subtest("Test initialHashedPassword override"): 166 for machine in machines: 167 check_login(machine, "3", "fran", "meow") 168 ''; 169}