Change:
Basically redo after it was reverted in . Steps to reproduce the behavior: any file in /etc/pam.d/ The non-empty lines should be separated by at most one empty line. Add any other context about the problem here. Would you be interested? system: host os: multi-user?: sandbox: version: channels(root): channels(victor): nixpkgs: Maintainer information:
I still think it'd be nice to do. I messed up last time and haven't bothered trying again. Happy to se others giving it a try.
Fair enough, I'll give it a try based on what looks like a good pattern at the bottom of the same file. That said, I have a major hole in my understanding of Nix which I'm hoping someone might help with: how do I build and verify the result of a single file like on its own? Is that possible, or do I have to build another package which depends on it just to see whether it works?
This issue has been mentioned on NixOS Discourse. There might be relevant details there:

Discussion:
# Samba stuff to the Samba module.  This requires that the PAM # module provides the right hooks. text = mkDefault <del> ('' # Account management. account required pam_unix.so ${optionalString use_ldap "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"} ${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.krb5.enable "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"} ${optionalString cfg.googleOsLoginAccountVerification '' </del> <ins> ( '' # Account management. account required pam_unix.so '' + optionalString use_ldap '' account sufficient ${pam_ldap}/lib/security/pam_ldap.so '' + 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.krb5.enable '' account sufficient ${pam_krb5}/lib/security/pam_krb5.so '' + optionalString cfg.googleOsLoginAccountVerification '' </ins> account [success=ok ignore=ignore default=die] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so account [success=ok default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so <del> ''} # Authentication management. ${optionalString cfg.googleOsLoginAuthentication "auth [success=done perm_denied=bad default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so"} ${optionalString cfg.rootOK "auth sufficient pam_rootok.so"} ${optionalString cfg.requireWheel "auth required pam_wheel.so use_uid"} ${optionalString cfg.logFailures "auth required pam_faillock.so"} ${optionalString (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 "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 cfg.usbAuth "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"} ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"} ${let yubi = config.security.pam.yubico; in optionalString 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"}"} ${optionalString cfg.fprintAuth "auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"} '' + </del> <ins> '' + '' # Authentication management. '' + optionalString cfg.googleOsLoginAuthentication '' auth [success=done perm_denied=bad default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so '' + optionalString cfg.rootOK '' auth sufficient pam_rootok.so '' + optionalString cfg.requireWheel '' auth required pam_wheel.so use_uid '' + optionalString cfg.logFailures '' auth required pam_faillock.so '' + optionalString (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 '' 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 cfg.usbAuth '' auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so '' + (let oath = config.security.pam.oath; in optionalString cfg.oathAuth '' auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} '') + (let yubi = config.security.pam.yubico; in optionalString 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"} '') + optionalString cfg.fprintAuth '' auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so '' + </ins> # 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

# earlier point and it will run again with 'sufficient' further down. # We use try_first_pass the second time to avoid prompting password twice (optionalString (cfg.unixAuth && <del> (config.security.pam.enableEcryptfs || cfg.pamMount || cfg.enableKwallet || cfg.enableGnomeKeyring || cfg.googleAuthenticator.enable || cfg.gnupg.enable || cfg.duoSecurity.enable)) '' auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth ${optionalString config.security.pam.enableEcryptfs "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"} ${optionalString cfg.pamMount "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")} ${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" } ${optionalString cfg.googleAuthenticator.enable "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"} ${optionalString cfg.duoSecurity.enable "auth required ${pkgs.duo-unix}/lib/security/pam_duo.so"} '') + '' ${optionalString cfg.unixAuth "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass"} ${optionalString cfg.otpwAuth "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"} ${optionalString use_ldap "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"} ${optionalString config.services.sssd.enable "auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass"} ${optionalString config.krb5.enable '' </del> <ins> (config.security.pam.enableEcryptfs || cfg.pamMount || cfg.enableKwallet || cfg.enableGnomeKeyring || cfg.googleAuthenticator.enable || cfg.gnupg.enable || cfg.duoSecurity.enable)) ( '' auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth '' + optionalString config.security.pam.enableEcryptfs '' auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap '' + optionalString cfg.pamMount '' 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 '' + 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"} '' + optionalString cfg.googleAuthenticator.enable '' auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp '' + optionalString cfg.duoSecurity.enable '' auth required ${pkgs.duo-unix}/lib/security/pam_duo.so '' )) + optionalString cfg.unixAuth '' auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass '' + optionalString cfg.otpwAuth '' auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so '' + optionalString use_ldap '' auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass '' + optionalString config.services.sssd.enable '' auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass '' + optionalString config.krb5.enable '' </ins> auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass <del> ''} auth required pam_deny.so # Password management. password sufficient pam_unix.so nullok sha512 ${optionalString config.security.pam.enableEcryptfs "password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} ${optionalString cfg.pamMount "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} ${optionalString use_ldap "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"} ${optionalString config.services.sssd.enable "password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok"} ${optionalString config.krb5.enable "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"} ${optionalString cfg.enableGnomeKeyring "password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok"} # Session management. ${optionalString cfg.setEnvironment '' </del> <ins> '' + '' auth required pam_deny.so # Password management. password sufficient pam_unix.so nullok sha512 '' + optionalString config.security.pam.enableEcryptfs '' password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so '' + optionalString cfg.pamMount '' password optional ${pkgs.pam_mount}/lib/security/pam_mount.so '' + optionalString use_ldap '' password sufficient ${pam_ldap}/lib/security/pam_ldap.so '' + optionalString config.services.sssd.enable '' password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok '' + optionalString config.krb5.enable '' password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass '' + optionalString cfg.enableGnomeKeyring '' password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok '' + '' # Session management. '' + optionalString cfg.setEnvironment '' </ins> session required pam_env.so conffile=/etc/pam/environment readenv=0 <del> ''} session required pam_unix.so ${optionalString cfg.setLoginUid "session ${ if config.boot.isContainer then "optional" else "required" } pam_loginuid.so"} ${optionalString cfg.ttyAudit.enable "session required ${pkgs.pam}/lib/security/pam_tty_audit.so </del> <ins> '' + '' session required pam_unix.so '' + optionalString cfg.setLoginUid '' session ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so '' + optionalString cfg.ttyAudit.enable '' session required ${pkgs.pam}/lib/security/pam_tty_audit.so </ins> open_only=${toString cfg.ttyAudit.openOnly} ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"} ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"} <del> "} ${optionalString cfg.makeHomeDir "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0077"} ${optionalString cfg.updateWtmp "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"} ${optionalString config.security.pam.enableEcryptfs "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} ${optionalString cfg.pamMount "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive"} ${optionalString use_ldap "session optional ${pam_ldap}/lib/security/pam_ldap.so"} ${optionalString config.services.sssd.enable "session optional ${pkgs.sssd}/lib/security/pam_sss.so"} ${optionalString config.krb5.enable "session optional ${pam_krb5}/lib/security/pam_krb5.so"} ${optionalString cfg.otpwAuth "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"} ${optionalString cfg.startSession "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"} ${optionalString cfg.forwardXAuth "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"} ${optionalString (cfg.limits != []) "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"} ${optionalString (cfg.showMotd && config.users.motd != null) "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"} ${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) ("session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so" + " kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5")} ${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) "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"} ''); </del> <ins> '' + optionalString cfg.makeHomeDir '' session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0077 '' + optionalString cfg.updateWtmp '' session required ${pkgs.pam}/lib/security/pam_lastlog.so silent '' + optionalString config.security.pam.enableEcryptfs '' session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so '' + optionalString cfg.pamMount '' session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive '' + optionalString use_ldap '' session optional ${pam_ldap}/lib/security/pam_ldap.so '' + optionalString config.services.sssd.enable '' session optional ${pkgs.sssd}/lib/security/pam_sss.so '' + optionalString config.krb5.enable '' session optional ${pam_krb5}/lib/security/pam_krb5.so '' + optionalString cfg.otpwAuth '' session optional ${pkgs.otpw}/lib/security/pam_otpw.so '' + optionalString cfg.startSession '' session optional ${pkgs.systemd}/lib/security/pam_systemd.so '' + optionalString cfg.forwardXAuth '' session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 '' + optionalString (cfg.limits != []) '' session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits} '' + optionalString (cfg.showMotd && config.users.motd != null) '' session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd} '' + 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) '' session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5 '' + 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) '' session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all '' ); </ins> }; };