The NixOS security.pam module provides config options for Linux PAM (Pluggable Authentication Modules). PAM is how the authentication stack is configured for login and also programs like sudo, passwd, su, etc. With PAM, you can configure 2FA, LDAP/AD, encrypted home directories, automatic keychain unlock, and more.
The PAM infrastructure in NixOS needs some love. I have done some refactoring and I’m thinking about spinning up an RFC for a bigger design evolution. First, I want to hear from you:
What do YOU want the NixOS PAM module to do for you?
Below is my first stab, broken out by user category. Please comment with any corrections or additions!
NixOS Users
Defaults are secure. “I don’t have to think about PAM.”
Enabling a feature (e.g. U2F, LDAP) is as easy as configuring a NixOS service.
Different PAM features “just work” together by default.
NixOS/PAM Power Users
PAM stack can be fine-tuned through NixOS config. (Reorder rules, change arguments, etc.)
Never need to manually write PAM config files.
New PAMs can be added through NixOS config.
Customizing parts of the PAM stack doesn’t require maintaining the whole stack yourself.
NixOS Maintainers
PAM options are defined entirely in their own files (NixOS modules).
Clear guidance exists for how new PAMs should fit into the stack of PAM rules.
Desktop environments and other software don’t need to duplicate the PAM stack.
Insecure or unsuitable defaults are fixed without breaking existing users.
Following a PR that probably looks like what you attempted (sorry I didn’t look at your PR in details): (I realise now that you refer to it in your PR)
Now that I have, I can see that we are thinking similarly about many things! A lot of the changes you made are on my todo list if not already implemented.
So far, I’ve focused on lifting PAM rules into the module system, offering a low-level abstraction for defining rules first. My thinking is that if a user has to use the text escape hatch, then they have to throw away all the benefits of the module system. (Imagine a user using five different security.pam settings happily, only to have to write all their PAM rules manually because one need isn’t met by the modules as defined.) Hence the desire to “never need to manually write PAM config files.”
We also want (and to some extent already have) higher-level abstractions for enabling “features” or “modules” that create many rules. These options can be more opinionated and offer useful defaults, like 2FA modules coming after password login. My hope is that I can defer these higher-level design changes until after reworking the rule generation internals.
It looks like your PR addressed both these needs, and our PRs have fairly minor differences in interface design. One area I’ve neglected that I discovered in testing yesterday is defaults, since it isn’t (AFAIK) possible for a user to extend every value in a config attrset.
Unfortunately their wasn’t much traction to make this happen when I worked on it, but hopefully you’ll have more luck!
My burning question for you is: Where did your PR get stuck? How can I make sure I don’t get similarly stuck? It looks like you did great work and documented it well.
Where did your PR get stuck? How can I make sure I don’t get similarly stuck? It looks like you did great work and documented it well.
I think it’s simply due to its size, and perhaps the lack of “advertising” around it. I think if you manage to make as many small, non-breaking changes as possible (at least to the exposed modules under security.pam like security.pam.yubico.enable) before changing that, you’ll be able to rework the backend and have a proper foundation for moving forward. And with that, try to get as many eyes as you can on the PRs to get traction from core maintainers to make this happen.
I’d also be careful of the timing, no one wants to merge potentially breaking changes before a stable release.
I don’t know if going through the RFC process would be appropriate for those changes, but if you decide to, I’d love to co-author it.
Thanks for your thoughts! I made some changes to #255547 to hopefully make things smoother:
Cleaned up some inconsistencies in how rules are defined up front. That made the rest of the commits more automated and consistent.
Set the new options visible = false and marked them experimental. The idea is to retain the flexibility to rework these options and minimize the impact of getting the design wrong the first time around.
I don’t know yet if an RFC makes sense, but I’d be happy to work with you on one if so. I’ll definitely want to bounce some ideas off you when I get to the user-facing option design regardless.
As for getting more eyes on this work, I’ll start now: this refactor PR is ready for review! If you believe these PAM changes are important, please take a look through the commits and leave a review. The changes are easy on the eyes, I promise.
i feel like more wiki documentation would be nice - right now there is some about u2f/otp on the Yubikey page, while i’m not finding instructions for fido2 (in my case thru a device from Nitrokey).
i don’t super mind whether docs go to the wiki or the official manual, but either way some more clarity on how to use this would be nice.
tests are cool too but i’m not sure they’d be as accessible for newer users as a substitute for documentation.
I was trying to figure out a way to get the nextcloud client to work with autologin on SDDM and an empty kwallet password and couldn’t make it work. I found some instructions on how to edit pam.d files to facilitate this but couldn’t try them out because I don’t know how to change pam.d files in NixOS. So that would be my use case.
Something I’ve been doing lately is trying to make kwallet5 unlock automatically with the encryption password. I succeeded, but as far as I can tell, for stuff like autologin where the actual authentication piece is different, but you want to reuse the “stuff that uses PAM_AUTH_TOK” portion of the main config, that’s seems to not be currently possible without, possibly, setting security.pam.rules.sddm-autologin.enableKwallet and similar at every usage site?
I was at NixCon 2024 and saw many people using YubiKeys, which sparked my interest. I do have one myself, but I have never used it beyond setting 2FA on some websites.
It was cool to see that one can use it to execute the sudo command without a password or even log into a gnome-session.
But setting things up is kind of a PITA… To have a normal functioning fingerprint scanner on my framework, I had to be lucky to stumble upon this comment on a GitHub issue, which is just stupid in a way. I tried replicating it using pure Nix, but I was not quite sure what I was doing since I have basically no knowledge about pam.
Now I wanted to setup the yubikey pam modules too, and there were some conflicts I had to resolve and it seems to work… somehow
But I still face the issue, when the yubikey is plugged in while gdm is starting, I can only use my fingerprint or yubikey, but not my password. Sure enough, why would I bother to type in my password, I can just use two other, more convenient, methods. But with those two, the gnome keyring does not start, and at some point I need to insert my password.
Long story short: for an ordinary user, it is annoying to configure certain things, and it would be cool to have some nice defaults that work together, or examples on how to make them work together nicely.
Hope you don’t mind my bumping a slightly older post, Thanks very much to @Majiir and @rissson for their work on this and to @infinisil for reviewing.
Here are some examples of things that I think people who are not necessarily nix or pam power users might want to be setup. I’m not neceassarily saying that there should be pre-made defaults that can just be enabled to handle these slightly more complex scenarios but rather examples of how to do this would Ideally be documented so people can more easily figure out how to make these sorts of configs. I’ve been trying to work through how I might configure some things like the example cases below as someone quite new to nix and PAM. I read through much of the source code for the module and referenced the PAM sysadmin guide but it is still quite tricky, based solely on the examples in this PR nixos/pam: assemble rules from modular configuration by Majiir · Pull Request #255547 · NixOS/nixpkgs · GitHub, to generalise to what a nix config that achieves a particular example scenario might look like.
Examples of more complex conditional authentications that I can imagine people might want:
say I have a fingerprint sensor and a yubikey I might want to make rules like:
login should require a password and one of the fingerprint sensor or the Yubikey to authenticate
session unlock should only require either the password or the fingerprint sensor
require a second factor (e.g. google authentication OTP module) for a remote login only when it comes from an IP address not is not on a specified list
sudo requires a fingerprint or a yubikey action
su requires a password and either a fingerprint or a yubikey action
related to how to do this are the questions, what are the PAM defaults?
How do I override the defaults?
what would a config that recapitulates the defaults look like?
I haven’t had the time to figure out quite how to do this but if anyone knows of or could write a couple more complex examples that might help me grok this I’d be happy to try and write up some documentation aiming to be accessible to the non PAM+Nix combined power-user for the wiki or wherever else is suitable.
You are poking at a few of the big problems with how PAM works right now in NixOS:
There are multiple interfaces used to define PAM rules. Most rules are defined in pam.nix using the hidden rules option, but some modules set the older text option. Setting text unsets all the other rules for that service, even ones that are explicitly configured by a user (because text only defaults to the rendering of rules). Using text also bypasses sanity checks and breaks anything that inspects the rules directly, like the AppArmor integration.
The default rules imply a default ordering. There is a variety of NixOS modules under security.pam.services.<name>.*, most of them disabled by default. For example, security.pam.services.<name>.rssh, where <name> could be a PAM service like sudo or login. Enabling one of these modules in turn enables a set of PAM rules for that service. But the ordering of those different rules is hard-coded in pam.nix, and it really should be configurable by the user. (It’s technically adjustable using the hidden order option, but this isn’t yet an ergonomic interface.)
The module structure is inconsistent. For example, you must enable rssh per-service, but security.pam.krb5 is a global module that applies to all PAM services without flexibility. Per-service sounds nice, but rssh can only be enabled per-service, and its settings are global. But then, ttyAudit can be enabled and configured per-service. That might sound like the best option, but if you want to configure such a rule for all PAM services by default, … you can’t. You have to know the list of services you want to use. So none of these options is ideal, and there’s no obvious reasons for the current structure.
All of that can be configured by other NixOS modules. So your PAM rules will vary depending on what display managers and services you have enabled, even if you never touch security.pam options. This is a good thing, and it’s reflective of the general power and convenience of the NixOS module system; but it makes it difficult to understand what “PAM defaults” even means.
There are some rules that are enabled for services (as long as they don’t set text) by default. For example, there is always an account, password and session rule for pam_unix, for every service. That’s a default (and it can’t even be turned off without using hidden options) so we can’t even look at point (4) and say “everything is configured through modules” because some rules are just hardcoded.
That isn’t an exhaustive list of challenges in security.pam, just a taste!
This is a great offer, thank you.
In the current state, almost any set of PAM rules is achievable. The question is how much can be done with well-crafted NixOS modules, and how much do you have to build yourself using the hidden rules option. That option is hidden for reasons (it’s an unstable interface, backward compatibility won’t be maintained while it’s hidden, there are some remaining design issues especially around order, etc.) so some users may prefer to stay away from it entirely.
Some of the scenarios you described could benefit from include/substack rules. But that would be awkward to configure right now because defining a new PAM service would pull in all the default rules.
The current state of security.pam might not be ready for documentation for the general user base.