at master 3.4 kB view raw
1{ ... }: 2 3let 4 oathSnakeoilSecret = "cdd4083ef8ff1fa9178c6d46bfb1a3"; 5 6 # With HOTP mode the password is calculated based on a counter of 7 # how many passwords have been made. In this env, we'll always be on 8 # the 0th counter, so the password is static. 9 # 10 # Generated in nix-shell -p oath-toolkit 11 # via: oathtool -v -d6 -w10 cdd4083ef8ff1fa9178c6d46bfb1a3 12 # and picking a the first 4: 13 oathSnakeOilPassword1 = "143349"; 14 oathSnakeOilPassword2 = "801753"; 15 16 alicePassword = "foobar"; 17 # Generated via: mkpasswd -m sha-512 and passing in "foobar" 18 hashedAlicePassword = "$6$MsMrE1q.1HrCgTS$Vq2e/uILzYjSN836TobAyN9xh9oi7EmCmucnZID25qgPoibkw8qTCugiAPnn4eCGvn1A.7oEBFJaaGUaJsQQY."; 19 20in 21{ 22 name = "pam-oath-login"; 23 24 nodes.machine = 25 { ... }: 26 { 27 security.pam.oath = { 28 enable = true; 29 }; 30 31 users.users.alice = { 32 isNormalUser = true; 33 name = "alice"; 34 uid = 1000; 35 hashedPassword = hashedAlicePassword; 36 extraGroups = [ "wheel" ]; 37 createHome = true; 38 home = "/home/alice"; 39 }; 40 41 systemd.services.setupOathSnakeoilFile = { 42 wantedBy = [ "default.target" ]; 43 before = [ "default.target" ]; 44 unitConfig = { 45 type = "oneshot"; 46 RemainAfterExit = true; 47 }; 48 script = '' 49 touch /etc/users.oath 50 chmod 600 /etc/users.oath 51 chown root /etc/users.oath 52 echo "HOTP/E/6 alice - ${oathSnakeoilSecret}" > /etc/users.oath 53 ''; 54 }; 55 }; 56 57 testScript = '' 58 def switch_to_tty(tty_number): 59 machine.fail(f"pgrep -f 'agetty.*tty{tty_number}'") 60 machine.send_key(f"alt-f{tty_number}") 61 machine.wait_until_succeeds(f"[ $(fgconsole) = {tty_number} ]") 62 machine.wait_for_unit(f"getty@tty{tty_number}.service") 63 machine.wait_until_succeeds(f"pgrep -f 'agetty.*tty{tty_number}'") 64 65 66 def enter_user_alice(tty_number): 67 machine.wait_until_tty_matches(tty_number, "login: ") 68 machine.send_chars("alice\n") 69 machine.wait_until_tty_matches(tty_number, "login: alice") 70 machine.wait_until_succeeds("pgrep login") 71 machine.wait_until_tty_matches(tty_number, "One-time password") 72 73 74 machine.wait_for_unit("multi-user.target") 75 machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'") 76 machine.screenshot("postboot") 77 78 with subtest("Invalid password"): 79 switch_to_tty("2") 80 enter_user_alice("2") 81 82 machine.send_chars("${oathSnakeOilPassword1}\n") 83 machine.wait_until_tty_matches("2", "Password: ") 84 machine.send_chars("blorg\n") 85 machine.wait_until_tty_matches("2", "Login incorrect") 86 87 with subtest("Invalid oath token"): 88 switch_to_tty("3") 89 enter_user_alice("3") 90 91 machine.send_chars("000000\n") 92 machine.wait_until_tty_matches("3", "Login incorrect") 93 machine.wait_until_tty_matches("3", "login:") 94 95 with subtest("Happy path: Both passwords are mandatory to get us in"): 96 switch_to_tty("4") 97 enter_user_alice("4") 98 99 machine.send_chars("${oathSnakeOilPassword2}\n") 100 machine.wait_until_tty_matches("4", "Password: ") 101 machine.send_chars("${alicePassword}\n") 102 103 machine.wait_until_succeeds("pgrep -u alice bash") 104 machine.send_chars("touch done4\n") 105 machine.wait_for_file("/home/alice/done4") 106 ''; 107}