pam_oath: require OATH and pam_unix credentials to be valid
This commit is contained in:
parent
59e77daf5b
commit
96d767de62
@ -253,6 +253,8 @@ let
|
||||
"auth sufficient ${pkgs.pam_u2f}/lib/security/pam_u2f.so"}
|
||||
${optionalString cfg.usbAuth
|
||||
"auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
|
||||
${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
|
||||
"auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
|
||||
'' +
|
||||
# Modules in this block require having the password set in PAM_AUTHTOK.
|
||||
# pam_unix is marked as 'sufficient' on NixOS which means nothing will run
|
||||
@ -271,8 +273,6 @@ let
|
||||
"auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
|
||||
${optionalString cfg.otpwAuth
|
||||
"auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
|
||||
${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
|
||||
"auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
|
||||
${optionalString use_ldap
|
||||
"auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
|
||||
${optionalString config.services.sssd.enable
|
||||
|
@ -283,6 +283,7 @@ in rec {
|
||||
tests.leaps = callTest tests/leaps.nix { };
|
||||
tests.nsd = callTest tests/nsd.nix {};
|
||||
tests.openssh = callTest tests/openssh.nix {};
|
||||
tests.pam-oath-login = callTest tests/pam-oath-login.nix {};
|
||||
#tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
|
||||
tests.peerflix = callTest tests/peerflix.nix {};
|
||||
tests.postgresql = callTest tests/postgresql.nix {};
|
||||
|
126
nixos/tests/pam-oath-login.nix
Normal file
126
nixos/tests/pam-oath-login.nix
Normal file
@ -0,0 +1,126 @@
|
||||
import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
|
||||
|
||||
let
|
||||
oathSnakeoilSecret = "cdd4083ef8ff1fa9178c6d46bfb1a3";
|
||||
|
||||
# With HOTP mode the password is calculated based on a counter of
|
||||
# how many passwords have been made. In this env, we'll always be on
|
||||
# the 0th counter, so the password is static.
|
||||
#
|
||||
# Generated in nix-shell -p oathToolkit
|
||||
# via: oathtool -v -d6 -w10 cdd4083ef8ff1fa9178c6d46bfb1a3
|
||||
# and picking a the first 4:
|
||||
oathSnakeOilPassword1 = "143349";
|
||||
oathSnakeOilPassword2 = "801753";
|
||||
oathSnakeOilPassword3 = "019933";
|
||||
oathSnakeOilPassword4 = "403895";
|
||||
|
||||
alicePassword = "foobar";
|
||||
# Generated via: mkpasswd -m sha-512 and passing in "foobar"
|
||||
hashedAlicePassword = "$6$MsMrE1q.1HrCgTS$Vq2e/uILzYjSN836TobAyN9xh9oi7EmCmucnZID25qgPoibkw8qTCugiAPnn4eCGvn1A.7oEBFJaaGUaJsQQY.";
|
||||
|
||||
in
|
||||
{
|
||||
name = "pam-oath-login";
|
||||
|
||||
machine =
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
security.pam.oath = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
users.extraUsers.alice = {
|
||||
isNormalUser = true;
|
||||
name = "alice";
|
||||
uid = 1000;
|
||||
hashedPassword = hashedAlicePassword;
|
||||
extraGroups = [ "wheel" ];
|
||||
createHome = true;
|
||||
home = "/home/alice";
|
||||
};
|
||||
|
||||
|
||||
systemd.services.setupOathSnakeoilFile = {
|
||||
wantedBy = [ "default.target" ];
|
||||
before = [ "default.target" ];
|
||||
unitConfig = {
|
||||
type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
touch /etc/users.oath
|
||||
chmod 600 /etc/users.oath
|
||||
chown root /etc/users.oath
|
||||
echo "HOTP/E/6 alice - ${oathSnakeoilSecret}" > /etc/users.oath
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
''
|
||||
$machine->waitForUnit('multi-user.target');
|
||||
$machine->waitUntilSucceeds("pgrep -f 'agetty.*tty1'");
|
||||
$machine->screenshot("postboot");
|
||||
|
||||
|
||||
subtest "Invalid password", sub {
|
||||
$machine->fail("pgrep -f 'agetty.*tty2'");
|
||||
$machine->sendKeys("alt-f2");
|
||||
$machine->waitUntilSucceeds("[ \$(fgconsole) = 2 ]");
|
||||
$machine->waitForUnit('getty@tty2.service');
|
||||
$machine->waitUntilSucceeds("pgrep -f 'agetty.*tty2'");
|
||||
|
||||
$machine->waitUntilTTYMatches(2, "login: ");
|
||||
$machine->sendChars("alice\n");
|
||||
$machine->waitUntilTTYMatches(2, "login: alice");
|
||||
$machine->waitUntilSucceeds("pgrep login");
|
||||
|
||||
$machine->waitUntilTTYMatches(2, "One-time password");
|
||||
$machine->sendChars("${oathSnakeOilPassword1}\n");
|
||||
$machine->waitUntilTTYMatches(2, "Password: ");
|
||||
$machine->sendChars("blorg\n");
|
||||
$machine->waitUntilTTYMatches(2, "Login incorrect");
|
||||
};
|
||||
|
||||
subtest "Invalid oath token", sub {
|
||||
$machine->fail("pgrep -f 'agetty.*tty3'");
|
||||
$machine->sendKeys("alt-f3");
|
||||
$machine->waitUntilSucceeds("[ \$(fgconsole) = 3 ]");
|
||||
$machine->waitForUnit('getty@tty3.service');
|
||||
$machine->waitUntilSucceeds("pgrep -f 'agetty.*tty3'");
|
||||
|
||||
$machine->waitUntilTTYMatches(3, "login: ");
|
||||
$machine->sendChars("alice\n");
|
||||
$machine->waitUntilTTYMatches(3, "login: alice");
|
||||
$machine->waitUntilSucceeds("pgrep login");
|
||||
$machine->waitUntilTTYMatches(3, "One-time password");
|
||||
$machine->sendChars("000000\n");
|
||||
$machine->waitUntilTTYMatches(3, "Login incorrect");
|
||||
$machine->waitUntilTTYMatches(3, "login:");
|
||||
};
|
||||
|
||||
subtest "Happy path (both passwords are mandatory to get us in)", sub {
|
||||
$machine->fail("pgrep -f 'agetty.*tty4'");
|
||||
$machine->sendKeys("alt-f4");
|
||||
$machine->waitUntilSucceeds("[ \$(fgconsole) = 4 ]");
|
||||
$machine->waitForUnit('getty@tty4.service');
|
||||
$machine->waitUntilSucceeds("pgrep -f 'agetty.*tty4'");
|
||||
|
||||
$machine->waitUntilTTYMatches(4, "login: ");
|
||||
$machine->sendChars("alice\n");
|
||||
$machine->waitUntilTTYMatches(4, "login: alice");
|
||||
$machine->waitUntilSucceeds("pgrep login");
|
||||
$machine->waitUntilTTYMatches(4, "One-time password");
|
||||
$machine->sendChars("${oathSnakeOilPassword2}\n");
|
||||
$machine->waitUntilTTYMatches(4, "Password: ");
|
||||
$machine->sendChars("${alicePassword}\n");
|
||||
|
||||
$machine->waitUntilSucceeds("pgrep -u alice bash");
|
||||
$machine->sendChars("touch done4\n");
|
||||
$machine->waitForFile("/home/alice/done4");
|
||||
};
|
||||
|
||||
'';
|
||||
|
||||
})
|
Loading…
Reference in New Issue
Block a user