Change:
# 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> }; };

Discussion:
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: