-
-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Correct password option docs and add related tests
- Loading branch information
1 parent
ae8c3f2
commit b572dd2
Showing
4 changed files
with
290 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
let | ||
password1 = "foobar"; | ||
password2 = "helloworld"; | ||
password3 = "bazqux"; | ||
password4 = "asdf123"; | ||
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.machine = | ||
{ pkgs, ... }: | ||
{ | ||
environment.systemPackages = [ pkgs.shadow ]; | ||
|
||
users = { | ||
mutableUsers = false; | ||
|
||
# NOTE: Below given A -> B it implies B overrides A . Each entry below builds off the next | ||
|
||
# initialHashedPassword -> hashedPassword | ||
users.fran = { | ||
isNormalUser = true; | ||
initialHashedPassword = hashed_yeshash; | ||
hashedPassword = hashed_sha512crypt; | ||
}; | ||
|
||
# initialHashedPassword -> hashedPassword -> initialPassword | ||
users.greg = { | ||
isNormalUser = true; | ||
hashedPassword = hashed_sha512crypt; | ||
initialPassword = password1; # Expect override of above | ||
}; | ||
|
||
# initialHashedPassword -> hashedPassword -> initialPassword -> password | ||
users.egon = { | ||
isNormalUser = true; | ||
initialPassword = password2; | ||
password = password1; | ||
}; | ||
|
||
# initialHashedPassword -> hashedPassword -> initialPassword -> password | ||
# NOTE: duplication, but to verify no initialXXX use is consistent | ||
users.alice = { | ||
isNormalUser = true; | ||
hashedPassword = hashed_sha512crypt; | ||
password = password1; | ||
}; | ||
|
||
# initialHashedPassword -> hashedPassword -> initialPassword -> password -> hashedPasswordFile | ||
users.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 is 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. | ||
# ``` | ||
users.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 explicitly show the lib.trace shown above is wrong | ||
users.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 | ||
}; | ||
}; | ||
}; | ||
|
||
testScript = '' | ||
with subtest("alice user has correct password"): | ||
print(machine.succeed("getent passwd alice")) | ||
assert "${hashed_sha512crypt}" not in machine.succeed("getent shadow alice"), "alice user password is not correct" | ||
with subtest("bob user has correct password"): | ||
print(machine.succeed("getent passwd bob")) | ||
assert "${hashed_bcrypt}" in machine.succeed("getent shadow bob"), "bob user password is not correct" | ||
with subtest("cat user has correct password"): | ||
print(machine.succeed("getent passwd cat")) | ||
assert "${hashed_bcrypt}" in machine.succeed("getent shadow cat"), "cat user password is not correct" | ||
with subtest("dan user has correct password"): | ||
print(machine.succeed("getent passwd dan")) | ||
assert "${hashed_bcrypt}" in machine.succeed("getent shadow dan"), "dan user password is not correct" | ||
with subtest("greg user has correct password"): | ||
print(machine.succeed("getent passwd greg")) | ||
assert "${hashed_sha512crypt}" not in machine.succeed("getent shadow greg"), "greg user password is not correct" | ||
machine.wait_for_unit("multi-user.target") | ||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'") | ||
with subtest("Test initialPassword override"): | ||
machine.send_key("alt-f2") | ||
machine.wait_until_succeeds("[ $(fgconsole) = 2 ]") | ||
machine.wait_for_unit("[email protected]") | ||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty2'") | ||
machine.wait_until_tty_matches("2", "login: ") | ||
machine.send_chars("egon\n") | ||
machine.wait_until_tty_matches("2", "login: egon") | ||
machine.wait_until_succeeds("pgrep login") | ||
machine.sleep(2) | ||
machine.send_chars("${password1}\n") | ||
machine.send_chars("whoami > /tmp/1\n") | ||
machine.wait_for_file("/tmp/1") | ||
assert "egon" in machine.succeed("cat /tmp/1") | ||
with subtest("Test initialHashedPassword override"): | ||
machine.send_key("alt-f3") | ||
machine.wait_until_succeeds("[ $(fgconsole) = 3 ]") | ||
machine.wait_for_unit("[email protected]") | ||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty3'") | ||
machine.wait_until_tty_matches("3", "login: ") | ||
machine.send_chars("fran\n") | ||
machine.wait_until_tty_matches("3", "login: fran") | ||
machine.wait_until_succeeds("pgrep login") | ||
machine.sleep(2) | ||
machine.send_chars("meow\n") | ||
machine.send_chars("whoami > /tmp/3\n") | ||
machine.wait_for_file("/tmp/3") | ||
assert "fran" in machine.succeed("cat /tmp/3") | ||
''; | ||
} | ||
) |
120 changes: 120 additions & 0 deletions
120
nixos/tests/systemd-sysusers-password-option-override-ordering.nix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
{ | ||
lib, | ||
pkgs ? import ../.., | ||
... | ||
}: | ||
let | ||
password = "test"; | ||
password1 = "test1"; | ||
hashedPassword = "$y$j9T$wLgKY231.8j.ciV2MfEXe1$P0k5j3bCwHgnwW0Ive3w4knrgpiA4TzhCYLAnHvDZ51"; # test | ||
hashedPassword1 = "$y$j9T$s8TyQJtNImvobhGM5Nlez0$3E8/O8EVGuA4sr1OQmrzi8GrRcy/AEhj454JjAn72A2"; # test | ||
hashed_sha512crypt = "$6$ymzs8WINZ5wGwQcV$VC2S0cQiX8NVukOLymysTPn4v1zJoJp3NGyhnqyv/dAf4NWZsBWYveQcj6gEJr4ZUjRBRjM0Pj1L8TCQ8hUUp0"; # meow | ||
|
||
hashedPasswordFile = pkgs.writeText "hashed-password" hashedPassword1; | ||
in | ||
{ | ||
name = "systemd-sysusers-password-option-override-ordering"; | ||
|
||
meta.maintainers = with lib.maintainers; [ fidgetingbits ]; | ||
|
||
nodes.machine = { | ||
systemd.sysusers.enable = true; | ||
system.etc.overlay.enable = true; | ||
boot.initrd.systemd.enable = true; | ||
|
||
users.mutableUsers = true; | ||
|
||
# NOTE: Below given A -> B it implies B overrides A . Each entry below builds off the next | ||
|
||
users.users.root = { | ||
hashedPasswordFile = lib.mkForce null; | ||
initialHashedPassword = password; | ||
}; | ||
|
||
# initialPassword -> initialHashedPassword | ||
users.users.alice = { | ||
isNormalUser = true; | ||
initialPassword = password; | ||
initialHashedPassword = hashedPassword; | ||
}; | ||
|
||
# initialPassword -> initialHashedPassword -> hashedPasswordFile | ||
users.users.bob = { | ||
isNormalUser = true; | ||
initialPassword = password; | ||
initialHashedPassword = hashedPassword; | ||
hashedPasswordFile = hashedPasswordFile.outPath; | ||
}; | ||
|
||
# Show that initialPassword -> password is not true for systemd-sysusers | ||
users.users.cat = { | ||
isNormalUser = true; | ||
initialPassword = password; | ||
password = password1; # We expect this not to override | ||
}; | ||
# Show that initialPassword -> password is not true for systemd-sysusers | ||
users.users.dan = { | ||
isNormalUser = true; | ||
initialHashedPassword = hashedPassword; | ||
hashedPassword = hashed_sha512crypt; # We expect this not to override | ||
}; | ||
}; | ||
|
||
testScript = '' | ||
machine.wait_for_unit("systemd-sysusers.service") | ||
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.alice:${password}" in sysusers_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" | ||
assert machine.succeed("stat -c '%a' /etc/gshadow") == "0\n" | ||
with subtest("alice user has correct password"): | ||
print(machine.succeed("getent passwd alice")) | ||
assert "${hashedPassword}" in machine.succeed("getent shadow alice"), "alice user password is not correct" | ||
with subtest("bob user has new password after switching to new generation"): | ||
print(machine.succeed("getent passwd bob")) | ||
print(machine.succeed("getent shadow bob")) | ||
assert "${hashedPassword1}" in machine.succeed("getent shadow bob"), "bob user password is not correct" | ||
machine.wait_for_unit("multi-user.target") | ||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'") | ||
with subtest("Test initialPassword override"): | ||
machine.send_key("alt-f2") | ||
machine.wait_until_succeeds("[ $(fgconsole) = 2 ]") | ||
machine.wait_for_unit("[email protected]") | ||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty2'") | ||
machine.wait_until_tty_matches("2", "login: ") | ||
machine.send_chars("cat\n") | ||
machine.wait_until_tty_matches("2", "login: cat") | ||
machine.wait_until_succeeds("pgrep login") | ||
machine.sleep(2) | ||
machine.send_chars("${password}\n") | ||
machine.send_chars("whoami > /tmp/1\n") | ||
machine.wait_for_file("/tmp/1") | ||
assert "cat" in machine.succeed("cat /tmp/1") | ||
with subtest("Test initialHashedPassword override"): | ||
machine.send_key("alt-f3") | ||
machine.wait_until_succeeds("[ $(fgconsole) = 3 ]") | ||
machine.wait_for_unit("[email protected]") | ||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty3'") | ||
machine.wait_until_tty_matches("3", "login: ") | ||
machine.send_chars("dan\n") | ||
machine.wait_until_tty_matches("3", "login: dan") | ||
machine.wait_until_succeeds("pgrep login") | ||
machine.sleep(2) | ||
machine.send_chars("test\n") | ||
machine.send_chars("whoami > /tmp/3\n") | ||
machine.wait_for_file("/tmp/3") | ||
assert "dan" in machine.succeed("cat /tmp/3") | ||
''; | ||
} |