about summary refs log tree commit diff
path: root/nixos/modules/security/pam.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/security/pam.nix')
-rw-r--r--nixos/modules/security/pam.nix277
1 files changed, 161 insertions, 116 deletions
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index d74353f19b26e..e74858f4ce854 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -7,6 +7,13 @@ with lib;
 
 let
 
+  moduleSettingsType = with types; attrsOf (nullOr (oneOf [ bool str int pathInStore ]));
+  moduleSettingsDescription = ''
+    Boolean values render just the key if true, and nothing if false.
+    Null values are ignored.
+    All other values are rendered as key-value pairs.
+  '';
+
   mkRulesTypeOption = type: mkOption {
     # These options are experimental and subject to breaking changes without notice.
     description = ''
@@ -71,12 +78,12 @@ let
           '';
         };
         settings = mkOption {
-          type = with types; attrsOf (nullOr (oneOf [ bool str int pathInStore ]));
+          type = moduleSettingsType;
           default = {};
           description = ''
             Settings to add as `module-arguments`.
 
-            Boolean values render just the key if true, and nothing if false. Null values are ignored. All other values are rendered as key-value pairs.
+            ${moduleSettingsDescription}
           '';
         };
       };
@@ -92,6 +99,7 @@ let
     }));
   };
 
+  package = config.security.pam.package;
   parentConfig = config;
 
   pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in {
@@ -481,6 +489,18 @@ let
         package = mkPackageOption pkgs.plasma5Packages "kwallet-pam" {
           pkgsText = "pkgs.plasma5Packages";
         };
+
+        forceRun = mkEnableOption null // {
+          description = ''
+            The `force_run` option is used to tell the PAM module for KWallet
+            to forcefully run even if no graphical session (such as a GUI
+            display manager) is detected. This is useful for when you are
+            starting an X Session or a Wayland Session from a TTY. If you
+            intend to log-in from a TTY, it is recommended that you enable
+            this option **and** ensure that `plasma-kwallet-pam.service` is
+            started by `graphical-session.target`.
+          '';
+        };
       };
 
       sssdStrictAccess = mkOption {
@@ -630,7 +650,7 @@ let
           { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
             config_file = "/etc/security/pam_mysql.conf";
           }; }
-          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = {
+          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; settings = {
             ignore_unknown_user = true;
           }; }
           { name = "sss"; enable = config.services.sssd.enable; control = if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
@@ -641,16 +661,16 @@ let
           # The required pam_unix.so module has to come after all the sufficient modules
           # because otherwise, the account lookup will fail if the user does not exist
           # locally, for example with MySQL- or LDAP-auth.
-          { name = "unix"; control = "required"; modulePath = "pam_unix.so"; }
+          { name = "unix"; control = "required"; modulePath = "${package}/lib/security/pam_unix.so"; }
         ];
 
         auth = autoOrderRules ([
           { name = "oslogin_login"; enable = cfg.googleOsLoginAuthentication; control = "[success=done perm_denied=die default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; }
-          { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "pam_rootok.so"; }
-          { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; settings = {
+          { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "${package}/lib/security/pam_rootok.so"; }
+          { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "${package}/lib/security/pam_wheel.so"; settings = {
             use_uid = true;
           }; }
-          { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "pam_faillock.so"; }
+          { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "${package}/lib/security/pam_faillock.so"; }
           { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
             config_file = "/etc/security/pam_mysql.conf";
           }; }
@@ -660,11 +680,7 @@ let
           (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; modulePath = "${pkgs.pam_p11}/lib/security/pam_p11.so"; args = [
             "${pkgs.opensc}/lib/opensc-pkcs11.so"
           ]; })
-          (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; settings = {
-            inherit (u2f) debug interactive cue origin;
-            authfile = u2f.authFile;
-            appid = u2f.appId;
-          }; })
+          (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; inherit (u2f) settings; })
           (let ussh = config.security.pam.ussh; in { name = "ussh"; enable = config.security.pam.ussh.enable && cfg.usshAuth; control = ussh.control; modulePath = "${pkgs.pam_ussh}/lib/security/pam_ussh.so"; settings = {
             ca_file = ussh.caFile;
             authorized_principals = ussh.authorizedPrincipals;
@@ -707,7 +723,7 @@ let
               || cfg.zfs))
             [
               { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
-              { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "pam_unix.so"; settings = {
+              { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
                 nullok = cfg.allowNullPassword;
                 inherit (cfg) nodelay;
                 likeauth = true;
@@ -728,7 +744,7 @@ let
               { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
                 store-only = cfg.gnupg.storeOnly;
               }; }
-              { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; settings = {
+              { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${package}/lib/security/pam_faildelay.so"; settings = {
                 inherit (cfg.failDelay) delay;
               }; }
               { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; settings = {
@@ -737,7 +753,7 @@ let
               { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; }
             ]) ++ [
           { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
-          { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "pam_unix.so"; settings = {
+          { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
             nullok = cfg.allowNullPassword;
             inherit (cfg) nodelay;
             likeauth = true;
@@ -747,7 +763,7 @@ let
           { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; settings = {
             use_first_pass = true;
           }; }
-          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = {
+          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; settings = {
             ignore_unknown_user = true;
             use_first_pass = true;
           }; }
@@ -765,12 +781,12 @@ let
             action = "store";
             use_first_pass = true;
           }; }
-          { name = "deny"; control = "required"; modulePath = "pam_deny.so"; }
+          { name = "deny"; control = "required"; modulePath = "${package}/lib/security/pam_deny.so"; }
         ]);
 
         password = autoOrderRules [
           { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
-          { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; settings = {
+          { name = "unix"; control = "sufficient"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
             nullok = true;
             yescrypt = true;
           }; }
@@ -784,7 +800,7 @@ let
           { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
             config_file = "/etc/security/pam_mysql.conf";
           }; }
-          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; }
+          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; }
           { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
           { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = {
             use_first_pass = true;
@@ -795,24 +811,24 @@ let
         ];
 
         session = autoOrderRules [
-          { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; settings = {
+          { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "${package}/lib/security/pam_env.so"; settings = {
             conffile = "/etc/pam/environment";
             readenv = 0;
           }; }
-          { name = "unix"; control = "required"; modulePath = "pam_unix.so"; }
-          { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "pam_loginuid.so"; }
-          { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_tty_audit.so"; settings = {
+          { name = "unix"; control = "required"; modulePath = "${package}/lib/security/pam_unix.so"; }
+          { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "${package}/lib/security/pam_loginuid.so"; }
+          { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${package}/lib/security/pam_tty_audit.so"; settings = {
             open_only = cfg.ttyAudit.openOnly;
             enable = cfg.ttyAudit.enablePattern;
             disable = cfg.ttyAudit.disablePattern;
           }; }
           { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
-          { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_mkhomedir.so"; settings = {
+          { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${package}/lib/security/pam_mkhomedir.so"; settings = {
             silent = true;
             skel = config.security.pam.makeHomeDir.skelDirectory;
             inherit (config.security.pam.makeHomeDir) umask;
           }; }
-          { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; settings = {
+          { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${package}/lib/security/pam_lastlog.so"; settings = {
             silent = true;
           }; }
           { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; }
@@ -820,11 +836,11 @@ let
           # 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
-          { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; args = [
+          { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "${package}/lib/security/pam_succeed_if.so"; args = [
             "service" "=" "systemd-user"
           ]; }
           { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; }
-          { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; args = [
+          { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "${package}/lib/security/pam_succeed_if.so"; args = [
             "service" "=" "systemd-user"
           ]; }
           { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = {
@@ -838,26 +854,26 @@ let
           { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
             config_file = "/etc/security/pam_mysql.conf";
           }; }
-          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; }
+          { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; }
           { name = "sss"; enable = config.services.sssd.enable; control = "optional"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
           { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; }
           { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; }
           { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; }
-          { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "pam_xauth.so"; settings = {
+          { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "${package}/lib/security/pam_xauth.so"; settings = {
             xauthpath = "${pkgs.xorg.xauth}/bin/xauth";
             systemuser = 99;
           }; }
-          { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; settings = {
+          { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${package}/lib/security/pam_limits.so"; settings = {
             conf = "${makeLimitsConf cfg.limits}";
           }; }
-          { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; settings = {
+          { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${package}/lib/security/pam_motd.so"; settings = {
             inherit motd;
           }; }
           { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; settings = {
             order = "user,group,default";
             debug = true;
           }; }
-          { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; }
+          { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; settings = lib.mkIf cfg.kwallet.forceRun { force_run = true; }; }
           { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = {
             auto_start = true;
           }; }
@@ -952,12 +968,20 @@ in
   imports = [
     (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
     (mkRenamedOptionModule [ "security" "pam" "enableSSHAgentAuth" ] [ "security" "pam" "sshAgentAuth" "enable" ])
+    (mkRenamedOptionModule [ "security" "pam" "u2f" "authFile" ] [ "security" "pam" "u2f" "settings" "authfile" ])
+    (mkRenamedOptionModule [ "security" "pam" "u2f" "appId" ] [ "security" "pam" "u2f" "settings" "appid" ])
+    (mkRenamedOptionModule [ "security" "pam" "u2f" "origin" ] [ "security" "pam" "u2f" "settings" "origin" ])
+    (mkRenamedOptionModule [ "security" "pam" "u2f" "debug" ] [ "security" "pam" "u2f" "settings" "debug" ])
+    (mkRenamedOptionModule [ "security" "pam" "u2f" "interactive" ] [ "security" "pam" "u2f" "settings" "interactive" ])
+    (mkRenamedOptionModule [ "security" "pam" "u2f" "cue" ] [ "security" "pam" "u2f" "settings" "cue" ])
   ];
 
   ###### interface
 
   options = {
 
+    security.pam.package = mkPackageOption pkgs "pam" { };
+
     security.pam.loginLimits = mkOption {
       default = [];
       type = limitsType;
@@ -1144,57 +1168,6 @@ in
         '';
       };
 
-      authFile = mkOption {
-        default = null;
-        type = with types; nullOr path;
-        description = ''
-          By default `pam-u2f` module reads the keys from
-          {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
-          {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
-          not set).
-
-          If you want to change auth file locations or centralize database (for
-          example use {file}`/etc/u2f-mappings`) you can set this
-          option.
-
-          File format is:
-          `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key`
-          This file can be generated using {command}`pamu2fcfg` command.
-
-          More information can be found [here](https://developers.yubico.com/pam-u2f/).
-        '';
-      };
-
-      appId = mkOption {
-        default = null;
-        type = with types; nullOr str;
-        description = ''
-            By default `pam-u2f` module sets the application
-            ID to `pam://$HOSTNAME`.
-
-            When using {command}`pamu2fcfg`, you can specify your
-            application ID with the `-i` flag.
-
-            More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
-        '';
-      };
-
-      origin = mkOption {
-        default = null;
-        type = with types; nullOr str;
-        description = ''
-            By default `pam-u2f` module sets the origin
-            to `pam://$HOSTNAME`.
-            Setting origin to an host independent value will allow you to
-            reuse credentials across machines
-
-            When using {command}`pamu2fcfg`, you can specify your
-            application ID with the `-o` flag.
-
-            More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
-        '';
-      };
-
       control = mkOption {
         default = "sufficient";
         type = types.enum [ "required" "requisite" "sufficient" "optional" ];
@@ -1209,33 +1182,104 @@ in
         '';
       };
 
-      debug = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Debug output to stderr.
-        '';
-      };
-
-      interactive = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Set to prompt a message and wait before testing the presence of a U2F device.
-          Recommended if your device doesn’t have a tactile trigger.
-        '';
-      };
-
-      cue = mkOption {
-        default = false;
-        type = types.bool;
+      settings = mkOption {
+        type = types.submodule {
+          freeformType = moduleSettingsType;
+
+          options = {
+            authfile = mkOption {
+              default = null;
+              type = with types; nullOr path;
+              description = ''
+                By default `pam-u2f` module reads the keys from
+                {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
+                {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
+                not set).
+
+                If you want to change auth file locations or centralize database (for
+                example use {file}`/etc/u2f-mappings`) you can set this
+                option.
+
+                File format is:
+                `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key`
+                This file can be generated using {command}`pamu2fcfg` command.
+
+                More information can be found [here](https://developers.yubico.com/pam-u2f/).
+              '';
+            };
+
+            appid = mkOption {
+              default = null;
+              type = with types; nullOr str;
+              description = ''
+                  By default `pam-u2f` module sets the application
+                  ID to `pam://$HOSTNAME`.
+
+                  When using {command}`pamu2fcfg`, you can specify your
+                  application ID with the `-i` flag.
+
+                  More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
+              '';
+            };
+
+            origin = mkOption {
+              default = null;
+              type = with types; nullOr str;
+              description = ''
+                  By default `pam-u2f` module sets the origin
+                  to `pam://$HOSTNAME`.
+                  Setting origin to an host independent value will allow you to
+                  reuse credentials across machines
+
+                  When using {command}`pamu2fcfg`, you can specify your
+                  application ID with the `-o` flag.
+
+                  More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
+              '';
+            };
+
+            debug = mkOption {
+              default = false;
+              type = types.bool;
+              description = ''
+                Debug output to stderr.
+              '';
+            };
+
+            interactive = mkOption {
+              default = false;
+              type = types.bool;
+              description = ''
+                Set to prompt a message and wait before testing the presence of a U2F device.
+                Recommended if your device doesn’t have a tactile trigger.
+              '';
+            };
+
+            cue = mkOption {
+              default = false;
+              type = types.bool;
+              description = ''
+                By default `pam-u2f` module does not inform user
+                that he needs to use the u2f device, it just waits without a prompt.
+
+                If you set this option to `true`,
+                `cue` option is added to `pam-u2f`
+                module and reminder message will be displayed.
+              '';
+            };
+          };
+        };
+        default = { };
+        example = {
+          authfile = "/etc/u2f_keys";
+          authpending_file = "";
+          userpresence = 0;
+          pinverification = 1;
+        };
         description = ''
-          By default `pam-u2f` module does not inform user
-          that he needs to use the u2f device, it just waits without a prompt.
+          Options to pass to the PAM module.
 
-          If you set this option to `true`,
-          `cue` option is added to `pam-u2f`
-          module and reminder message will be displayed.
+          ${moduleSettingsDescription}
         '';
       };
     };
@@ -1486,9 +1530,9 @@ in
 
     environment.systemPackages =
       # Include the PAM modules in the system path mostly for the manpages.
-      [ pkgs.pam ]
+      [ package ]
       ++ optional config.users.ldap.enable pam_ldap
-      ++ optional config.services.kanidm.enablePam pkgs.kanidm
+      ++ optional config.services.kanidm.enablePam config.services.kanidm.package
       ++ optional config.services.sssd.enable pkgs.sssd
       ++ optionals config.security.pam.krb5.enable [pam_krb5 pam_ccreds]
       ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
@@ -1504,7 +1548,7 @@ in
         setuid = true;
         owner = "root";
         group = "root";
-        source = "${pkgs.pam}/bin/unix_chkpwd";
+        source = "${package}/bin/unix_chkpwd";
       };
     };
 
@@ -1545,11 +1589,6 @@ in
       lib.concatMapStrings
         (name: "r ${config.environment.etc."pam.d/${name}".source},\n")
         (attrNames config.security.pam.services) +
-      ''
-      mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
-      mr ${getLib pkgs.pam}/lib/security/pam_*.so,
-      r ${getLib pkgs.pam}/lib/security/,
-      '' +
       (with lib; pipe config.security.pam.services [
         attrValues
         (catAttrs "rules")
@@ -1557,6 +1596,12 @@ in
         (concatMap attrValues)
         (filter (rule: rule.enable))
         (catAttrs "modulePath")
+        # TODO(@uninsane): replace this warning + filter with just an assertion
+        (map (modulePath: lib.warnIfNot
+          (hasPrefix "/" modulePath)
+          ''non-absolute PAM modulePath "${modulePath}" is unsupported by apparmor and will be treated as an error by future versions of nixpkgs; see <https://github.com/NixOS/nixpkgs/pull/314791>''
+          modulePath
+        ))
         (filter (hasPrefix "/"))
         unique
         (map (module: "mr ${module},"))