From dd458977a005ed068d8b2fe1664049b9092e2d22 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sun, 24 Sep 2023 15:39:21 -0400 Subject: [PATCH 01/15] nixos/pam: clean up rules Makes the rules more uniform in structure and style. This makes it easier to automate subsequent commits. No behavior changes. --- nixos/modules/security/pam.nix | 38 ++++++++++++++-------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index d83259ccbebc..a28ee276ce3a 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -484,14 +484,11 @@ let optionalString cfg.mysqlAuth '' account sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf '' + - optionalString (config.services.kanidm.enablePam) '' + optionalString config.services.kanidm.enablePam '' account sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user '' + - optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false) '' - account sufficient ${pkgs.sssd}/lib/security/pam_sss.so - '' + - optionalString (config.services.sssd.enable && cfg.sssdStrictAccess) '' - account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so + optionalString config.services.sssd.enable '' + account ${if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"} ${pkgs.sssd}/lib/security/pam_sss.so '' + optionalString config.security.pam.krb5.enable '' account sufficient ${pam_krb5}/lib/security/pam_krb5.so @@ -532,10 +529,9 @@ let (let p11 = config.security.pam.p11; in optionalString cfg.p11Auth '' auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so '') + - (let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth ('' - auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} '' - + ''${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} - '')) + + (let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth '' + auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} + '') + optionalString cfg.usbAuth '' auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so '' + @@ -593,13 +589,13 @@ let auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive '' + optionalString cfg.enableKwallet '' - auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 '' + optionalString cfg.enableGnomeKeyring '' auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so '' + optionalString cfg.gnupg.enable '' - auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly " store-only"} + auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} '' + optionalString cfg.failDelay.enable '' auth optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} @@ -641,7 +637,8 @@ let '' + optionalString config.services.homed.enable '' password sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so - '' + '' + '' + + '' password sufficient pam_unix.so nullok yescrypt '' + optionalString config.security.pam.enableEcryptfs '' @@ -687,12 +684,9 @@ let optionalString cfg.setLoginUid '' session ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so '' + - optionalString cfg.ttyAudit.enable (concatStringsSep " \\\n " ([ - "session required ${pkgs.pam}/lib/security/pam_tty_audit.so" - ] ++ optional cfg.ttyAudit.openOnly "open_only" - ++ optional (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}" - ++ optional (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}" - )) + + optionalString cfg.ttyAudit.enable '' + session required ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} + '' + optionalString config.services.homed.enable '' session required ${config.systemd.package}/lib/security/pam_systemd_home.so '' + @@ -753,16 +747,16 @@ let optionalString (cfg.enableAppArmor && config.security.apparmor.enable) '' session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug '' + - optionalString (cfg.enableKwallet) '' + optionalString cfg.enableKwallet '' session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 '' + - optionalString (cfg.enableGnomeKeyring) '' + optionalString cfg.enableGnomeKeyring '' session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start '' + optionalString cfg.gnupg.enable '' session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} '' + - optionalString (config.virtualisation.lxc.lxcfs.enable) '' + optionalString config.virtualisation.lxc.lxcfs.enable '' session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all '' ); From 0f9d719d8ab58642d30aec627aa628efe44aa207 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sun, 24 Sep 2023 17:42:47 -0400 Subject: [PATCH 02/15] nixos/pam: split rule lists into individual rules --- nixos/modules/security/pam.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index a28ee276ce3a..2f4f6605548a 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -495,6 +495,8 @@ let '' + optionalString cfg.googleOsLoginAccountVerification '' account [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + '' + + optionalString cfg.googleOsLoginAccountVerification '' account [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so '' + optionalString config.services.homed.enable '' @@ -627,7 +629,11 @@ let '' + optionalString config.security.pam.krb5.enable '' auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + '' + + optionalString config.security.pam.krb5.enable '' auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass + '' + + optionalString config.security.pam.krb5.enable '' auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass '' + '' @@ -705,10 +711,14 @@ let # anyways. # See also https://github.com/google/fscrypt/issues/95 session [success=1 default=ignore] pam_succeed_if.so service = systemd-user + '' + + optionalString config.security.pam.enableFscrypt '' session optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so '' + optionalString cfg.zfs '' session [success=1 default=ignore] pam_succeed_if.so service = systemd-user + '' + + optionalString cfg.zfs '' session optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} '' + optionalString cfg.pamMount '' From d6bb805932ae8fdea7e516f40b2455109d4b4013 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Fri, 15 Sep 2023 22:25:18 -0400 Subject: [PATCH 03/15] nixos/pam: extract header comments Unblocks converting the rules from one big string to a rich data structure. --- nixos/modules/security/pam.nix | 409 ++++++++++++++++++--------------- 1 file changed, 227 insertions(+), 182 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 2f4f6605548a..4e12d7fc0691 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -6,6 +6,15 @@ with lib; let + + mkRulesTypeOption = type: mkOption { + # This option is experimental and subject to breaking changes without notice. + description = lib.mdDoc '' + PAM `${type}` rules for this service. + ''; + type = types.listOf types.str; + }; + parentConfig = config; pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in { @@ -18,6 +27,28 @@ let description = lib.mdDoc "Name of the PAM service."; }; + rules = mkOption { + # This option is experimental and subject to breaking changes without notice. + visible = false; + + description = lib.mdDoc '' + PAM rules for this service. + + ::: {.warning} + This option and its suboptions are experimental and subject to breaking changes without notice. + + If you use this option in your system configuration, you will need to manually monitor this module for any changes. Otherwise, failure to adjust your configuration properly could lead to you being locked out of your system, or worse, your system could be left wide open to attackers. + + If you share configuration examples that use this option, you MUST include this warning so that users are informed. + + You may freely use this option within `nixpkgs`, and future changes will account for those use sites. + ::: + ''; + type = types.submodule { + options = genAttrs [ "account" "auth" "password" "session" ] mkRulesTypeOption; + }; + }; + unixAuth = mkOption { default = true; type = types.bool; @@ -470,88 +501,104 @@ let setLoginUid = mkDefault cfg.startSession; limits = mkDefault config.security.pam.loginLimits; + text = let + formatRules = type: pipe cfg.rules.${type} [ + (map (removeSuffix "\n")) + (concatStringsSep "\n") + ]; + in mkDefault '' + # Account management. + ${formatRules "account"} + + # Authentication management. + ${formatRules "auth"} + + # Password management. + ${formatRules "password"} + + # Session management. + ${formatRules "session"} + ''; + # !!! TODO: move the LDAP stuff to the LDAP module, and the # Samba stuff to the Samba module. This requires that the PAM # module provides the right hooks. - text = mkDefault - ( - '' - # Account management. - '' + - optionalString use_ldap '' + rules = { + account = + optional use_ldap '' account sufficient ${pam_ldap}/lib/security/pam_ldap.so - '' + - optionalString cfg.mysqlAuth '' + '' ++ + optional cfg.mysqlAuth '' account sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' + - optionalString config.services.kanidm.enablePam '' + '' ++ + optional config.services.kanidm.enablePam '' account sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user - '' + - optionalString config.services.sssd.enable '' + '' ++ + optional config.services.sssd.enable '' account ${if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"} ${pkgs.sssd}/lib/security/pam_sss.so - '' + - optionalString config.security.pam.krb5.enable '' + '' ++ + optional config.security.pam.krb5.enable '' account sufficient ${pam_krb5}/lib/security/pam_krb5.so - '' + - optionalString cfg.googleOsLoginAccountVerification '' + '' ++ + optional cfg.googleOsLoginAccountVerification '' account [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so - '' + - optionalString cfg.googleOsLoginAccountVerification '' + '' ++ + optional cfg.googleOsLoginAccountVerification '' account [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so - '' + - optionalString config.services.homed.enable '' + '' ++ + optional config.services.homed.enable '' account sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so - '' + + '' ++ # 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. - '' + singleton '' account required pam_unix.so + ''; - # Authentication management. - '' + - optionalString cfg.googleOsLoginAuthentication '' + auth = + optional cfg.googleOsLoginAuthentication '' auth [success=done perm_denied=die default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so - '' + - optionalString cfg.rootOK '' + '' ++ + optional cfg.rootOK '' auth sufficient pam_rootok.so - '' + - optionalString cfg.requireWheel '' + '' ++ + optional cfg.requireWheel '' auth required pam_wheel.so use_uid - '' + - optionalString cfg.logFailures '' + '' ++ + optional cfg.logFailures '' auth required pam_faillock.so - '' + - optionalString cfg.mysqlAuth '' + '' ++ + optional cfg.mysqlAuth '' auth sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' + - optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) '' + '' ++ + optional (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) '' auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} - '' + - (let p11 = config.security.pam.p11; in optionalString cfg.p11Auth '' + '' ++ + (let p11 = config.security.pam.p11; in optional cfg.p11Auth '' auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so - '') + - (let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth '' + '') ++ + (let u2f = config.security.pam.u2f; in optional cfg.u2fAuth '' auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} - '') + - optionalString cfg.usbAuth '' + '') ++ + optional cfg.usbAuth '' auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so - '' + - (let ussh = config.security.pam.ussh; in optionalString (config.security.pam.ussh.enable && cfg.usshAuth) '' + '' ++ + (let ussh = config.security.pam.ussh; in optional (config.security.pam.ussh.enable && cfg.usshAuth) '' auth ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} - '') + - (let oath = config.security.pam.oath; in optionalString cfg.oathAuth '' + '') ++ + (let oath = config.security.pam.oath; in optional cfg.oathAuth '' auth requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} - '') + - (let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth '' + '') ++ + (let yubi = config.security.pam.yubico; in optional cfg.yubicoAuth '' auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} - '') + - (let dp9ik = config.security.pam.dp9ik; in optionalString dp9ik.enable '' + '') ++ + (let dp9ik = config.security.pam.dp9ik; in optional dp9ik.enable '' auth ${dp9ik.control} ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} - '') + - optionalString cfg.fprintAuth '' + '') ++ + optional cfg.fprintAuth '' auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so - '' + + '' ++ # 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 # after it succeeds. Certain modules need to run after pam_unix @@ -560,7 +607,7 @@ let # We use try_first_pass the second time to avoid prompting password twice. # # The same principle applies to systemd-homed - (optionalString ((cfg.unixAuth || config.services.homed.enable) && + (optionals ((cfg.unixAuth || config.services.homed.enable) && (config.security.pam.enableEcryptfs || config.security.pam.enableFscrypt || cfg.pamMount @@ -572,204 +619,202 @@ let || cfg.duoSecurity.enable || cfg.zfs)) ( - optionalString config.services.homed.enable '' + optional config.services.homed.enable '' auth optional ${config.systemd.package}/lib/security/pam_systemd_home.so - '' + - optionalString cfg.unixAuth '' + '' ++ + optional cfg.unixAuth '' auth optional pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth - '' + - optionalString config.security.pam.enableEcryptfs '' + '' ++ + optional config.security.pam.enableEcryptfs '' auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap - '' + - optionalString config.security.pam.enableFscrypt '' + '' ++ + optional config.security.pam.enableFscrypt '' auth optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so - '' + - optionalString cfg.zfs '' + '' ++ + optional cfg.zfs '' auth optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} - '' + - optionalString cfg.pamMount '' + '' ++ + optional cfg.pamMount '' auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive - '' + - optionalString cfg.enableKwallet '' + '' ++ + optional cfg.enableKwallet '' auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 - '' + - optionalString cfg.enableGnomeKeyring '' + '' ++ + optional cfg.enableGnomeKeyring '' auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so - '' + - optionalString cfg.gnupg.enable '' + '' ++ + optional cfg.gnupg.enable '' auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} - '' + - optionalString cfg.failDelay.enable '' + '' ++ + optional cfg.failDelay.enable '' auth optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} - '' + - optionalString cfg.googleAuthenticator.enable '' + '' ++ + optional cfg.googleAuthenticator.enable '' auth required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp - '' + - optionalString cfg.duoSecurity.enable '' + '' ++ + optional cfg.duoSecurity.enable '' auth required ${pkgs.duo-unix}/lib/security/pam_duo.so '' - )) + - optionalString config.services.homed.enable '' + )) ++ + optional config.services.homed.enable '' auth sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so - '' + - optionalString cfg.unixAuth '' + '' ++ + optional cfg.unixAuth '' auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass - '' + - optionalString cfg.otpwAuth '' + '' ++ + optional cfg.otpwAuth '' auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so - '' + - optionalString use_ldap '' + '' ++ + optional use_ldap '' auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass - '' + - optionalString config.services.kanidm.enablePam '' + '' ++ + optional config.services.kanidm.enablePam '' auth sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass - '' + - optionalString config.services.sssd.enable '' + '' ++ + optional config.services.sssd.enable '' auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass - '' + - optionalString config.security.pam.krb5.enable '' + '' ++ + optional config.security.pam.krb5.enable '' auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass - '' + - optionalString config.security.pam.krb5.enable '' + '' ++ + optional config.security.pam.krb5.enable '' auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass - '' + - optionalString config.security.pam.krb5.enable '' + '' ++ + optional config.security.pam.krb5.enable '' auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass - '' + - '' + '' ++ + singleton '' auth required pam_deny.so + ''; - # Password management. - '' + - optionalString config.services.homed.enable '' + password = + optional config.services.homed.enable '' password sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so - '' + - '' + '' ++ + singleton '' password sufficient pam_unix.so nullok yescrypt - '' + - optionalString config.security.pam.enableEcryptfs '' + '' ++ + optional config.security.pam.enableEcryptfs '' password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so - '' + - optionalString config.security.pam.enableFscrypt '' + '' ++ + optional config.security.pam.enableFscrypt '' password optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so - '' + - optionalString cfg.zfs '' + '' ++ + optional cfg.zfs '' password optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} - '' + - optionalString cfg.pamMount '' + '' ++ + optional cfg.pamMount '' password optional ${pkgs.pam_mount}/lib/security/pam_mount.so - '' + - optionalString use_ldap '' + '' ++ + optional use_ldap '' password sufficient ${pam_ldap}/lib/security/pam_ldap.so - '' + - optionalString cfg.mysqlAuth '' + '' ++ + optional cfg.mysqlAuth '' password sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' + - optionalString config.services.kanidm.enablePam '' + '' ++ + optional config.services.kanidm.enablePam '' password sufficient ${pkgs.kanidm}/lib/pam_kanidm.so - '' + - optionalString config.services.sssd.enable '' + '' ++ + optional config.services.sssd.enable '' password sufficient ${pkgs.sssd}/lib/security/pam_sss.so - '' + - optionalString config.security.pam.krb5.enable '' + '' ++ + optional config.security.pam.krb5.enable '' password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass - '' + - optionalString cfg.enableGnomeKeyring '' + '' ++ + optional cfg.enableGnomeKeyring '' password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok - '' + - '' + ''; - # Session management. - '' + - optionalString cfg.setEnvironment '' + session = + optional cfg.setEnvironment '' session required pam_env.so conffile=/etc/pam/environment readenv=0 - '' + - '' + '' ++ + singleton '' session required pam_unix.so - '' + - optionalString cfg.setLoginUid '' + '' ++ + optional cfg.setLoginUid '' session ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so - '' + - optionalString cfg.ttyAudit.enable '' + '' ++ + optional cfg.ttyAudit.enable '' session required ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} - '' + - optionalString config.services.homed.enable '' + '' ++ + optional config.services.homed.enable '' session required ${config.systemd.package}/lib/security/pam_systemd_home.so - '' + - optionalString cfg.makeHomeDir '' + '' ++ + optional cfg.makeHomeDir '' session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} - '' + - optionalString cfg.updateWtmp '' + '' ++ + optional cfg.updateWtmp '' session required ${pkgs.pam}/lib/security/pam_lastlog.so silent - '' + - optionalString config.security.pam.enableEcryptfs '' + '' ++ + optional config.security.pam.enableEcryptfs '' session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so - '' + - optionalString config.security.pam.enableFscrypt '' + '' ++ + optional 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 - '' + - optionalString config.security.pam.enableFscrypt '' + '' ++ + optional config.security.pam.enableFscrypt '' session optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so - '' + - optionalString cfg.zfs '' + '' ++ + optional cfg.zfs '' session [success=1 default=ignore] pam_succeed_if.so service = systemd-user - '' + - optionalString cfg.zfs '' + '' ++ + optional cfg.zfs '' session optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} - '' + - optionalString cfg.pamMount '' + '' ++ + optional cfg.pamMount '' session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive - '' + - optionalString use_ldap '' + '' ++ + optional use_ldap '' session optional ${pam_ldap}/lib/security/pam_ldap.so - '' + - optionalString cfg.mysqlAuth '' + '' ++ + optional cfg.mysqlAuth '' session optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' + - optionalString config.services.kanidm.enablePam '' + '' ++ + optional config.services.kanidm.enablePam '' session optional ${pkgs.kanidm}/lib/pam_kanidm.so - '' + - optionalString config.services.sssd.enable '' + '' ++ + optional config.services.sssd.enable '' session optional ${pkgs.sssd}/lib/security/pam_sss.so - '' + - optionalString config.security.pam.krb5.enable '' + '' ++ + optional config.security.pam.krb5.enable '' session optional ${pam_krb5}/lib/security/pam_krb5.so - '' + - optionalString cfg.otpwAuth '' + '' ++ + optional cfg.otpwAuth '' session optional ${pkgs.otpw}/lib/security/pam_otpw.so - '' + - optionalString cfg.startSession '' + '' ++ + optional cfg.startSession '' session optional ${config.systemd.package}/lib/security/pam_systemd.so - '' + - optionalString cfg.forwardXAuth '' + '' ++ + optional cfg.forwardXAuth '' session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 - '' + - optionalString (cfg.limits != []) '' + '' ++ + optional (cfg.limits != []) '' session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} - '' + - optionalString (cfg.showMotd && (config.users.motd != null || config.users.motdFile != null)) '' + '' ++ + optional (cfg.showMotd && (config.users.motd != null || config.users.motdFile != null)) '' session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} - '' + - optionalString (cfg.enableAppArmor && config.security.apparmor.enable) '' + '' ++ + optional (cfg.enableAppArmor && config.security.apparmor.enable) '' session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug - '' + - optionalString cfg.enableKwallet '' + '' ++ + optional cfg.enableKwallet '' session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 - '' + - optionalString cfg.enableGnomeKeyring '' + '' ++ + optional cfg.enableGnomeKeyring '' session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start - '' + - optionalString cfg.gnupg.enable '' + '' ++ + optional cfg.gnupg.enable '' session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} - '' + - optionalString config.virtualisation.lxc.lxcfs.enable '' + '' ++ + optional config.virtualisation.lxc.lxcfs.enable '' session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all - '' - ); + ''; + }; }; }; From 3c85d159f780250cd95cc0f50b13ad169399b92b Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Fri, 15 Sep 2023 22:15:24 -0400 Subject: [PATCH 04/15] nixos/pam: automatically populate rule type Eliminates a redundancy between the 'rules' suboptions and the type specified in each rule. We eventually want to give each rule a name so that we can merge config overrides. The PAM name is a natural choice for rule name, but a PAM is often used in multiple rule types. Organizing rules by type and rule name avoids name collisions. --- nixos/modules/security/pam.nix | 174 +++++++++++++++++---------------- 1 file changed, 88 insertions(+), 86 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 4e12d7fc0691..87b5afcf0bdc 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -503,7 +503,9 @@ let text = let formatRules = type: pipe cfg.rules.${type} [ - (map (removeSuffix "\n")) + (map (rule: concatStringsSep " " + [ type (removeSuffix "\n" rule) ] + )) (concatStringsSep "\n") ]; in mkDefault '' @@ -526,78 +528,78 @@ let rules = { account = optional use_ldap '' - account sufficient ${pam_ldap}/lib/security/pam_ldap.so + sufficient ${pam_ldap}/lib/security/pam_ldap.so '' ++ optional cfg.mysqlAuth '' - account sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf '' ++ optional config.services.kanidm.enablePam '' - account sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user + sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user '' ++ optional config.services.sssd.enable '' - account ${if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"} ${pkgs.sssd}/lib/security/pam_sss.so + ${if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"} ${pkgs.sssd}/lib/security/pam_sss.so '' ++ optional config.security.pam.krb5.enable '' - account sufficient ${pam_krb5}/lib/security/pam_krb5.so + sufficient ${pam_krb5}/lib/security/pam_krb5.so '' ++ optional cfg.googleOsLoginAccountVerification '' - account [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so '' ++ optional cfg.googleOsLoginAccountVerification '' - account [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so + [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so '' ++ optional config.services.homed.enable '' - account sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so '' ++ # 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. singleton '' - account required pam_unix.so + required pam_unix.so ''; auth = optional cfg.googleOsLoginAuthentication '' - auth [success=done perm_denied=die default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + [success=done perm_denied=die default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so '' ++ optional cfg.rootOK '' - auth sufficient pam_rootok.so + sufficient pam_rootok.so '' ++ optional cfg.requireWheel '' - auth required pam_wheel.so use_uid + required pam_wheel.so use_uid '' ++ optional cfg.logFailures '' - auth required pam_faillock.so + required pam_faillock.so '' ++ optional cfg.mysqlAuth '' - auth sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf '' ++ optional (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) '' - auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} + sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} '' ++ (let p11 = config.security.pam.p11; in optional cfg.p11Auth '' - auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so + ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so '') ++ (let u2f = config.security.pam.u2f; in optional cfg.u2fAuth '' - auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} + ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} '') ++ optional cfg.usbAuth '' - auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so + sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so '' ++ (let ussh = config.security.pam.ussh; in optional (config.security.pam.ussh.enable && cfg.usshAuth) '' - auth ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} + ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} '') ++ (let oath = config.security.pam.oath; in optional cfg.oathAuth '' - auth requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} + requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} '') ++ (let yubi = config.security.pam.yubico; in optional cfg.yubicoAuth '' - auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} + ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} '') ++ (let dp9ik = config.security.pam.dp9ik; in optional dp9ik.enable '' - auth ${dp9ik.control} ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} + ${dp9ik.control} ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} '') ++ optional cfg.fprintAuth '' - auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so + sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so '' ++ # 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 @@ -620,199 +622,199 @@ let || cfg.zfs)) ( optional config.services.homed.enable '' - auth optional ${config.systemd.package}/lib/security/pam_systemd_home.so + optional ${config.systemd.package}/lib/security/pam_systemd_home.so '' ++ optional cfg.unixAuth '' - auth optional pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth + optional pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth '' ++ optional config.security.pam.enableEcryptfs '' - auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap + optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap '' ++ optional config.security.pam.enableFscrypt '' - auth optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so '' ++ optional cfg.zfs '' - auth optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} '' ++ optional cfg.pamMount '' - auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive '' ++ optional cfg.enableKwallet '' - auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 '' ++ optional cfg.enableGnomeKeyring '' - auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so + optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so '' ++ optional cfg.gnupg.enable '' - auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} + optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} '' ++ optional cfg.failDelay.enable '' - auth optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} + optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} '' ++ optional cfg.googleAuthenticator.enable '' - auth required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp + required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp '' ++ optional cfg.duoSecurity.enable '' - auth required ${pkgs.duo-unix}/lib/security/pam_duo.so + required ${pkgs.duo-unix}/lib/security/pam_duo.so '' )) ++ optional config.services.homed.enable '' - auth sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so '' ++ optional cfg.unixAuth '' - auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass + sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass '' ++ optional cfg.otpwAuth '' - auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so + sufficient ${pkgs.otpw}/lib/security/pam_otpw.so '' ++ optional use_ldap '' - auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass + sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass '' ++ optional config.services.kanidm.enablePam '' - auth sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass + sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass '' ++ optional config.services.sssd.enable '' - auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass + sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass '' ++ optional config.security.pam.krb5.enable '' - auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass '' ++ optional config.security.pam.krb5.enable '' - auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass + [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass '' ++ optional config.security.pam.krb5.enable '' - auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass + sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass '' ++ singleton '' - auth required pam_deny.so + required pam_deny.so ''; password = optional config.services.homed.enable '' - password sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so '' ++ singleton '' - password sufficient pam_unix.so nullok yescrypt + sufficient pam_unix.so nullok yescrypt '' ++ optional config.security.pam.enableEcryptfs '' - password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so '' ++ optional config.security.pam.enableFscrypt '' - password optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so '' ++ optional cfg.zfs '' - password optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} '' ++ optional cfg.pamMount '' - password optional ${pkgs.pam_mount}/lib/security/pam_mount.so + optional ${pkgs.pam_mount}/lib/security/pam_mount.so '' ++ optional use_ldap '' - password sufficient ${pam_ldap}/lib/security/pam_ldap.so + sufficient ${pam_ldap}/lib/security/pam_ldap.so '' ++ optional cfg.mysqlAuth '' - password sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf '' ++ optional config.services.kanidm.enablePam '' - password sufficient ${pkgs.kanidm}/lib/pam_kanidm.so + sufficient ${pkgs.kanidm}/lib/pam_kanidm.so '' ++ optional config.services.sssd.enable '' - password sufficient ${pkgs.sssd}/lib/security/pam_sss.so + sufficient ${pkgs.sssd}/lib/security/pam_sss.so '' ++ optional config.security.pam.krb5.enable '' - password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass '' ++ optional cfg.enableGnomeKeyring '' - password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok + optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok ''; session = optional cfg.setEnvironment '' - session required pam_env.so conffile=/etc/pam/environment readenv=0 + required pam_env.so conffile=/etc/pam/environment readenv=0 '' ++ singleton '' - session required pam_unix.so + required pam_unix.so '' ++ optional cfg.setLoginUid '' - session ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so + ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so '' ++ optional cfg.ttyAudit.enable '' - session required ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} + required ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} '' ++ optional config.services.homed.enable '' - session required ${config.systemd.package}/lib/security/pam_systemd_home.so + required ${config.systemd.package}/lib/security/pam_systemd_home.so '' ++ optional cfg.makeHomeDir '' - session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} + required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} '' ++ optional cfg.updateWtmp '' - session required ${pkgs.pam}/lib/security/pam_lastlog.so silent + required ${pkgs.pam}/lib/security/pam_lastlog.so silent '' ++ optional config.security.pam.enableEcryptfs '' - session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so '' ++ optional 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 + [success=1 default=ignore] pam_succeed_if.so service = systemd-user '' ++ optional config.security.pam.enableFscrypt '' - session optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so '' ++ optional cfg.zfs '' - session [success=1 default=ignore] pam_succeed_if.so service = systemd-user + [success=1 default=ignore] pam_succeed_if.so service = systemd-user '' ++ optional cfg.zfs '' - session optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} + optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} '' ++ optional cfg.pamMount '' - session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive '' ++ optional use_ldap '' - session optional ${pam_ldap}/lib/security/pam_ldap.so + optional ${pam_ldap}/lib/security/pam_ldap.so '' ++ optional cfg.mysqlAuth '' - session optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf '' ++ optional config.services.kanidm.enablePam '' - session optional ${pkgs.kanidm}/lib/pam_kanidm.so + optional ${pkgs.kanidm}/lib/pam_kanidm.so '' ++ optional config.services.sssd.enable '' - session optional ${pkgs.sssd}/lib/security/pam_sss.so + optional ${pkgs.sssd}/lib/security/pam_sss.so '' ++ optional config.security.pam.krb5.enable '' - session optional ${pam_krb5}/lib/security/pam_krb5.so + optional ${pam_krb5}/lib/security/pam_krb5.so '' ++ optional cfg.otpwAuth '' - session optional ${pkgs.otpw}/lib/security/pam_otpw.so + optional ${pkgs.otpw}/lib/security/pam_otpw.so '' ++ optional cfg.startSession '' - session optional ${config.systemd.package}/lib/security/pam_systemd.so + optional ${config.systemd.package}/lib/security/pam_systemd.so '' ++ optional cfg.forwardXAuth '' - session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 + optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 '' ++ optional (cfg.limits != []) '' - session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} + required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} '' ++ optional (cfg.showMotd && (config.users.motd != null || config.users.motdFile != null)) '' - session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} + optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} '' ++ optional (cfg.enableAppArmor && config.security.apparmor.enable) '' - session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug + optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug '' ++ optional cfg.enableKwallet '' - session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 '' ++ optional cfg.enableGnomeKeyring '' - session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start + optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start '' ++ optional cfg.gnupg.enable '' - session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} + optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} '' ++ optional config.virtualisation.lxc.lxcfs.enable '' - session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all + optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all ''; }; }; From fbd7427b14dc33eba441fad923c828264251eef0 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sat, 16 Sep 2023 01:03:41 -0400 Subject: [PATCH 05/15] nixos/pam: define rules as submodules Allows us to decompose rules into multiple fields that we later format as textual rules. Eventually allows users to override individual fields. --- nixos/modules/security/pam.nix | 388 +++++++++++++++++---------------- 1 file changed, 205 insertions(+), 183 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 87b5afcf0bdc..e0c19508b947 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -8,11 +8,27 @@ with lib; let mkRulesTypeOption = type: mkOption { - # This option is experimental and subject to breaking changes without notice. + # These options are experimental and subject to breaking changes without notice. description = lib.mdDoc '' PAM `${type}` rules for this service. ''; - type = types.listOf types.str; + type = types.listOf (types.submodule { + options = { + enable = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether this rule is added to the PAM service config file. + ''; + }; + text = mkOption { + type = types.str; + description = lib.mdDoc '' + Text of the rule (without `service` or `type` fields). + ''; + }; + }; + }); }; parentConfig = config; @@ -503,8 +519,9 @@ let text = let formatRules = type: pipe cfg.rules.${type} [ + (filter (rule: rule.enable)) (map (rule: concatStringsSep " " - [ type (removeSuffix "\n" rule) ] + [ type (removeSuffix "\n" rule.text) ] )) (concatStringsSep "\n") ]; @@ -526,81 +543,83 @@ let # Samba stuff to the Samba module. This requires that the PAM # module provides the right hooks. rules = { - account = - optional use_ldap '' + account = [ + { enable = use_ldap; text = '' sufficient ${pam_ldap}/lib/security/pam_ldap.so - '' ++ - optional cfg.mysqlAuth '' + ''; } + { enable = cfg.mysqlAuth; text = '' sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' ++ - optional config.services.kanidm.enablePam '' + ''; } + { enable = config.services.kanidm.enablePam; text = '' sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user - '' ++ - optional config.services.sssd.enable '' + ''; } + { enable = config.services.sssd.enable; text = '' ${if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"} ${pkgs.sssd}/lib/security/pam_sss.so - '' ++ - optional config.security.pam.krb5.enable '' + ''; } + { enable = config.security.pam.krb5.enable; text = '' sufficient ${pam_krb5}/lib/security/pam_krb5.so - '' ++ - optional cfg.googleOsLoginAccountVerification '' + ''; } + { enable = cfg.googleOsLoginAccountVerification; text = '' [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so - '' ++ - optional cfg.googleOsLoginAccountVerification '' + ''; } + { enable = cfg.googleOsLoginAccountVerification; text = '' [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so - '' ++ - optional config.services.homed.enable '' + ''; } + { enable = config.services.homed.enable; text = '' sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so - '' ++ + ''; } # 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. - singleton '' + { text = '' required pam_unix.so - ''; + ''; } + ]; - auth = - optional cfg.googleOsLoginAuthentication '' + auth = [ + { enable = cfg.googleOsLoginAuthentication; text = '' [success=done perm_denied=die default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so - '' ++ - optional cfg.rootOK '' + ''; } + { enable = cfg.rootOK; text = '' sufficient pam_rootok.so - '' ++ - optional cfg.requireWheel '' + ''; } + { enable = cfg.requireWheel; text = '' required pam_wheel.so use_uid - '' ++ - optional cfg.logFailures '' + ''; } + { enable = cfg.logFailures; text = '' required pam_faillock.so - '' ++ - optional cfg.mysqlAuth '' + ''; } + { enable = cfg.mysqlAuth; text = '' sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' ++ - optional (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) '' + ''; } + { enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; text = '' sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} - '' ++ - (let p11 = config.security.pam.p11; in optional cfg.p11Auth '' + ''; } + (let p11 = config.security.pam.p11; in { enable = cfg.p11Auth; text = '' ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so - '') ++ - (let u2f = config.security.pam.u2f; in optional cfg.u2fAuth '' + ''; }) + (let u2f = config.security.pam.u2f; in { enable = cfg.u2fAuth; text = '' ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} - '') ++ - optional cfg.usbAuth '' + ''; }) + { enable = cfg.usbAuth; text = '' sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so - '' ++ - (let ussh = config.security.pam.ussh; in optional (config.security.pam.ussh.enable && cfg.usshAuth) '' + ''; } + (let ussh = config.security.pam.ussh; in { enable = config.security.pam.ussh.enable && cfg.usshAuth; text = '' ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} - '') ++ - (let oath = config.security.pam.oath; in optional cfg.oathAuth '' + ''; }) + (let oath = config.security.pam.oath; in { enable = cfg.oathAuth; text = '' requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} - '') ++ - (let yubi = config.security.pam.yubico; in optional cfg.yubicoAuth '' + ''; }) + (let yubi = config.security.pam.yubico; in { enable = cfg.yubicoAuth; text = '' ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} - '') ++ - (let dp9ik = config.security.pam.dp9ik; in optional dp9ik.enable '' + ''; }) + (let dp9ik = config.security.pam.dp9ik; in { enable = dp9ik.enable; text = '' ${dp9ik.control} ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} - '') ++ - optional cfg.fprintAuth '' + ''; }) + { enable = cfg.fprintAuth; text = '' sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so - '' ++ + ''; } + ] ++ # 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 # after it succeeds. Certain modules need to run after pam_unix @@ -620,202 +639,205 @@ let || cfg.failDelay.enable || cfg.duoSecurity.enable || cfg.zfs)) - ( - optional config.services.homed.enable '' + [ + { enable = config.services.homed.enable; text = '' optional ${config.systemd.package}/lib/security/pam_systemd_home.so - '' ++ - optional cfg.unixAuth '' + ''; } + { enable = cfg.unixAuth; text = '' optional pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth - '' ++ - optional config.security.pam.enableEcryptfs '' + ''; } + { enable = config.security.pam.enableEcryptfs; text = '' optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap - '' ++ - optional config.security.pam.enableFscrypt '' + ''; } + { enable = config.security.pam.enableFscrypt; text = '' optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so - '' ++ - optional cfg.zfs '' + ''; } + { enable = cfg.zfs; text = '' optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} - '' ++ - optional cfg.pamMount '' + ''; } + { enable = cfg.pamMount; text = '' optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive - '' ++ - optional cfg.enableKwallet '' + ''; } + { enable = cfg.enableKwallet; text = '' optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 - '' ++ - optional cfg.enableGnomeKeyring '' + ''; } + { enable = cfg.enableGnomeKeyring; text = '' optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so - '' ++ - optional cfg.gnupg.enable '' + ''; } + { enable = cfg.gnupg.enable; text = '' optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} - '' ++ - optional cfg.failDelay.enable '' + ''; } + { enable = cfg.failDelay.enable; text = '' optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} - '' ++ - optional cfg.googleAuthenticator.enable '' + ''; } + { enable = cfg.googleAuthenticator.enable; text = '' required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp - '' ++ - optional cfg.duoSecurity.enable '' + ''; } + { enable = cfg.duoSecurity.enable; text = '' required ${pkgs.duo-unix}/lib/security/pam_duo.so - '' - )) ++ - optional config.services.homed.enable '' + ''; } + ]) ++ [ + { enable = config.services.homed.enable; text = '' sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so - '' ++ - optional cfg.unixAuth '' + ''; } + { enable = cfg.unixAuth; text = '' sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass - '' ++ - optional cfg.otpwAuth '' + ''; } + { enable = cfg.otpwAuth; text = '' sufficient ${pkgs.otpw}/lib/security/pam_otpw.so - '' ++ - optional use_ldap '' + ''; } + { enable = use_ldap; text = '' sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass - '' ++ - optional config.services.kanidm.enablePam '' + ''; } + { enable = config.services.kanidm.enablePam; text = '' sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass - '' ++ - optional config.services.sssd.enable '' + ''; } + { enable = config.services.sssd.enable; text = '' sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass - '' ++ - optional config.security.pam.krb5.enable '' + ''; } + { enable = config.security.pam.krb5.enable; text = '' [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass - '' ++ - optional config.security.pam.krb5.enable '' + ''; } + { enable = config.security.pam.krb5.enable; text = '' [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass - '' ++ - optional config.security.pam.krb5.enable '' + ''; } + { enable = config.security.pam.krb5.enable; text = '' sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass - '' ++ - singleton '' + ''; } + { text = '' required pam_deny.so - ''; + ''; } + ]; - password = - optional config.services.homed.enable '' + password = [ + { enable = config.services.homed.enable; text = '' sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so - '' ++ - singleton '' + ''; } + { text = '' sufficient pam_unix.so nullok yescrypt - '' ++ - optional config.security.pam.enableEcryptfs '' + ''; } + { enable = config.security.pam.enableEcryptfs; text = '' optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so - '' ++ - optional config.security.pam.enableFscrypt '' + ''; } + { enable = config.security.pam.enableFscrypt; text = '' optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so - '' ++ - optional cfg.zfs '' + ''; } + { enable = cfg.zfs; text = '' optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} - '' ++ - optional cfg.pamMount '' + ''; } + { enable = cfg.pamMount; text = '' optional ${pkgs.pam_mount}/lib/security/pam_mount.so - '' ++ - optional use_ldap '' + ''; } + { enable = use_ldap; text = '' sufficient ${pam_ldap}/lib/security/pam_ldap.so - '' ++ - optional cfg.mysqlAuth '' + ''; } + { enable = cfg.mysqlAuth; text = '' sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' ++ - optional config.services.kanidm.enablePam '' + ''; } + { enable = config.services.kanidm.enablePam; text = '' sufficient ${pkgs.kanidm}/lib/pam_kanidm.so - '' ++ - optional config.services.sssd.enable '' + ''; } + { enable = config.services.sssd.enable; text = '' sufficient ${pkgs.sssd}/lib/security/pam_sss.so - '' ++ - optional config.security.pam.krb5.enable '' + ''; } + { enable = config.security.pam.krb5.enable; text = '' sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass - '' ++ - optional cfg.enableGnomeKeyring '' + ''; } + { enable = cfg.enableGnomeKeyring; text = '' optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok - ''; + ''; } + ]; - session = - optional cfg.setEnvironment '' + session = [ + { enable = cfg.setEnvironment; text = '' required pam_env.so conffile=/etc/pam/environment readenv=0 - '' ++ - singleton '' + ''; } + { text = '' required pam_unix.so - '' ++ - optional cfg.setLoginUid '' + ''; } + { enable = cfg.setLoginUid; text = '' ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so - '' ++ - optional cfg.ttyAudit.enable '' + ''; } + { enable = cfg.ttyAudit.enable; text = '' required ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} - '' ++ - optional config.services.homed.enable '' + ''; } + { enable = config.services.homed.enable; text = '' required ${config.systemd.package}/lib/security/pam_systemd_home.so - '' ++ - optional cfg.makeHomeDir '' + ''; } + { enable = cfg.makeHomeDir; text = '' required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} - '' ++ - optional cfg.updateWtmp '' + ''; } + { enable = cfg.updateWtmp; text = '' required ${pkgs.pam}/lib/security/pam_lastlog.so silent - '' ++ - optional config.security.pam.enableEcryptfs '' + ''; } + { enable = config.security.pam.enableEcryptfs; text = '' optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so - '' ++ - optional 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 + ''; } + # 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 + { enable = config.security.pam.enableFscrypt; text = '' [success=1 default=ignore] pam_succeed_if.so service = systemd-user - '' ++ - optional config.security.pam.enableFscrypt '' + ''; } + { enable = config.security.pam.enableFscrypt; text = '' optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so - '' ++ - optional cfg.zfs '' + ''; } + { enable = cfg.zfs; text = '' [success=1 default=ignore] pam_succeed_if.so service = systemd-user - '' ++ - optional cfg.zfs '' + ''; } + { enable = cfg.zfs; text = '' optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} - '' ++ - optional cfg.pamMount '' + ''; } + { enable = cfg.pamMount; text = '' optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive - '' ++ - optional use_ldap '' + ''; } + { enable = use_ldap; text = '' optional ${pam_ldap}/lib/security/pam_ldap.so - '' ++ - optional cfg.mysqlAuth '' + ''; } + { enable = cfg.mysqlAuth; text = '' optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf - '' ++ - optional config.services.kanidm.enablePam '' + ''; } + { enable = config.services.kanidm.enablePam; text = '' optional ${pkgs.kanidm}/lib/pam_kanidm.so - '' ++ - optional config.services.sssd.enable '' + ''; } + { enable = config.services.sssd.enable; text = '' optional ${pkgs.sssd}/lib/security/pam_sss.so - '' ++ - optional config.security.pam.krb5.enable '' + ''; } + { enable = config.security.pam.krb5.enable; text = '' optional ${pam_krb5}/lib/security/pam_krb5.so - '' ++ - optional cfg.otpwAuth '' + ''; } + { enable = cfg.otpwAuth; text = '' optional ${pkgs.otpw}/lib/security/pam_otpw.so - '' ++ - optional cfg.startSession '' + ''; } + { enable = cfg.startSession; text = '' optional ${config.systemd.package}/lib/security/pam_systemd.so - '' ++ - optional cfg.forwardXAuth '' + ''; } + { enable = cfg.forwardXAuth; text = '' optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 - '' ++ - optional (cfg.limits != []) '' + ''; } + { enable = cfg.limits != []; text = '' required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} - '' ++ - optional (cfg.showMotd && (config.users.motd != null || config.users.motdFile != null)) '' + ''; } + { enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); text = '' optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} - '' ++ - optional (cfg.enableAppArmor && config.security.apparmor.enable) '' + ''; } + { enable = cfg.enableAppArmor && config.security.apparmor.enable; text = '' optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug - '' ++ - optional cfg.enableKwallet '' + ''; } + { enable = cfg.enableKwallet; text = '' optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 - '' ++ - optional cfg.enableGnomeKeyring '' + ''; } + { enable = cfg.enableGnomeKeyring; text = '' optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start - '' ++ - optional cfg.gnupg.enable '' + ''; } + { enable = cfg.gnupg.enable; text = '' optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} - '' ++ - optional config.virtualisation.lxc.lxcfs.enable '' + ''; } + { enable = config.virtualisation.lxc.lxcfs.enable; text = '' optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all - ''; + ''; } + ]; }; }; From 0563e0a379448e9195d5f9f470d3f693b3180ae6 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sat, 16 Sep 2023 13:00:46 -0400 Subject: [PATCH 06/15] nixos/pam: give each rule a name These names are internal identifiers. They will be used as keys so that users can reconfigure rules by merging a rule config with the same name. The name is arbitrary. The built-in rules are named after the PAM where practical. --- nixos/modules/security/pam.nix | 176 +++++++++++++++++---------------- 1 file changed, 91 insertions(+), 85 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index e0c19508b947..569a1deb6b61 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -14,6 +14,12 @@ let ''; type = types.listOf (types.submodule { options = { + name = mkOption { + type = types.str; + description = lib.mdDoc '' + Name of this rule. + ''; + }; enable = mkOption { type = types.bool; default = true; @@ -544,79 +550,79 @@ let # module provides the right hooks. rules = { account = [ - { enable = use_ldap; text = '' + { name = "ldap"; enable = use_ldap; text = '' sufficient ${pam_ldap}/lib/security/pam_ldap.so ''; } - { enable = cfg.mysqlAuth; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; text = '' sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { enable = config.services.kanidm.enablePam; text = '' + { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user ''; } - { enable = config.services.sssd.enable; text = '' + { name = "sss"; enable = config.services.sssd.enable; text = '' ${if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"} ${pkgs.sssd}/lib/security/pam_sss.so ''; } - { enable = config.security.pam.krb5.enable; text = '' + { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' sufficient ${pam_krb5}/lib/security/pam_krb5.so ''; } - { enable = cfg.googleOsLoginAccountVerification; text = '' + { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; text = '' [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so ''; } - { enable = cfg.googleOsLoginAccountVerification; text = '' + { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; text = '' [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so ''; } - { enable = config.services.homed.enable; text = '' + { name = "systemd_home"; enable = config.services.homed.enable; text = '' sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } # 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. - { text = '' + { name = "unix"; text = '' required pam_unix.so ''; } ]; auth = [ - { enable = cfg.googleOsLoginAuthentication; text = '' + { name = "oslogin_login"; enable = cfg.googleOsLoginAuthentication; text = '' [success=done perm_denied=die default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so ''; } - { enable = cfg.rootOK; text = '' + { name = "rootok"; enable = cfg.rootOK; text = '' sufficient pam_rootok.so ''; } - { enable = cfg.requireWheel; text = '' + { name = "wheel"; enable = cfg.requireWheel; text = '' required pam_wheel.so use_uid ''; } - { enable = cfg.logFailures; text = '' + { name = "faillock"; enable = cfg.logFailures; text = '' required pam_faillock.so ''; } - { enable = cfg.mysqlAuth; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; text = '' sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; text = '' + { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; text = '' sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} ''; } - (let p11 = config.security.pam.p11; in { enable = cfg.p11Auth; text = '' + (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; text = '' ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so ''; }) - (let u2f = config.security.pam.u2f; in { enable = cfg.u2fAuth; text = '' + (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; text = '' ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} ''; }) - { enable = cfg.usbAuth; text = '' + { name = "usb"; enable = cfg.usbAuth; text = '' sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so ''; } - (let ussh = config.security.pam.ussh; in { enable = config.security.pam.ussh.enable && cfg.usshAuth; text = '' + (let ussh = config.security.pam.ussh; in { name = "ussh"; enable = config.security.pam.ussh.enable && cfg.usshAuth; text = '' ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} ''; }) - (let oath = config.security.pam.oath; in { enable = cfg.oathAuth; text = '' + (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; text = '' requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} ''; }) - (let yubi = config.security.pam.yubico; in { enable = cfg.yubicoAuth; text = '' + (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; text = '' ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} ''; }) - (let dp9ik = config.security.pam.dp9ik; in { enable = dp9ik.enable; text = '' + (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; text = '' ${dp9ik.control} ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} ''; }) - { enable = cfg.fprintAuth; text = '' + { name = "fprintd"; enable = cfg.fprintAuth; text = '' sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so ''; } ] ++ @@ -640,201 +646,201 @@ let || cfg.duoSecurity.enable || cfg.zfs)) [ - { enable = config.services.homed.enable; text = '' + { name = "systemd_home-early"; enable = config.services.homed.enable; text = '' optional ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { enable = cfg.unixAuth; text = '' + { name = "unix-early"; enable = cfg.unixAuth; text = '' optional pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth ''; } - { enable = config.security.pam.enableEcryptfs; text = '' + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; text = '' optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap ''; } - { enable = config.security.pam.enableFscrypt; text = '' + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; text = '' optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so ''; } - { enable = cfg.zfs; text = '' + { name = "zfs_key"; enable = cfg.zfs; text = '' optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ''; } - { enable = cfg.pamMount; text = '' + { name = "mount"; enable = cfg.pamMount; text = '' optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive ''; } - { enable = cfg.enableKwallet; text = '' + { name = "kwallet5"; enable = cfg.enableKwallet; text = '' optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 ''; } - { enable = cfg.enableGnomeKeyring; text = '' + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; text = '' optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so ''; } - { enable = cfg.gnupg.enable; text = '' + { name = "gnupg"; enable = cfg.gnupg.enable; text = '' optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} ''; } - { enable = cfg.failDelay.enable; text = '' + { name = "faildelay"; enable = cfg.failDelay.enable; text = '' optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} ''; } - { enable = cfg.googleAuthenticator.enable; text = '' + { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; text = '' required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp ''; } - { enable = cfg.duoSecurity.enable; text = '' + { name = "duo"; enable = cfg.duoSecurity.enable; text = '' required ${pkgs.duo-unix}/lib/security/pam_duo.so ''; } ]) ++ [ - { enable = config.services.homed.enable; text = '' + { name = "systemd_home"; enable = config.services.homed.enable; text = '' sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { enable = cfg.unixAuth; text = '' + { name = "unix"; enable = cfg.unixAuth; text = '' sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass ''; } - { enable = cfg.otpwAuth; text = '' + { name = "otpw"; enable = cfg.otpwAuth; text = '' sufficient ${pkgs.otpw}/lib/security/pam_otpw.so ''; } - { enable = use_ldap; text = '' + { name = "ldap"; enable = use_ldap; text = '' sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass ''; } - { enable = config.services.kanidm.enablePam; text = '' + { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass ''; } - { enable = config.services.sssd.enable; text = '' + { name = "sss"; enable = config.services.sssd.enable; text = '' sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass ''; } - { enable = config.security.pam.krb5.enable; text = '' + { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass ''; } - { enable = config.security.pam.krb5.enable; text = '' + { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; text = '' [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass ''; } - { enable = config.security.pam.krb5.enable; text = '' + { name = "ccreds-store"; enable = config.security.pam.krb5.enable; text = '' sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass ''; } - { text = '' + { name = "deny"; text = '' required pam_deny.so ''; } ]; password = [ - { enable = config.services.homed.enable; text = '' + { name = "systemd_home"; enable = config.services.homed.enable; text = '' sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { text = '' + { name = "unix"; text = '' sufficient pam_unix.so nullok yescrypt ''; } - { enable = config.security.pam.enableEcryptfs; text = '' + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; text = '' optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so ''; } - { enable = config.security.pam.enableFscrypt; text = '' + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; text = '' optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so ''; } - { enable = cfg.zfs; text = '' + { name = "zfs_key"; enable = cfg.zfs; text = '' optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ''; } - { enable = cfg.pamMount; text = '' + { name = "mount"; enable = cfg.pamMount; text = '' optional ${pkgs.pam_mount}/lib/security/pam_mount.so ''; } - { enable = use_ldap; text = '' + { name = "ldap"; enable = use_ldap; text = '' sufficient ${pam_ldap}/lib/security/pam_ldap.so ''; } - { enable = cfg.mysqlAuth; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; text = '' sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { enable = config.services.kanidm.enablePam; text = '' + { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ''; } - { enable = config.services.sssd.enable; text = '' + { name = "sss"; enable = config.services.sssd.enable; text = '' sufficient ${pkgs.sssd}/lib/security/pam_sss.so ''; } - { enable = config.security.pam.krb5.enable; text = '' + { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass ''; } - { enable = cfg.enableGnomeKeyring; text = '' + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; text = '' optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok ''; } ]; session = [ - { enable = cfg.setEnvironment; text = '' + { name = "env"; enable = cfg.setEnvironment; text = '' required pam_env.so conffile=/etc/pam/environment readenv=0 ''; } - { text = '' + { name = "unix"; text = '' required pam_unix.so ''; } - { enable = cfg.setLoginUid; text = '' + { name = "loginuid"; enable = cfg.setLoginUid; text = '' ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so ''; } - { enable = cfg.ttyAudit.enable; text = '' + { name = "tty_audit"; enable = cfg.ttyAudit.enable; text = '' required ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} ''; } - { enable = config.services.homed.enable; text = '' + { name = "systemd_home"; enable = config.services.homed.enable; text = '' required ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { enable = cfg.makeHomeDir; text = '' + { name = "mkhomedir"; enable = cfg.makeHomeDir; text = '' required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} ''; } - { enable = cfg.updateWtmp; text = '' + { name = "lastlog"; enable = cfg.updateWtmp; text = '' required ${pkgs.pam}/lib/security/pam_lastlog.so silent ''; } - { enable = config.security.pam.enableEcryptfs; text = '' + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; text = '' optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so ''; } # 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 - { enable = config.security.pam.enableFscrypt; text = '' + { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; text = '' [success=1 default=ignore] pam_succeed_if.so service = systemd-user ''; } - { enable = config.security.pam.enableFscrypt; text = '' + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; text = '' optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so ''; } - { enable = cfg.zfs; text = '' + { name = "zfs_key-skip-systemd"; enable = cfg.zfs; text = '' [success=1 default=ignore] pam_succeed_if.so service = systemd-user ''; } - { enable = cfg.zfs; text = '' + { name = "zfs_key"; enable = cfg.zfs; text = '' optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} ''; } - { enable = cfg.pamMount; text = '' + { name = "mount"; enable = cfg.pamMount; text = '' optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive ''; } - { enable = use_ldap; text = '' + { name = "ldap"; enable = use_ldap; text = '' optional ${pam_ldap}/lib/security/pam_ldap.so ''; } - { enable = cfg.mysqlAuth; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; text = '' optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { enable = config.services.kanidm.enablePam; text = '' + { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' optional ${pkgs.kanidm}/lib/pam_kanidm.so ''; } - { enable = config.services.sssd.enable; text = '' + { name = "sss"; enable = config.services.sssd.enable; text = '' optional ${pkgs.sssd}/lib/security/pam_sss.so ''; } - { enable = config.security.pam.krb5.enable; text = '' + { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' optional ${pam_krb5}/lib/security/pam_krb5.so ''; } - { enable = cfg.otpwAuth; text = '' + { name = "otpw"; enable = cfg.otpwAuth; text = '' optional ${pkgs.otpw}/lib/security/pam_otpw.so ''; } - { enable = cfg.startSession; text = '' + { name = "systemd"; enable = cfg.startSession; text = '' optional ${config.systemd.package}/lib/security/pam_systemd.so ''; } - { enable = cfg.forwardXAuth; text = '' + { name = "xauth"; enable = cfg.forwardXAuth; text = '' optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 ''; } - { enable = cfg.limits != []; text = '' + { name = "limits"; enable = cfg.limits != []; text = '' required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} ''; } - { enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); text = '' + { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); text = '' optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} ''; } - { enable = cfg.enableAppArmor && config.security.apparmor.enable; text = '' + { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; text = '' optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug ''; } - { enable = cfg.enableKwallet; text = '' + { name = "kwallet5"; enable = cfg.enableKwallet; text = '' optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 ''; } - { enable = cfg.enableGnomeKeyring; text = '' + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; text = '' optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start ''; } - { enable = cfg.gnupg.enable; text = '' + { name = "gnupg"; enable = cfg.gnupg.enable; text = '' optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} ''; } - { enable = config.virtualisation.lxc.lxcfs.enable; text = '' + { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; text = '' optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all ''; } ]; From 25bc21f19a3f87d7512d7bd450ee7674370f14fb Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sat, 16 Sep 2023 13:32:55 -0400 Subject: [PATCH 07/15] nixos/pam: extract control field --- nixos/modules/security/pam.nix | 350 +++++++++++++++++---------------- 1 file changed, 178 insertions(+), 172 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 569a1deb6b61..12a818a9680b 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -27,10 +27,16 @@ let Whether this rule is added to the PAM service config file. ''; }; + control = mkOption { + type = types.str; + description = lib.mdDoc '' + Indicates the behavior of the PAM-API should the module fail to succeed in its authentication task. See `control` in {manpage}`pam.conf(5)` for details. + ''; + }; text = mkOption { type = types.str; description = lib.mdDoc '' - Text of the rule (without `service` or `type` fields). + Text of the rule (without `service`, `type` or `control` fields). ''; }; }; @@ -527,7 +533,7 @@ let formatRules = type: pipe cfg.rules.${type} [ (filter (rule: rule.enable)) (map (rule: concatStringsSep " " - [ type (removeSuffix "\n" rule.text) ] + [ type rule.control (removeSuffix "\n" rule.text) ] )) (concatStringsSep "\n") ]; @@ -550,80 +556,80 @@ let # module provides the right hooks. rules = { account = [ - { name = "ldap"; enable = use_ldap; text = '' - sufficient ${pam_ldap}/lib/security/pam_ldap.so + { name = "ldap"; enable = use_ldap; control = "sufficient"; text = '' + ${pam_ldap}/lib/security/pam_ldap.so ''; } - { name = "mysql"; enable = cfg.mysqlAuth; text = '' - sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; text = '' + ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' - sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; text = '' + ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user ''; } - { name = "sss"; enable = config.services.sssd.enable; text = '' - ${if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"} ${pkgs.sssd}/lib/security/pam_sss.so + { name = "sss"; enable = config.services.sssd.enable; control = if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"; text = '' + ${pkgs.sssd}/lib/security/pam_sss.so ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' - sufficient ${pam_krb5}/lib/security/pam_krb5.so + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; text = '' + ${pam_krb5}/lib/security/pam_krb5.so ''; } - { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; text = '' - [success=ok ignore=ignore default=die] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; text = '' + ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so ''; } - { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; text = '' - [success=ok default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so + { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; text = '' + ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so ''; } - { name = "systemd_home"; enable = config.services.homed.enable; text = '' - sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; text = '' + ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } # 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"; text = '' - required pam_unix.so + { name = "unix"; control = "required"; text = '' + pam_unix.so ''; } ]; auth = [ - { name = "oslogin_login"; enable = cfg.googleOsLoginAuthentication; text = '' - [success=done perm_denied=die default=ignore] ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + { name = "oslogin_login"; enable = cfg.googleOsLoginAuthentication; control = "[success=done perm_denied=die default=ignore]"; text = '' + ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so ''; } - { name = "rootok"; enable = cfg.rootOK; text = '' - sufficient pam_rootok.so + { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; text = '' + pam_rootok.so ''; } - { name = "wheel"; enable = cfg.requireWheel; text = '' - required pam_wheel.so use_uid + { name = "wheel"; enable = cfg.requireWheel; control = "required"; text = '' + pam_wheel.so use_uid ''; } - { name = "faillock"; enable = cfg.logFailures; text = '' - required pam_faillock.so + { name = "faillock"; enable = cfg.logFailures; control = "required"; text = '' + pam_faillock.so ''; } - { name = "mysql"; enable = cfg.mysqlAuth; text = '' - sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; text = '' + ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; text = '' - sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} + { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; text = '' + ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} ''; } - (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; text = '' - ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so + (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; text = '' + ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so ''; }) - (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; text = '' - ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} + (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; text = '' + ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} ''; }) - { name = "usb"; enable = cfg.usbAuth; text = '' - sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so + { name = "usb"; enable = cfg.usbAuth; control = "sufficient"; text = '' + ${pkgs.pam_usb}/lib/security/pam_usb.so ''; } - (let ussh = config.security.pam.ussh; in { name = "ussh"; enable = config.security.pam.ussh.enable && cfg.usshAuth; text = '' - ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} + (let ussh = config.security.pam.ussh; in { name = "ussh"; enable = config.security.pam.ussh.enable && cfg.usshAuth; control = ussh.control; text = '' + ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} ''; }) - (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; text = '' - requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} + (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; text = '' + ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} ''; }) - (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; text = '' - ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} + (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; text = '' + ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} ''; }) - (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; text = '' - ${dp9ik.control} ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} + (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; text = '' + ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} ''; }) - { name = "fprintd"; enable = cfg.fprintAuth; text = '' - sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so + { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; text = '' + ${pkgs.fprintd}/lib/security/pam_fprintd.so ''; } ] ++ # Modules in this block require having the password set in PAM_AUTHTOK. @@ -646,202 +652,202 @@ let || cfg.duoSecurity.enable || cfg.zfs)) [ - { name = "systemd_home-early"; enable = config.services.homed.enable; text = '' - optional ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; text = '' + ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { name = "unix-early"; enable = cfg.unixAuth; text = '' - optional pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth + { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; text = '' + pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; text = '' - optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; text = '' + ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; text = '' - optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; text = '' + ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so ''; } - { name = "zfs_key"; enable = cfg.zfs; text = '' - optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; text = '' + ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ''; } - { name = "mount"; enable = cfg.pamMount; text = '' - optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + { name = "mount"; enable = cfg.pamMount; control = "optional"; text = '' + ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; text = '' - optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; text = '' + ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; text = '' - optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; text = '' + ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; text = '' - optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; text = '' + ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} ''; } - { name = "faildelay"; enable = cfg.failDelay.enable; text = '' - optional ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} + { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; text = '' + ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} ''; } - { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; text = '' - required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp + { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; text = '' + ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp ''; } - { name = "duo"; enable = cfg.duoSecurity.enable; text = '' - required ${pkgs.duo-unix}/lib/security/pam_duo.so + { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; text = '' + ${pkgs.duo-unix}/lib/security/pam_duo.so ''; } ]) ++ [ - { name = "systemd_home"; enable = config.services.homed.enable; text = '' - sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; text = '' + ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { name = "unix"; enable = cfg.unixAuth; text = '' - sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass + { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; text = '' + pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass ''; } - { name = "otpw"; enable = cfg.otpwAuth; text = '' - sufficient ${pkgs.otpw}/lib/security/pam_otpw.so + { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; text = '' + ${pkgs.otpw}/lib/security/pam_otpw.so ''; } - { name = "ldap"; enable = use_ldap; text = '' - sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass + { name = "ldap"; enable = use_ldap; control = "sufficient"; text = '' + ${pam_ldap}/lib/security/pam_ldap.so use_first_pass ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' - sufficient ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; text = '' + ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass ''; } - { name = "sss"; enable = config.services.sssd.enable; text = '' - sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass + { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; text = '' + ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' - [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; text = '' + ${pam_krb5}/lib/security/pam_krb5.so use_first_pass ''; } - { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; text = '' - [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass + { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; text = '' + ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass ''; } - { name = "ccreds-store"; enable = config.security.pam.krb5.enable; text = '' - sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass + { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; text = '' + ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass ''; } - { name = "deny"; text = '' - required pam_deny.so + { name = "deny"; control = "required"; text = '' + pam_deny.so ''; } ]; password = [ - { name = "systemd_home"; enable = config.services.homed.enable; text = '' - sufficient ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; text = '' + ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { name = "unix"; text = '' - sufficient pam_unix.so nullok yescrypt + { name = "unix"; control = "sufficient"; text = '' + pam_unix.so nullok yescrypt ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; text = '' - optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; text = '' + ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; text = '' - optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; text = '' + ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so ''; } - { name = "zfs_key"; enable = cfg.zfs; text = '' - optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; text = '' + ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ''; } - { name = "mount"; enable = cfg.pamMount; text = '' - optional ${pkgs.pam_mount}/lib/security/pam_mount.so + { name = "mount"; enable = cfg.pamMount; control = "optional"; text = '' + ${pkgs.pam_mount}/lib/security/pam_mount.so ''; } - { name = "ldap"; enable = use_ldap; text = '' - sufficient ${pam_ldap}/lib/security/pam_ldap.so + { name = "ldap"; enable = use_ldap; control = "sufficient"; text = '' + ${pam_ldap}/lib/security/pam_ldap.so ''; } - { name = "mysql"; enable = cfg.mysqlAuth; text = '' - sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; text = '' + ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' - sufficient ${pkgs.kanidm}/lib/pam_kanidm.so + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; text = '' + ${pkgs.kanidm}/lib/pam_kanidm.so ''; } - { name = "sss"; enable = config.services.sssd.enable; text = '' - sufficient ${pkgs.sssd}/lib/security/pam_sss.so + { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; text = '' + ${pkgs.sssd}/lib/security/pam_sss.so ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' - sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; text = '' + ${pam_krb5}/lib/security/pam_krb5.so use_first_pass ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; text = '' - optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; text = '' + ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok ''; } ]; session = [ - { name = "env"; enable = cfg.setEnvironment; text = '' - required pam_env.so conffile=/etc/pam/environment readenv=0 + { name = "env"; enable = cfg.setEnvironment; control = "required"; text = '' + pam_env.so conffile=/etc/pam/environment readenv=0 ''; } - { name = "unix"; text = '' - required pam_unix.so + { name = "unix"; control = "required"; text = '' + pam_unix.so ''; } - { name = "loginuid"; enable = cfg.setLoginUid; text = '' - ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so + { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; text = '' + pam_loginuid.so ''; } - { name = "tty_audit"; enable = cfg.ttyAudit.enable; text = '' - required ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} + { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; text = '' + ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} ''; } - { name = "systemd_home"; enable = config.services.homed.enable; text = '' - required ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; text = '' + ${config.systemd.package}/lib/security/pam_systemd_home.so ''; } - { name = "mkhomedir"; enable = cfg.makeHomeDir; text = '' - required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} + { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; text = '' + ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} ''; } - { name = "lastlog"; enable = cfg.updateWtmp; text = '' - required ${pkgs.pam}/lib/security/pam_lastlog.so silent + { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; text = '' + ${pkgs.pam}/lib/security/pam_lastlog.so silent ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; text = '' - optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; text = '' + ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so ''; } # 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 - { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; text = '' - [success=1 default=ignore] pam_succeed_if.so service = systemd-user + { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; text = '' + pam_succeed_if.so service = systemd-user ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; text = '' - optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; text = '' + ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so ''; } - { name = "zfs_key-skip-systemd"; enable = cfg.zfs; text = '' - [success=1 default=ignore] pam_succeed_if.so service = systemd-user + { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; text = '' + pam_succeed_if.so service = systemd-user ''; } - { name = "zfs_key"; enable = cfg.zfs; text = '' - optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; text = '' + ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} ''; } - { name = "mount"; enable = cfg.pamMount; text = '' - optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + { name = "mount"; enable = cfg.pamMount; control = "optional"; text = '' + ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive ''; } - { name = "ldap"; enable = use_ldap; text = '' - optional ${pam_ldap}/lib/security/pam_ldap.so + { name = "ldap"; enable = use_ldap; control = "optional"; text = '' + ${pam_ldap}/lib/security/pam_ldap.so ''; } - { name = "mysql"; enable = cfg.mysqlAuth; text = '' - optional ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; text = '' + ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; text = '' - optional ${pkgs.kanidm}/lib/pam_kanidm.so + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; text = '' + ${pkgs.kanidm}/lib/pam_kanidm.so ''; } - { name = "sss"; enable = config.services.sssd.enable; text = '' - optional ${pkgs.sssd}/lib/security/pam_sss.so + { name = "sss"; enable = config.services.sssd.enable; control = "optional"; text = '' + ${pkgs.sssd}/lib/security/pam_sss.so ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; text = '' - optional ${pam_krb5}/lib/security/pam_krb5.so + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; text = '' + ${pam_krb5}/lib/security/pam_krb5.so ''; } - { name = "otpw"; enable = cfg.otpwAuth; text = '' - optional ${pkgs.otpw}/lib/security/pam_otpw.so + { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; text = '' + ${pkgs.otpw}/lib/security/pam_otpw.so ''; } - { name = "systemd"; enable = cfg.startSession; text = '' - optional ${config.systemd.package}/lib/security/pam_systemd.so + { name = "systemd"; enable = cfg.startSession; control = "optional"; text = '' + ${config.systemd.package}/lib/security/pam_systemd.so ''; } - { name = "xauth"; enable = cfg.forwardXAuth; text = '' - optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 + { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; text = '' + pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 ''; } - { name = "limits"; enable = cfg.limits != []; text = '' - required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} + { name = "limits"; enable = cfg.limits != []; control = "required"; text = '' + ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} ''; } - { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); text = '' - optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} + { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; text = '' + ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} ''; } - { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; text = '' - optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug + { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; text = '' + ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; text = '' - optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; text = '' + ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; text = '' - optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; text = '' + ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; text = '' - optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; text = '' + ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} ''; } - { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; text = '' - optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all + { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; control = "optional"; text = '' + ${pkgs.lxc}/lib/security/pam_cgfs.so -c all ''; } ]; }; From 12a488e89c32f778b463620b56f92bfc010b0514 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sat, 16 Sep 2023 13:43:36 -0400 Subject: [PATCH 08/15] nixos/pam: extract modulePath field --- nixos/modules/security/pam.nix | 318 +++++++++++++++------------------ 1 file changed, 144 insertions(+), 174 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 12a818a9680b..012b83c6e1b2 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -33,10 +33,16 @@ let Indicates the behavior of the PAM-API should the module fail to succeed in its authentication task. See `control` in {manpage}`pam.conf(5)` for details. ''; }; + modulePath = mkOption { + type = types.str; + description = lib.mdDoc '' + Either the full filename of the PAM to be used by the application (it begins with a '/'), or a relative pathname from the default module location. See `module-path` in {manpage}`pam.conf(5)` for details. + ''; + }; text = mkOption { type = types.str; description = lib.mdDoc '' - Text of the rule (without `service`, `type` or `control` fields). + Text of the rule (without `service`, `type`, `control` or `module-path` fields). ''; }; }; @@ -532,9 +538,10 @@ let text = let formatRules = type: pipe cfg.rules.${type} [ (filter (rule: rule.enable)) - (map (rule: concatStringsSep " " - [ type rule.control (removeSuffix "\n" rule.text) ] - )) + (map (rule: concatStringsSep " " ( + [ type rule.control rule.modulePath ] + ++ optional (rule.text != "") (removeSuffix "\n" rule.text) + ))) (concatStringsSep "\n") ]; in mkDefault '' @@ -556,80 +563,68 @@ let # module provides the right hooks. rules = { account = [ - { name = "ldap"; enable = use_ldap; control = "sufficient"; text = '' - ${pam_ldap}/lib/security/pam_ldap.so + { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; text = '' - ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' + config_file=/etc/security/pam_mysql.conf ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; text = '' - ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' + ignore_unknown_user ''; } - { name = "sss"; enable = config.services.sssd.enable; control = if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"; text = '' - ${pkgs.sssd}/lib/security/pam_sss.so + { 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"; text = '' ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; text = '' - ${pam_krb5}/lib/security/pam_krb5.so + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' ''; } - { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; text = '' - ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; text = '' ''; } - { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; text = '' - ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so + { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so"; text = '' ''; } - { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; text = '' - ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } # 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"; text = '' - pam_unix.so + { name = "unix"; control = "required"; modulePath = "pam_unix.so"; text = '' ''; } ]; auth = [ - { name = "oslogin_login"; enable = cfg.googleOsLoginAuthentication; control = "[success=done perm_denied=die default=ignore]"; text = '' - ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so + { 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"; text = '' ''; } - { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; text = '' - pam_rootok.so + { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "pam_rootok.so"; text = '' ''; } - { name = "wheel"; enable = cfg.requireWheel; control = "required"; text = '' - pam_wheel.so use_uid + { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; text = '' + use_uid ''; } - { name = "faillock"; enable = cfg.logFailures; control = "required"; text = '' - pam_faillock.so + { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "pam_faillock.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; text = '' - ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' + config_file=/etc/security/pam_mysql.conf ''; } - { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; text = '' - ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} + { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; text = '' + file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} ''; } - (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; text = '' - ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so + (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; modulePath = "${pkgs.pam_p11}/lib/security/pam_p11.so"; text = '' + ${pkgs.opensc}/lib/opensc-pkcs11.so ''; }) - (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; text = '' - ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} + (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; text = '' + ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} ''; }) - { name = "usb"; enable = cfg.usbAuth; control = "sufficient"; text = '' - ${pkgs.pam_usb}/lib/security/pam_usb.so + { name = "usb"; enable = cfg.usbAuth; control = "sufficient"; modulePath = "${pkgs.pam_usb}/lib/security/pam_usb.so"; text = '' ''; } - (let ussh = config.security.pam.ussh; in { name = "ussh"; enable = config.security.pam.ussh.enable && cfg.usshAuth; control = ussh.control; text = '' - ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} + (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"; text = '' + ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} ''; }) - (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; text = '' - ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} + (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; text = '' + window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} ''; }) - (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; text = '' - ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} + (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so"; text = '' + mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} ''; }) - (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; text = '' - ${pkgs.pam_dp9ik}/lib/security/pam_p9.so ${dp9ik.authserver} + (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so"; text = '' + ${dp9ik.authserver} ''; }) - { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; text = '' - ${pkgs.fprintd}/lib/security/pam_fprintd.so + { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${pkgs.fprintd}/lib/security/pam_fprintd.so"; text = '' ''; } ] ++ # Modules in this block require having the password set in PAM_AUTHTOK. @@ -652,202 +647,177 @@ let || cfg.duoSecurity.enable || cfg.zfs)) [ - { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; text = '' - ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; text = '' - pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth + { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "pam_unix.so"; text = '' + ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; text = '' - ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' + unwrap ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; text = '' - ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; text = '' - ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; text = '' + homes=${config.security.pam.zfs.homes} ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; text = '' - ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' + disable_interactive ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; text = '' - ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; text = '' + kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; text = '' - ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; text = '' - ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly "store-only"} + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; text = '' + ${optionalString cfg.gnupg.storeOnly "store-only"} ''; } - { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; text = '' - ${pkgs.pam}/lib/security/pam_faildelay.so delay=${toString cfg.failDelay.delay} + { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; text = '' + delay=${toString cfg.failDelay.delay} ''; } - { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; text = '' - ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so no_increment_hotp + { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; text = '' + no_increment_hotp ''; } - { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; text = '' - ${pkgs.duo-unix}/lib/security/pam_duo.so + { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; text = '' ''; } ]) ++ [ - { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; text = '' - ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; text = '' - pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass + { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "pam_unix.so"; text = '' + ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass ''; } - { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; text = '' - ${pkgs.otpw}/lib/security/pam_otpw.so + { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; text = '' ''; } - { name = "ldap"; enable = use_ldap; control = "sufficient"; text = '' - ${pam_ldap}/lib/security/pam_ldap.so use_first_pass + { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' + use_first_pass ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; text = '' - ${pkgs.kanidm}/lib/pam_kanidm.so ignore_unknown_user use_first_pass + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' + ignore_unknown_user use_first_pass ''; } - { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; text = '' - ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass + { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' + use_first_pass ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; text = '' - ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' + use_first_pass ''; } - { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; text = '' - ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass + { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; text = '' + action=validate use_first_pass ''; } - { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; text = '' - ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass + { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; text = '' + action=store use_first_pass ''; } - { name = "deny"; control = "required"; text = '' - pam_deny.so + { name = "deny"; control = "required"; modulePath = "pam_deny.so"; text = '' ''; } ]; password = [ - { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; text = '' - ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix"; control = "sufficient"; text = '' - pam_unix.so nullok yescrypt + { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; text = '' + nullok yescrypt ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; text = '' - ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; text = '' - ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; text = '' - ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; text = '' + homes=${config.security.pam.zfs.homes} ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; text = '' - ${pkgs.pam_mount}/lib/security/pam_mount.so + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' ''; } - { name = "ldap"; enable = use_ldap; control = "sufficient"; text = '' - ${pam_ldap}/lib/security/pam_ldap.so + { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; text = '' - ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' + config_file=/etc/security/pam_mysql.conf ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; text = '' - ${pkgs.kanidm}/lib/pam_kanidm.so + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' ''; } - { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; text = '' - ${pkgs.sssd}/lib/security/pam_sss.so + { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; text = '' - ${pam_krb5}/lib/security/pam_krb5.so use_first_pass + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' + use_first_pass ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; text = '' - ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' + use_authtok ''; } ]; session = [ - { name = "env"; enable = cfg.setEnvironment; control = "required"; text = '' - pam_env.so conffile=/etc/pam/environment readenv=0 + { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; text = '' + conffile=/etc/pam/environment readenv=0 ''; } - { name = "unix"; control = "required"; text = '' - pam_unix.so + { name = "unix"; control = "required"; modulePath = "pam_unix.so"; text = '' ''; } - { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; text = '' - pam_loginuid.so + { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "pam_loginuid.so"; text = '' ''; } - { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; text = '' - ${pkgs.pam}/lib/security/pam_tty_audit.so ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} + { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_tty_audit.so"; text = '' + ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} ''; } - { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; text = '' - ${config.systemd.package}/lib/security/pam_systemd_home.so + { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; text = '' - ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} + { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_mkhomedir.so"; text = '' + silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} ''; } - { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; text = '' - ${pkgs.pam}/lib/security/pam_lastlog.so silent + { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; text = '' + silent ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; text = '' - ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' ''; } # 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 - { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; text = '' - pam_succeed_if.so service = systemd-user + { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; text = '' + service = systemd-user ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; text = '' - ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; text = '' - pam_succeed_if.so service = systemd-user + { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; text = '' + service = systemd-user ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; text = '' - ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; text = '' + homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; text = '' - ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' + disable_interactive ''; } - { name = "ldap"; enable = use_ldap; control = "optional"; text = '' - ${pam_ldap}/lib/security/pam_ldap.so + { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; text = '' - ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' + config_file=/etc/security/pam_mysql.conf ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; text = '' - ${pkgs.kanidm}/lib/pam_kanidm.so + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' ''; } - { name = "sss"; enable = config.services.sssd.enable; control = "optional"; text = '' - ${pkgs.sssd}/lib/security/pam_sss.so + { name = "sss"; enable = config.services.sssd.enable; control = "optional"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; text = '' - ${pam_krb5}/lib/security/pam_krb5.so + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' ''; } - { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; text = '' - ${pkgs.otpw}/lib/security/pam_otpw.so + { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; text = '' ''; } - { name = "systemd"; enable = cfg.startSession; control = "optional"; text = '' - ${config.systemd.package}/lib/security/pam_systemd.so + { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; text = '' ''; } - { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; text = '' - pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 + { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "pam_xauth.so"; text = '' + xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 ''; } - { name = "limits"; enable = cfg.limits != []; control = "required"; text = '' - ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} + { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; text = '' + conf=${makeLimitsConf cfg.limits} ''; } - { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; text = '' - ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} + { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; text = '' + motd=${motd} ''; } - { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; text = '' - ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug + { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; text = '' + order=user,group,default debug ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; text = '' - ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; text = '' + kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; text = '' - ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' + auto_start ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; text = '' - ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"} + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; text = '' + ${optionalString cfg.gnupg.noAutostart " no-autostart"} ''; } - { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; control = "optional"; text = '' - ${pkgs.lxc}/lib/security/pam_cgfs.so -c all + { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; control = "optional"; modulePath = "${pkgs.lxc}/lib/security/pam_cgfs.so"; text = '' + -c all ''; } ]; }; From 6eea7fb19485f7a6138975c7904e1e77a051e481 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Mon, 18 Sep 2023 18:39:53 -0400 Subject: [PATCH 09/15] nixos/pam: extract args field Module arguments have common escaping rules for all PAMs. --- nixos/modules/security/pam.nix | 287 ++++++++++++++++++++++----------- nixos/tests/pam/test_chfn.py | 2 +- 2 files changed, 191 insertions(+), 98 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 012b83c6e1b2..bcdb69eb06d3 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -39,10 +39,19 @@ let Either the full filename of the PAM to be used by the application (it begins with a '/'), or a relative pathname from the default module location. See `module-path` in {manpage}`pam.conf(5)` for details. ''; }; + args = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Tokens that can be used to modify the specific behavior of the given PAM. Such arguments will be documented for each individual module. See `module-arguments` in {manpage}`pam.conf(5)` for details. + + Escaping rules for spaces and square brackets are automatically applied. + ''; + }; text = mkOption { type = types.str; description = lib.mdDoc '' - Text of the rule (without `service`, `type`, `control` or `module-path` fields). + (Obsolete.) ''; }; }; @@ -536,10 +545,16 @@ let limits = mkDefault config.security.pam.loginLimits; text = let + # Formats a string for use in `module-arguments`. See `man pam.conf`. + formatModuleArgument = token: + if hasInfix " " token + then "[${replaceStrings ["]"] ["\\]"] token}]" + else token; formatRules = type: pipe cfg.rules.${type} [ (filter (rule: rule.enable)) (map (rule: concatStringsSep " " ( [ type rule.control rule.modulePath ] + ++ map formatModuleArgument rule.args ++ optional (rule.text != "") (removeSuffix "\n" rule.text) ))) (concatStringsSep "\n") @@ -565,11 +580,13 @@ let account = [ { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' - config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ + "config_file=/etc/security/pam_mysql.conf" + ]; text = '' ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' - ignore_unknown_user + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; args = [ + "ignore_unknown_user" + ]; text = '' ''; } { 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"; text = '' ''; } @@ -593,36 +610,58 @@ let ''; } { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "pam_rootok.so"; text = '' ''; } - { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; text = '' - use_uid + { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; args = [ + "use_uid" + ]; text = '' ''; } { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "pam_faillock.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' - config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ + "config_file=/etc/security/pam_mysql.conf" + ]; text = '' ''; } - { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; text = '' - file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles} + { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; args = [ + "file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles}" + ]; text = '' ''; } - (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; modulePath = "${pkgs.pam_p11}/lib/security/pam_p11.so"; text = '' - ${pkgs.opensc}/lib/opensc-pkcs11.so + (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" + ]; text = '' ''; }) - (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; text = '' - ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"} + (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; args = concatLists [ + (optional u2f.debug "debug") + (optional (u2f.authFile != null) "authfile=${u2f.authFile}") + (optional u2f.interactive "interactive") + (optional u2f.cue "cue") + (optional (u2f.appId != null) "appid=${u2f.appId}") + (optional (u2f.origin != null) "origin=${u2f.origin}") + ]; text = '' ''; }) { name = "usb"; enable = cfg.usbAuth; control = "sufficient"; modulePath = "${pkgs.pam_usb}/lib/security/pam_usb.so"; text = '' ''; } - (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"; text = '' - ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} + (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"; args = concatLists [ + (optional (ussh.caFile != null) "ca_file=${ussh.caFile}") + (optional (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}") + (optional (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}") + (optional (ussh.group != null) "group=${ussh.group}") + ]; text = '' ''; }) - (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; text = '' - window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} + (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; args = [ + "window=${toString oath.window}" + "usersfile=${toString oath.usersFile}" + "digits=${toString oath.digits}" + ]; text = '' ''; }) - (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so"; text = '' - mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} + (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so"; args = concatLists [ + (singleton "mode=${toString yubi.mode}") + (optional (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}") + (optional (yubi.mode == "client") "id=${toString yubi.id}") + (optional yubi.debug "debug") + ]; text = '' ''; }) - (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so"; text = '' - ${dp9ik.authserver} + (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so"; args = [ + dp9ik.authserver + ]; text = '' ''; }) { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${pkgs.fprintd}/lib/security/pam_fprintd.so"; text = '' ''; } @@ -649,61 +688,84 @@ let [ { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "pam_unix.so"; text = '' - ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth + { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "pam_unix.so"; args = concatLists [ + (optional cfg.allowNullPassword "nullok") + (optional cfg.nodelay "nodelay") + (singleton "likeauth") + ]; text = '' ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' - unwrap + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; args = [ + "unwrap" + ]; text = '' ''; } { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; text = '' - homes=${config.security.pam.zfs.homes} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; args = [ + "homes=${config.security.pam.zfs.homes}" + ]; text = '' ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' - disable_interactive + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; args = [ + "disable_interactive" + ]; text = '' ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; text = '' - kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; args = [ + "kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5" + ]; text = '' ''; } { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; text = '' - ${optionalString cfg.gnupg.storeOnly "store-only"} + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; args = concatLists [ + (optional cfg.gnupg.storeOnly "store-only") + ]; text = '' ''; } - { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; text = '' - delay=${toString cfg.failDelay.delay} + { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; args = [ + "delay=${toString cfg.failDelay.delay}" + ]; text = '' ''; } - { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; text = '' - no_increment_hotp + { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; args = [ + "no_increment_hotp" + ]; text = '' ''; } { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; text = '' ''; } ]) ++ [ { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "pam_unix.so"; text = '' - ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass + { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "pam_unix.so"; args = concatLists [ + (optional cfg.allowNullPassword "nullok") + (optional cfg.nodelay "nodelay") + (singleton "likeauth") + (singleton "try_first_pass") + ]; text = '' ''; } { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; text = '' ''; } - { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' - use_first_pass + { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; args = [ + "use_first_pass" + ]; text = '' ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' - ignore_unknown_user use_first_pass + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; args = [ + "ignore_unknown_user" + "use_first_pass" + ]; text = '' ''; } - { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' - use_first_pass + { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; args = [ + "use_first_pass" + ]; text = '' ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' - use_first_pass + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; args = [ + "use_first_pass" + ]; text = '' ''; } - { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; text = '' - action=validate use_first_pass + { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; args = [ + "action=validate" + "use_first_pass" + ]; text = '' ''; } - { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; text = '' - action=store use_first_pass + { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; args = [ + "action=store" + "use_first_pass" + ]; text = '' ''; } { name = "deny"; control = "required"; modulePath = "pam_deny.so"; text = '' ''; } @@ -712,53 +774,68 @@ let password = [ { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; text = '' - nullok yescrypt + { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; args = [ + "nullok" + "yescrypt" + ]; text = '' ''; } { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' ''; } { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; text = '' - homes=${config.security.pam.zfs.homes} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; args = [ + "homes=${config.security.pam.zfs.homes}" + ]; text = '' ''; } { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' ''; } { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' - config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ + "config_file=/etc/security/pam_mysql.conf" + ]; text = '' ''; } { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' ''; } { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' - use_first_pass + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; args = [ + "use_first_pass" + ]; text = '' ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' - use_authtok + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; args = [ + "use_authtok" + ]; text = '' ''; } ]; session = [ - { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; text = '' - conffile=/etc/pam/environment readenv=0 + { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; args = [ + "conffile=/etc/pam/environment" + "readenv=0" + ]; text = '' ''; } { name = "unix"; control = "required"; modulePath = "pam_unix.so"; text = '' ''; } { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "pam_loginuid.so"; text = '' ''; } - { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_tty_audit.so"; text = '' - ${optionalString cfg.ttyAudit.openOnly "open_only"} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} + { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_tty_audit.so"; args = concatLists [ + (optional cfg.ttyAudit.openOnly "open_only") + (optional (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}") + (optional (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}") + ]; text = '' ''; } { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_mkhomedir.so"; text = '' - silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=${config.security.pam.makeHomeDir.umask} + { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_mkhomedir.so"; args = [ + "silent" + "skel=${config.security.pam.makeHomeDir.skelDirectory}" + "umask=${config.security.pam.makeHomeDir.umask}" + ]; text = '' ''; } - { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; text = '' - silent + { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; args = [ + "silent" + ]; text = '' ''; } { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' ''; } @@ -766,24 +843,30 @@ 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"; text = '' - service = systemd-user + { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; args = [ + "service" "=" "systemd-user" + ]; text = '' ''; } { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; text = '' - service = systemd-user + { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; args = [ + "service" "=" "systemd-user" + ]; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; text = '' - homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; args = concatLists [ + (singleton "homes=${config.security.pam.zfs.homes}") + (optional config.security.pam.zfs.noUnmount "nounmount") + ]; text = '' ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' - disable_interactive + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; args = [ + "disable_interactive" + ]; text = '' ''; } { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; text = '' - config_file=/etc/security/pam_mysql.conf + { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ + "config_file=/etc/security/pam_mysql.conf" + ]; text = '' ''; } { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' ''; } @@ -795,29 +878,39 @@ let ''; } { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; text = '' ''; } - { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "pam_xauth.so"; text = '' - xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 + { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "pam_xauth.so"; args = [ + "xauthpath=${pkgs.xorg.xauth}/bin/xauth" + "systemuser=99" + ]; text = '' ''; } - { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; text = '' - conf=${makeLimitsConf cfg.limits} + { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; args = [ + "conf=${makeLimitsConf cfg.limits}" + ]; text = '' ''; } - { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; text = '' - motd=${motd} + { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; args = [ + "motd=${motd}" + ]; text = '' ''; } - { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; text = '' - order=user,group,default debug + { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; args = [ + "order=user,group,default" + "debug" + ]; text = '' ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; text = '' - kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; args = [ + "kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5" + ]; text = '' ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' - auto_start + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; args = [ + "auto_start" + ]; text = '' ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; text = '' - ${optionalString cfg.gnupg.noAutostart " no-autostart"} + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; args = concatLists [ + (optional cfg.gnupg.noAutostart " no-autostart") + ]; text = '' ''; } - { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; control = "optional"; modulePath = "${pkgs.lxc}/lib/security/pam_cgfs.so"; text = '' - -c all + { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; control = "optional"; modulePath = "${pkgs.lxc}/lib/security/pam_cgfs.so"; args = [ + "-c" "all" + ]; text = '' ''; } ]; }; diff --git a/nixos/tests/pam/test_chfn.py b/nixos/tests/pam/test_chfn.py index a48438b8d305..4fd33447a543 100644 --- a/nixos/tests/pam/test_chfn.py +++ b/nixos/tests/pam/test_chfn.py @@ -6,7 +6,7 @@ expected_lines = { "auth required pam_deny.so", "auth sufficient @@pam_ccreds@@/lib/security/pam_ccreds.so action=store use_first_pass", "auth sufficient pam_rootok.so", - "auth sufficient pam_unix.so likeauth try_first_pass", + "auth sufficient pam_unix.so likeauth try_first_pass", "password sufficient @@pam_krb5@@/lib/security/pam_krb5.so use_first_pass", "password sufficient pam_unix.so nullok yescrypt", "session optional @@pam_krb5@@/lib/security/pam_krb5.so", From 5b8439f9666d3fc6ef3139ce48f4d9b34e1fd525 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sun, 24 Sep 2023 19:55:54 -0400 Subject: [PATCH 10/15] nixos/pam: add settings option for common argument styles Adds easily overrideable settings for the most common PAM argument styles. These are: - Flag (e.g. "use_first_pass"): rendered for true boolean values. false values are ignored. - Key-value (e.g. "action=validate"): rendered for non-null, non-boolean values. Most PAM arguments can be configured this way. Others can still be configured with the 'args' option. --- nixos/modules/security/pam.nix | 339 +++++++++++++++++---------------- nixos/tests/pam/pam-u2f.nix | 2 +- 2 files changed, 177 insertions(+), 164 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index bcdb69eb06d3..2d79c9142121 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -12,7 +12,7 @@ let description = lib.mdDoc '' PAM `${type}` rules for this service. ''; - type = types.listOf (types.submodule { + type = types.listOf (types.submodule ({ config, ... }: { options = { name = mkOption { type = types.str; @@ -41,11 +41,21 @@ let }; args = mkOption { type = types.listOf types.str; - default = []; description = lib.mdDoc '' Tokens that can be used to modify the specific behavior of the given PAM. Such arguments will be documented for each individual module. See `module-arguments` in {manpage}`pam.conf(5)` for details. Escaping rules for spaces and square brackets are automatically applied. + + {option}`settings` are automatically added as {option}`args`. It's recommended to use the {option}`settings` option whenever possible so that arguments can be overridden. + ''; + }; + settings = mkOption { + type = with types; attrsOf (nullOr (oneOf [ bool str int pathInStore ])); + default = {}; + description = lib.mdDoc '' + 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. ''; }; text = mkOption { @@ -55,7 +65,15 @@ let ''; }; }; - }); + config = { + # Formats an attrset of settings as args for use as `module-arguments`. + args = concatLists (flip mapAttrsToList config.settings (name: value: + if isBool value + then optional value name + else optional (value != null) "${name}=${toString value}" + )); + }; + })); }; parentConfig = config; @@ -580,13 +598,13 @@ let account = [ { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ - "config_file=/etc/security/pam_mysql.conf" - ]; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { + config_file = "/etc/security/pam_mysql.conf"; + }; text = '' ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; args = [ - "ignore_unknown_user" - ]; text = '' + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = { + ignore_unknown_user = true; + }; text = '' ''; } { 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"; text = '' ''; } @@ -610,54 +628,49 @@ let ''; } { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "pam_rootok.so"; text = '' ''; } - { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; args = [ - "use_uid" - ]; text = '' + { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; settings = { + use_uid = true; + }; text = '' ''; } { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "pam_faillock.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ - "config_file=/etc/security/pam_mysql.conf" - ]; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { + config_file = "/etc/security/pam_mysql.conf"; + }; text = '' ''; } - { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; args = [ - "file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles}" - ]; text = '' + { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; settings = { + file = lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles; + }; text = '' ''; } (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" ]; text = '' ''; }) - (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; args = concatLists [ - (optional u2f.debug "debug") - (optional (u2f.authFile != null) "authfile=${u2f.authFile}") - (optional u2f.interactive "interactive") - (optional u2f.cue "cue") - (optional (u2f.appId != null) "appid=${u2f.appId}") - (optional (u2f.origin != null) "origin=${u2f.origin}") - ]; text = '' - ''; }) + (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; + }; text = ('' + ''); }) { name = "usb"; enable = cfg.usbAuth; control = "sufficient"; modulePath = "${pkgs.pam_usb}/lib/security/pam_usb.so"; text = '' ''; } - (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"; args = concatLists [ - (optional (ussh.caFile != null) "ca_file=${ussh.caFile}") - (optional (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}") - (optional (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}") - (optional (ussh.group != null) "group=${ussh.group}") - ]; text = '' + (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; + authorized_principals_file = ussh.authorizedPrincipalsFile; + inherit (ussh) group; + }; text = '' ''; }) - (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; args = [ - "window=${toString oath.window}" - "usersfile=${toString oath.usersFile}" - "digits=${toString oath.digits}" - ]; text = '' + (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; settings = { + inherit (oath) window digits; + usersfile = oath.usersFile; + }; text = '' ''; }) - (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so"; args = concatLists [ - (singleton "mode=${toString yubi.mode}") - (optional (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}") - (optional (yubi.mode == "client") "id=${toString yubi.id}") - (optional yubi.debug "debug") - ]; text = '' + (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so"; settings = { + inherit (yubi) mode debug; + chalresp_path = yubi.challengeResponsePath; + id = mkIf (yubi.mode == "client") yubi.id; + }; text = '' ''; }) (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so"; args = [ dp9ik.authserver @@ -688,84 +701,84 @@ let [ { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "pam_unix.so"; args = concatLists [ - (optional cfg.allowNullPassword "nullok") - (optional cfg.nodelay "nodelay") - (singleton "likeauth") - ]; text = '' + { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "pam_unix.so"; settings = { + nullok = cfg.allowNullPassword; + inherit (cfg) nodelay; + likeauth = true; + }; text = '' ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; args = [ - "unwrap" - ]; text = '' + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; settings = { + unwrap = true; + }; text = '' ''; } { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; args = [ - "homes=${config.security.pam.zfs.homes}" - ]; text = '' + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { + inherit (config.security.pam.zfs) homes; + }; text = '' ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; args = [ - "disable_interactive" - ]; text = '' + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = { + disable_interactive = true; + }; text = '' ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; args = [ - "kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5" - ]; text = '' + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = { + kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5"; + }; text = '' ''; } { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; args = concatLists [ - (optional cfg.gnupg.storeOnly "store-only") - ]; text = '' + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = { + store-only = cfg.gnupg.storeOnly; + }; text = '' ''; } - { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; args = [ - "delay=${toString cfg.failDelay.delay}" - ]; text = '' + { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; settings = { + inherit (cfg.failDelay) delay; + }; text = '' ''; } - { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; args = [ - "no_increment_hotp" - ]; text = '' + { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; settings = { + no_increment_hotp = true; + }; text = '' ''; } { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; text = '' ''; } ]) ++ [ { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "pam_unix.so"; args = concatLists [ - (optional cfg.allowNullPassword "nullok") - (optional cfg.nodelay "nodelay") - (singleton "likeauth") - (singleton "try_first_pass") - ]; text = '' + { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "pam_unix.so"; settings = { + nullok = cfg.allowNullPassword; + inherit (cfg) nodelay; + likeauth = true; + try_first_pass = true; + }; text = '' ''; } { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; text = '' ''; } - { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; args = [ - "use_first_pass" - ]; text = '' + { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; settings = { + use_first_pass = true; + }; text = '' ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; args = [ - "ignore_unknown_user" - "use_first_pass" - ]; text = '' + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = { + ignore_unknown_user = true; + use_first_pass = true; + }; text = '' ''; } - { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; args = [ - "use_first_pass" - ]; text = '' + { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; settings = { + use_first_pass = true; + }; text = '' ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; args = [ - "use_first_pass" - ]; text = '' + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = { + use_first_pass = true; + }; text = '' ''; } - { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; args = [ - "action=validate" - "use_first_pass" - ]; text = '' + { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = { + action = "validate"; + use_first_pass = true; + }; text = '' ''; } - { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; args = [ - "action=store" - "use_first_pass" - ]; text = '' + { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = { + action = "store"; + use_first_pass = true; + }; text = '' ''; } { name = "deny"; control = "required"; modulePath = "pam_deny.so"; text = '' ''; } @@ -774,68 +787,68 @@ let password = [ { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; args = [ - "nullok" - "yescrypt" - ]; text = '' + { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; settings = { + nullok = true; + yescrypt = true; + }; text = '' ''; } { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' ''; } { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; args = [ - "homes=${config.security.pam.zfs.homes}" - ]; text = '' + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { + inherit (config.security.pam.zfs) homes; + }; text = '' ''; } { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' ''; } { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ - "config_file=/etc/security/pam_mysql.conf" - ]; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { + config_file = "/etc/security/pam_mysql.conf"; + }; text = '' ''; } { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' ''; } { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; args = [ - "use_first_pass" - ]; text = '' + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = { + use_first_pass = true; + }; text = '' ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; args = [ - "use_authtok" - ]; text = '' + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = { + use_authtok = true; + }; text = '' ''; } ]; session = [ - { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; args = [ - "conffile=/etc/pam/environment" - "readenv=0" - ]; text = '' + { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; settings = { + conffile = "/etc/pam/environment"; + readenv = 0; + }; text = '' ''; } { name = "unix"; control = "required"; modulePath = "pam_unix.so"; text = '' ''; } { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "pam_loginuid.so"; text = '' ''; } - { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_tty_audit.so"; args = concatLists [ - (optional cfg.ttyAudit.openOnly "open_only") - (optional (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}") - (optional (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}") - ]; text = '' + { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_tty_audit.so"; settings = { + open_only = cfg.ttyAudit.openOnly; + enable = cfg.ttyAudit.enablePattern; + disable = cfg.ttyAudit.disablePattern; + }; text = '' ''; } { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' ''; } - { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_mkhomedir.so"; args = [ - "silent" - "skel=${config.security.pam.makeHomeDir.skelDirectory}" - "umask=${config.security.pam.makeHomeDir.umask}" - ]; text = '' + { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_mkhomedir.so"; settings = { + silent = true; + skel = config.security.pam.makeHomeDir.skelDirectory; + inherit (config.security.pam.makeHomeDir) umask; + }; text = '' ''; } - { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; args = [ - "silent" - ]; text = '' + { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; settings = { + silent = true; + }; text = '' ''; } { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' ''; } @@ -853,20 +866,20 @@ let "service" "=" "systemd-user" ]; text = '' ''; } - { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; args = concatLists [ - (singleton "homes=${config.security.pam.zfs.homes}") - (optional config.security.pam.zfs.noUnmount "nounmount") - ]; text = '' + { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { + inherit (config.security.pam.zfs) homes; + nounmount = config.security.pam.zfs.noUnmount; + }; text = '' ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; args = [ - "disable_interactive" - ]; text = '' + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = { + disable_interactive = true; + }; text = '' ''; } { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' ''; } - { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; args = [ - "config_file=/etc/security/pam_mysql.conf" - ]; text = '' + { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { + config_file = "/etc/security/pam_mysql.conf"; + }; text = '' ''; } { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' ''; } @@ -878,35 +891,35 @@ let ''; } { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; text = '' ''; } - { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "pam_xauth.so"; args = [ - "xauthpath=${pkgs.xorg.xauth}/bin/xauth" - "systemuser=99" - ]; text = '' + { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "pam_xauth.so"; settings = { + xauthpath = "${pkgs.xorg.xauth}/bin/xauth"; + systemuser = 99; + }; text = '' ''; } - { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; args = [ - "conf=${makeLimitsConf cfg.limits}" - ]; text = '' + { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; settings = { + conf = "${makeLimitsConf cfg.limits}"; + }; text = '' ''; } - { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; args = [ - "motd=${motd}" - ]; text = '' + { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; settings = { + inherit motd; + }; text = '' ''; } - { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; args = [ - "order=user,group,default" - "debug" - ]; text = '' + { 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; + }; text = '' ''; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; args = [ - "kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5" - ]; text = '' + { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = { + kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5"; + }; text = '' ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; args = [ - "auto_start" - ]; text = '' + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = { + auto_start = true; + }; text = '' ''; } - { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; args = concatLists [ - (optional cfg.gnupg.noAutostart " no-autostart") - ]; text = '' + { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = { + no-autostart = cfg.gnupg.noAutostart; + }; text = '' ''; } { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; control = "optional"; modulePath = "${pkgs.lxc}/lib/security/pam_cgfs.so"; args = [ "-c" "all" diff --git a/nixos/tests/pam/pam-u2f.nix b/nixos/tests/pam/pam-u2f.nix index 07408dea797e..46e307a3f125 100644 --- a/nixos/tests/pam/pam-u2f.nix +++ b/nixos/tests/pam/pam-u2f.nix @@ -20,7 +20,7 @@ import ../make-test-python.nix ({ ... }: '' machine.wait_for_unit("multi-user.target") machine.succeed( - 'egrep "auth required .*/lib/security/pam_u2f.so.*debug.*interactive.*cue.*origin=nixos-test" /etc/pam.d/ -R' + 'egrep "auth required .*/lib/security/pam_u2f.so.*cue.*debug.*interactive.*origin=nixos-test" /etc/pam.d/ -R' ) ''; }) From e86487e579271dc5f0d7627a8f7f5a496d133d59 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sat, 16 Sep 2023 14:33:39 -0400 Subject: [PATCH 11/15] nixos/pam: remove empty text fields --- nixos/modules/security/pam.nix | 262 +++++++++++---------------------- 1 file changed, 85 insertions(+), 177 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 2d79c9142121..8c9e17bd9cf2 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -58,12 +58,6 @@ let 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. ''; }; - text = mkOption { - type = types.str; - description = lib.mdDoc '' - (Obsolete.) - ''; - }; }; config = { # Formats an attrset of settings as args for use as `module-arguments`. @@ -573,7 +567,6 @@ let (map (rule: concatStringsSep " " ( [ type rule.control rule.modulePath ] ++ map formatModuleArgument rule.args - ++ optional (rule.text != "") (removeSuffix "\n" rule.text) ))) (concatStringsSep "\n") ]; @@ -596,88 +589,65 @@ let # module provides the right hooks. rules = { account = [ - { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' - ''; } + { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.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"; - }; text = '' - ''; } + }; } { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = { ignore_unknown_user = true; - }; text = '' - ''; } - { 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"; text = '' - ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' - ''; } - { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; text = '' - ''; } - { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so"; text = '' - ''; } - { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' - ''; } + }; } + { 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"; } + { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; } + { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; } + { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so"; } + { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } # 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"; text = '' - ''; } + { name = "unix"; control = "required"; modulePath = "pam_unix.so"; } ]; auth = [ - { 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"; text = '' - ''; } - { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "pam_rootok.so"; text = '' - ''; } + { 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 = { use_uid = true; - }; text = '' - ''; } - { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "pam_faillock.so"; text = '' - ''; } + }; } + { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "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"; - }; text = '' - ''; } + }; } { name = "ssh_agent_auth"; enable = config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; settings = { file = lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles; - }; text = '' - ''; } + }; } (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" - ]; text = '' - ''; }) + ]; }) (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; - }; text = ('' - ''); }) - { name = "usb"; enable = cfg.usbAuth; control = "sufficient"; modulePath = "${pkgs.pam_usb}/lib/security/pam_usb.so"; text = '' - ''; } + }; }) + { name = "usb"; enable = cfg.usbAuth; control = "sufficient"; modulePath = "${pkgs.pam_usb}/lib/security/pam_usb.so"; } (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; authorized_principals_file = ussh.authorizedPrincipalsFile; inherit (ussh) group; - }; text = '' - ''; }) + }; }) (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; settings = { inherit (oath) window digits; usersfile = oath.usersFile; - }; text = '' - ''; }) + }; }) (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so"; settings = { inherit (yubi) mode debug; chalresp_path = yubi.challengeResponsePath; id = mkIf (yubi.mode == "client") yubi.id; - }; text = '' - ''; }) + }; }) (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so"; args = [ dp9ik.authserver - ]; text = '' - ''; }) - { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${pkgs.fprintd}/lib/security/pam_fprintd.so"; text = '' - ''; } + ]; }) + { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${pkgs.fprintd}/lib/security/pam_fprintd.so"; } ] ++ # 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 @@ -699,232 +669,170 @@ let || cfg.duoSecurity.enable || cfg.zfs)) [ - { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' - ''; } + { 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 = { nullok = cfg.allowNullPassword; inherit (cfg) nodelay; likeauth = true; - }; text = '' - ''; } + }; } { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; settings = { unwrap = true; - }; text = '' - ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' - ''; } + }; } + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; } { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { inherit (config.security.pam.zfs) homes; - }; text = '' - ''; } + }; } { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = { disable_interactive = true; - }; text = '' - ''; } + }; } { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = { kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5"; - }; text = '' - ''; } - { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; text = '' - ''; } + }; } + { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; } { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = { store-only = cfg.gnupg.storeOnly; - }; text = '' - ''; } + }; } { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; settings = { inherit (cfg.failDelay) delay; - }; text = '' - ''; } + }; } { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; settings = { no_increment_hotp = true; - }; text = '' - ''; } - { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; text = '' - ''; } + }; } + { 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"; text = '' - ''; } + { 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 = { nullok = cfg.allowNullPassword; inherit (cfg) nodelay; likeauth = true; try_first_pass = true; - }; text = '' - ''; } - { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; text = '' - ''; } + }; } + { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; } { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; settings = { use_first_pass = true; - }; text = '' - ''; } + }; } { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = { ignore_unknown_user = true; use_first_pass = true; - }; text = '' - ''; } + }; } { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; settings = { use_first_pass = true; - }; text = '' - ''; } + }; } { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = { use_first_pass = true; - }; text = '' - ''; } + }; } { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = { action = "validate"; use_first_pass = true; - }; text = '' - ''; } + }; } { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = { action = "store"; use_first_pass = true; - }; text = '' - ''; } - { name = "deny"; control = "required"; modulePath = "pam_deny.so"; text = '' - ''; } + }; } + { name = "deny"; control = "required"; modulePath = "pam_deny.so"; } ]; password = [ - { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' - ''; } + { 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 = { nullok = true; yescrypt = true; - }; text = '' - ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' - ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' - ''; } + }; } + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; } + { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; } { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { inherit (config.security.pam.zfs) homes; - }; text = '' - ''; } - { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; text = '' - ''; } - { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' - ''; } + }; } + { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; } + { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.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"; - }; text = '' - ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' - ''; } - { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' - ''; } + }; } + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/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; - }; text = '' - ''; } + }; } { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = { use_authtok = true; - }; text = '' - ''; } + }; } ]; session = [ { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; settings = { conffile = "/etc/pam/environment"; readenv = 0; - }; text = '' - ''; } - { name = "unix"; control = "required"; modulePath = "pam_unix.so"; text = '' - ''; } - { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "pam_loginuid.so"; text = '' - ''; } + }; } + { 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 = { open_only = cfg.ttyAudit.openOnly; enable = cfg.ttyAudit.enablePattern; disable = cfg.ttyAudit.disablePattern; - }; text = '' - ''; } - { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; text = '' - ''; } + }; } + { 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 = { silent = true; skel = config.security.pam.makeHomeDir.skelDirectory; inherit (config.security.pam.makeHomeDir) umask; - }; text = '' - ''; } + }; } { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; settings = { silent = true; - }; text = '' - ''; } - { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; text = '' - ''; } + }; } + { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; } # 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 { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; args = [ "service" "=" "systemd-user" - ]; text = '' - ''; } - { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; text = '' - ''; } + ]; } + { 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 = [ "service" "=" "systemd-user" - ]; text = '' - ''; } + ]; } { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { inherit (config.security.pam.zfs) homes; nounmount = config.security.pam.zfs.noUnmount; - }; text = '' - ''; } + }; } { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = { disable_interactive = true; - }; text = '' - ''; } - { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; text = '' - ''; } + }; } + { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; } { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { config_file = "/etc/security/pam_mysql.conf"; - }; text = '' - ''; } - { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; text = '' - ''; } - { name = "sss"; enable = config.services.sssd.enable; control = "optional"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; text = '' - ''; } - { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; text = '' - ''; } - { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; text = '' - ''; } - { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; text = '' - ''; } + }; } + { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${pkgs.kanidm}/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 = { xauthpath = "${pkgs.xorg.xauth}/bin/xauth"; systemuser = 99; - }; text = '' - ''; } + }; } { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; settings = { conf = "${makeLimitsConf cfg.limits}"; - }; text = '' - ''; } + }; } { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; settings = { inherit motd; - }; text = '' - ''; } + }; } { 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; - }; text = '' - ''; } + }; } { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = { kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5"; - }; text = '' - ''; } + }; } { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = { auto_start = true; - }; text = '' - ''; } + }; } { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = { no-autostart = cfg.gnupg.noAutostart; - }; text = '' - ''; } + }; } { name = "cgfs"; enable = config.virtualisation.lxc.lxcfs.enable; control = "optional"; modulePath = "${pkgs.lxc}/lib/security/pam_cgfs.so"; args = [ "-c" "all" - ]; text = '' - ''; } + ]; } ]; }; }; From 077cdcc7e9a9ed406860e97ce3590fba15aed18b Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sat, 16 Sep 2023 15:29:11 -0400 Subject: [PATCH 12/15] nixos/pam: convert rules to attrs, add order field Makes it possible to override properties of a rule by name. Introduces an 'order' field that can be overridden to change the sequence of rules. For now, the order value for each built-in rule is derived from its place in the hardcoded list of rules. --- nixos/modules/security/pam.nix | 50 +++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 8c9e17bd9cf2..b7a5d7131b57 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -11,14 +11,18 @@ let # These options are experimental and subject to breaking changes without notice. description = lib.mdDoc '' PAM `${type}` rules for this service. + + Attribute keys are the name of each rule. ''; - type = types.listOf (types.submodule ({ config, ... }: { + type = types.attrsOf (types.submodule ({ name, config, ... }: { options = { name = mkOption { type = types.str; description = lib.mdDoc '' Name of this rule. ''; + internal = true; + readOnly = true; }; enable = mkOption { type = types.bool; @@ -27,6 +31,23 @@ let Whether this rule is added to the PAM service config file. ''; }; + order = mkOption { + type = types.int; + description = lib.mdDoc '' + Order of this rule in the service file. Rules are arranged in ascending order of this value. + + ::: {.warning} + The `order` values for the built-in rules are subject to change. If you assign a constant value to this option, a system update could silently reorder your rule. You could be locked out of your system, or your system could be left wide open. When using this option, set it to a relative offset from another rule's `order` value: + + ```nix + { + security.pam.services.login.rules.auth.foo.order = + config.security.pam.services.login.rules.auth.unix.order + 10; + } + ``` + ::: + ''; + }; control = mkOption { type = types.str; description = lib.mdDoc '' @@ -60,6 +81,7 @@ let }; }; config = { + inherit name; # Formats an attrset of settings as args for use as `module-arguments`. args = concatLists (flip mapAttrsToList config.settings (name: value: if isBool value @@ -557,13 +579,21 @@ let limits = mkDefault config.security.pam.loginLimits; text = let + ensureUniqueOrder = type: rules: + let + checkPair = a: b: assert assertMsg (a.order != b.order) "security.pam.services.${name}.rules.${type}: rules '${a.name}' and '${b.name}' cannot have the same order value (${toString a.order})"; b; + checked = zipListsWith checkPair rules (drop 1 rules); + in take 1 rules ++ checked; # Formats a string for use in `module-arguments`. See `man pam.conf`. formatModuleArgument = token: if hasInfix " " token then "[${replaceStrings ["]"] ["\\]"] token}]" else token; formatRules = type: pipe cfg.rules.${type} [ + attrValues (filter (rule: rule.enable)) + (sort (a: b: a.order < b.order)) + (ensureUniqueOrder type) (map (rule: concatStringsSep " " ( [ type rule.control rule.modulePath ] ++ map formatModuleArgument rule.args @@ -587,8 +617,14 @@ let # !!! TODO: move the LDAP stuff to the LDAP module, and the # Samba stuff to the Samba module. This requires that the PAM # module provides the right hooks. - rules = { - account = [ + rules = let + autoOrderRules = flip pipe [ + (imap1 (index: rule: rule // { order = mkDefault (10000 + index * 100); } )) + (map (rule: nameValuePair rule.name (removeAttrs rule [ "name" ]))) + listToAttrs + ]; + in { + account = autoOrderRules [ { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.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"; @@ -607,7 +643,7 @@ let { name = "unix"; control = "required"; modulePath = "pam_unix.so"; } ]; - auth = [ + 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 = { @@ -730,9 +766,9 @@ let use_first_pass = true; }; } { name = "deny"; control = "required"; modulePath = "pam_deny.so"; } - ]; + ]); - password = [ + 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 = { nullok = true; @@ -758,7 +794,7 @@ let }; } ]; - session = [ + session = autoOrderRules [ { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; settings = { conffile = "/etc/pam/environment"; readenv = 0; From 43f7cb4a95436d58a0fe40ff501d42085d676691 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Mon, 9 Oct 2023 22:54:22 -0400 Subject: [PATCH 13/15] nixos/pam: add order comment to each rule line --- nixos/modules/security/pam.nix | 1 + nixos/tests/pam/test_chfn.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index b7a5d7131b57..242ce52bfb5b 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -597,6 +597,7 @@ let (map (rule: concatStringsSep " " ( [ type rule.control rule.modulePath ] ++ map formatModuleArgument rule.args + ++ [ "# ${rule.name} (order ${toString rule.order})" ] ))) (concatStringsSep "\n") ]; diff --git a/nixos/tests/pam/test_chfn.py b/nixos/tests/pam/test_chfn.py index 4fd33447a543..3cfbb3908e9d 100644 --- a/nixos/tests/pam/test_chfn.py +++ b/nixos/tests/pam/test_chfn.py @@ -15,9 +15,10 @@ expected_lines = { } actual_lines = set(machine.succeed("cat /etc/pam.d/chfn").splitlines()) -missing_lines = expected_lines - actual_lines -extra_lines = actual_lines - expected_lines -non_functional_lines = set([line for line in extra_lines if (line == "" or line.startswith("#"))]) +stripped_lines = set([line.split("#")[0].rstrip() for line in actual_lines]) +missing_lines = expected_lines - stripped_lines +extra_lines = stripped_lines - expected_lines +non_functional_lines = set([line for line in extra_lines if line == ""]) unexpected_functional_lines = extra_lines - non_functional_lines with subtest("All expected lines are in the file"): From e712b6e81d2e07ca33dce83d6270b3ab3cba0f90 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Wed, 20 Sep 2023 23:44:56 -0400 Subject: [PATCH 14/15] nixos/pam: generate apparmor includes from rules Removes redundant config from the module. Fixes a bug where some modules (e.g. ussh) were added to apparmor even though they had no rules enabled. --- nixos/modules/security/pam.nix | 98 +++++----------------------------- 1 file changed, 13 insertions(+), 85 deletions(-) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 242ce52bfb5b..bd6ab5856fcf 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -1508,9 +1508,7 @@ in fscrypt = {}; }; - security.apparmor.includes."abstractions/pam" = let - isEnabled = test: fold or false (map test (attrValues config.security.pam.services)); - in + security.apparmor.includes."abstractions/pam" = lib.concatMapStrings (name: "r ${config.environment.etc."pam.d/${name}".source},\n") (attrNames config.security.pam.services) + @@ -1519,88 +1517,18 @@ in mr ${getLib pkgs.pam}/lib/security/pam_*.so, r ${getLib pkgs.pam}/lib/security/, '' + - optionalString use_ldap '' - mr ${pam_ldap}/lib/security/pam_ldap.so, - '' + - optionalString config.services.kanidm.enablePam '' - mr ${pkgs.kanidm}/lib/pam_kanidm.so, - '' + - optionalString config.services.sssd.enable '' - mr ${pkgs.sssd}/lib/security/pam_sss.so, - '' + - optionalString config.security.pam.krb5.enable '' - mr ${pam_krb5}/lib/security/pam_krb5.so, - mr ${pam_ccreds}/lib/security/pam_ccreds.so, - '' + - optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) '' - mr ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so, - mr ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so, - '' + - optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication)) '' - mr ${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so, - '' + - optionalString (config.security.pam.enableSSHAgentAuth - && isEnabled (cfg: cfg.sshAgentAuth)) '' - mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so, - '' + - optionalString (isEnabled (cfg: cfg.fprintAuth)) '' - mr ${pkgs.fprintd}/lib/security/pam_fprintd.so, - '' + - optionalString (isEnabled (cfg: cfg.u2fAuth)) '' - mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so, - '' + - optionalString (isEnabled (cfg: cfg.usbAuth)) '' - mr ${pkgs.pam_usb}/lib/security/pam_usb.so, - '' + - optionalString (isEnabled (cfg: cfg.usshAuth)) '' - mr ${pkgs.pam_ussh}/lib/security/pam_ussh.so, - '' + - optionalString (isEnabled (cfg: cfg.oathAuth)) '' - "mr ${pkgs.oath-toolkit}/lib/security/pam_oath.so, - '' + - optionalString (isEnabled (cfg: cfg.mysqlAuth)) '' - mr ${pkgs.pam_mysql}/lib/security/pam_mysql.so, - '' + - optionalString (isEnabled (cfg: cfg.yubicoAuth)) '' - mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so, - '' + - optionalString (isEnabled (cfg: cfg.duoSecurity.enable)) '' - mr ${pkgs.duo-unix}/lib/security/pam_duo.so, - '' + - optionalString (isEnabled (cfg: cfg.otpwAuth)) '' - mr ${pkgs.otpw}/lib/security/pam_otpw.so, - '' + - 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, - '' + - optionalString (isEnabled (cfg: cfg.enableGnomeKeyring)) '' - mr ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so, - '' + - optionalString (isEnabled (cfg: cfg.startSession)) '' - mr ${config.systemd.package}/lib/security/pam_systemd.so, - '' + - optionalString (isEnabled (cfg: cfg.enableAppArmor) - && config.security.apparmor.enable) '' - mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so, - '' + - optionalString (isEnabled (cfg: cfg.enableKwallet)) '' - mr ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so, - '' + - optionalString config.virtualisation.lxc.lxcfs.enable '' - mr ${pkgs.lxc}/lib/security/pam_cgfs.so, - '' + - optionalString (isEnabled (cfg: cfg.zfs)) '' - mr ${config.boot.zfs.package}/lib/security/pam_zfs_key.so, - '' + - optionalString config.services.homed.enable '' - mr ${config.systemd.package}/lib/security/pam_systemd_home.so - ''; + (with lib; pipe config.security.pam.services [ + attrValues + (catAttrs "rules") + (concatMap attrValues) + (concatMap attrValues) + (filter (rule: rule.enable)) + (catAttrs "modulePath") + (filter (hasPrefix "/")) + unique + (map (module: "mr ${module},")) + concatLines + ]); }; } From 9d6e6e18bccb8e4dbcc5d68263cdcac16ce51973 Mon Sep 17 00:00:00 2001 From: Majiir Paktu Date: Sun, 24 Sep 2023 21:50:20 -0400 Subject: [PATCH 15/15] nixos/pam: add maintainer --- nixos/modules/security/pam.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index bd6ab5856fcf..709bb8b94a65 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -947,6 +947,8 @@ in { + meta.maintainers = [ maintainers.majiir ]; + imports = [ (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ]) ];