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"}
|
"auth sufficient ${pkgs.pam_u2f}/lib/security/pam_u2f.so"}
|
||||||
${optionalString cfg.usbAuth
|
${optionalString cfg.usbAuth
|
||||||
"auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
|
"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.
|
# 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
|
# 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"}
|
"auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
|
||||||
${optionalString cfg.otpwAuth
|
${optionalString cfg.otpwAuth
|
||||||
"auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
|
"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
|
${optionalString use_ldap
|
||||||
"auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
|
"auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
|
||||||
${optionalString config.services.sssd.enable
|
${optionalString config.services.sssd.enable
|
||||||
|
@ -283,6 +283,7 @@ in rec {
|
|||||||
tests.leaps = callTest tests/leaps.nix { };
|
tests.leaps = callTest tests/leaps.nix { };
|
||||||
tests.nsd = callTest tests/nsd.nix {};
|
tests.nsd = callTest tests/nsd.nix {};
|
||||||
tests.openssh = callTest tests/openssh.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.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
|
||||||
tests.peerflix = callTest tests/peerflix.nix {};
|
tests.peerflix = callTest tests/peerflix.nix {};
|
||||||
tests.postgresql = callTest tests/postgresql.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