nixos/pam: support fscrypt login protectors
fscrypt can automatically unlock directories with the user's login password. To do this it ships a PAM module which reads the user's password and loads the respective keys into the user's kernel keyring. Significant inspiration was taken from the ecryptfs implementation.
This commit is contained in:
parent
db3f2b35d3
commit
f046cc0923
3 changed files with 81 additions and 0 deletions
|
@ -526,6 +526,7 @@ let
|
|||
# We use try_first_pass the second time to avoid prompting password twice
|
||||
(optionalString (cfg.unixAuth &&
|
||||
(config.security.pam.enableEcryptfs
|
||||
|| config.security.pam.enableFscrypt
|
||||
|| cfg.pamMount
|
||||
|| cfg.enableKwallet
|
||||
|| cfg.enableGnomeKeyring
|
||||
|
@ -539,6 +540,9 @@ let
|
|||
optionalString config.security.pam.enableEcryptfs ''
|
||||
auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap
|
||||
'' +
|
||||
optionalString config.security.pam.enableFscrypt ''
|
||||
auth optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so
|
||||
'' +
|
||||
optionalString cfg.pamMount ''
|
||||
auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive
|
||||
'' +
|
||||
|
@ -584,6 +588,9 @@ let
|
|||
optionalString config.security.pam.enableEcryptfs ''
|
||||
password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so
|
||||
'' +
|
||||
optionalString config.security.pam.enableFscrypt ''
|
||||
password optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so
|
||||
'' +
|
||||
optionalString cfg.pamMount ''
|
||||
password optional ${pkgs.pam_mount}/lib/security/pam_mount.so
|
||||
'' +
|
||||
|
@ -630,6 +637,14 @@ let
|
|||
optionalString config.security.pam.enableEcryptfs ''
|
||||
session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so
|
||||
'' +
|
||||
optionalString config.security.pam.enableFscrypt ''
|
||||
# Work around https://github.com/systemd/systemd/issues/8598
|
||||
# Skips the pam_fscrypt module for systemd-user sessions which do not have a password
|
||||
# anyways.
|
||||
# See also https://github.com/google/fscrypt/issues/95
|
||||
session [success=1 default=ignore] pam_succeed_if.so service = systemd-user
|
||||
session optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so
|
||||
'' +
|
||||
optionalString cfg.pamMount ''
|
||||
session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive
|
||||
'' +
|
||||
|
@ -1146,6 +1161,14 @@ in
|
|||
};
|
||||
|
||||
security.pam.enableEcryptfs = mkEnableOption (lib.mdDoc "eCryptfs PAM module (mounting ecryptfs home directory on login)");
|
||||
security.pam.enableFscrypt = mkEnableOption (lib.mdDoc ''
|
||||
Enables fscrypt to automatically unlock directories with the user's login password.
|
||||
|
||||
This also enables a service at security.pam.services.fscrypt which is used by
|
||||
fscrypt to verify the user's password when setting up a new protector. If you
|
||||
use something other than pam_unix to verify user passwords, please remember to
|
||||
adjust this PAM service.
|
||||
'');
|
||||
|
||||
users.motd = mkOption {
|
||||
default = null;
|
||||
|
@ -1170,6 +1193,7 @@ in
|
|||
++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
|
||||
++ optionals config.security.pam.oath.enable [ pkgs.oath-toolkit ]
|
||||
++ optionals config.security.pam.p11.enable [ pkgs.pam_p11 ]
|
||||
++ optionals config.security.pam.enableFscrypt [ pkgs.fscrypt-experimental ]
|
||||
++ optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ];
|
||||
|
||||
boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs [ "ecryptfs" ];
|
||||
|
@ -1211,6 +1235,9 @@ in
|
|||
it complains "Cannot create session: Already running in a
|
||||
session". */
|
||||
runuser-l = { rootOK = true; unixAuth = false; };
|
||||
} // optionalAttrs (config.security.pam.enableFscrypt) {
|
||||
# Allow fscrypt to verify login passphrase
|
||||
fscrypt = {};
|
||||
};
|
||||
|
||||
security.apparmor.includes."abstractions/pam" = let
|
||||
|
@ -1275,6 +1302,9 @@ in
|
|||
optionalString config.security.pam.enableEcryptfs ''
|
||||
mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,
|
||||
'' +
|
||||
optionalString config.security.pam.enableFscrypt ''
|
||||
mr ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so,
|
||||
'' +
|
||||
optionalString (isEnabled (cfg: cfg.pamMount)) ''
|
||||
mr ${pkgs.pam_mount}/lib/security/pam_mount.so,
|
||||
'' +
|
||||
|
|
|
@ -178,6 +178,7 @@ in {
|
|||
ec2-config = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-config or {};
|
||||
ec2-nixops = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-nixops or {};
|
||||
ecryptfs = handleTest ./ecryptfs.nix {};
|
||||
fscrypt = handleTest ./fscrypt.nix {};
|
||||
ejabberd = handleTest ./xmpp/ejabberd.nix {};
|
||||
elk = handleTestOn ["x86_64-linux"] ./elk.nix {};
|
||||
emacs-daemon = handleTest ./emacs-daemon.nix {};
|
||||
|
|
50
nixos/tests/fscrypt.nix
Normal file
50
nixos/tests/fscrypt.nix
Normal file
|
@ -0,0 +1,50 @@
|
|||
import ./make-test-python.nix ({ ... }:
|
||||
{
|
||||
name = "fscrypt";
|
||||
|
||||
nodes.machine = { pkgs, ... }: {
|
||||
imports = [ ./common/user-account.nix ];
|
||||
security.pam.enableFscrypt = true;
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
def login_as_alice():
|
||||
machine.wait_until_tty_matches("1", "login: ")
|
||||
machine.send_chars("alice\n")
|
||||
machine.wait_until_tty_matches("1", "Password: ")
|
||||
machine.send_chars("foobar\n")
|
||||
machine.wait_until_tty_matches("1", "alice\@machine")
|
||||
|
||||
|
||||
def logout():
|
||||
machine.send_chars("logout\n")
|
||||
machine.wait_until_tty_matches("1", "login: ")
|
||||
|
||||
|
||||
machine.wait_for_unit("default.target")
|
||||
|
||||
with subtest("Enable fscrypt on filesystem"):
|
||||
machine.succeed("tune2fs -O encrypt /dev/vda")
|
||||
machine.succeed("fscrypt setup --quiet --force --time=1ms")
|
||||
|
||||
with subtest("Set up alice with an fscrypt-enabled home directory"):
|
||||
machine.succeed("(echo foobar; echo foobar) | passwd alice")
|
||||
machine.succeed("chown -R alice.users ~alice")
|
||||
machine.succeed("echo foobar | fscrypt encrypt --skip-unlock --source=pam_passphrase --user=alice /home/alice")
|
||||
|
||||
with subtest("Create file as alice"):
|
||||
login_as_alice()
|
||||
machine.succeed("echo hello > /home/alice/world")
|
||||
logout()
|
||||
# Wait for logout to be processed
|
||||
machine.sleep(1)
|
||||
|
||||
with subtest("File should not be readable without being logged in as alice"):
|
||||
machine.fail("cat /home/alice/world")
|
||||
|
||||
with subtest("File should be readable again as alice"):
|
||||
login_as_alice()
|
||||
machine.succeed("cat /home/alice/world")
|
||||
logout()
|
||||
'';
|
||||
})
|
Loading…
Reference in a new issue