Re: [RFC] NFS: named client identities for mTLS mounts and a per-namespace .nfs keyring
Hannes Reinecke <[email protected]> Wed, 3 Jun 2026 03:39:29 +0200
| Newsgroups | dev.linux.lists.kernel-tls-handshake,org.kernel.vger.keyrings,org.kernel.vger.linux-nfs,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
On 6/2/26 17:47, Chuck Lever wrote: > Today, exactly one x.509 certificate and private key pair can be > used at a time for all NFS mounts. The location of that pair is > set in /etc/tlshd/config. > > We currently have an awkward experimental mechanism for specifying > an alternative x.509 certificate and private key for an xprtsec=mtls > NFS mount, but it needs to be completed so it can be documented and > advertised for use. > > I asked Claude to write a rough draft of a design document that > outlines what needs to be done to finish the work. I would like > input on the kernel-side mechanism in particular for the > per-network-namespace keyring and the way userspace reaches it. > > > Problem > ======= > > NFS mutual-TLS mounts (xprtsec=mtls) need the client to present an > x.509 certificate and prove possession of its private key. The > handshake runs in userspace in tlshd; the kernel hands tlshd the > credentials by keyring serial number over the handshake genetlink > upcall. > > The only front end today is two undocumented integer mount options: > > mount -o xprtsec=mtls,cert_serial=723847,privkey_serial=723848 \ > server:/export /mnt > > The administrator must load the cert and key into the keyring out of > band, discover the integer serials, and paste them onto the command > line. Serials are opaque, non-reproducible across boots, and easy to > transpose. There is also no isolation: nfs_tls_key_verify() does a > global key_lookup() on the serial, and the .nfs keyring created in > fs/nfs/inode.c is module-global and never referenced again -- any tlshd > that learns a serial can read the key. > > This RFC proposes a named, per-mount client-identity interface backed > by a provisioning CLI, and fixes the keyring to isolate credentials per > network namespace. The kernel handshake ABI (integer serials over > genetlink) does not change. > > > The cross-subsystem ask: a per-netns .nfs keyring > ================================================= > > Network namespace is the correct isolation domain. tlshd is bound to a > network namespace, not a user namespace: it services sockets passed up > from the kernel over the per-netns handshake genetlink socket, and one > tlshd runs per network namespace that needs TLS-protected mounts. > > - Replace the dead module-global .nfs keyring with one keyring per > network namespace, held in struct nfs_net (fs/nfs/netns.h) and > allocated at nfs_net_init(). The keys subsystem otherwise > namespaces on user_namespace, so this is a kernel-held object > referenced from nfs_net (like today's global keyring, but one per > netns). The DNS resolver's per-netns key scoping (net->key_domain, > request_key_net()) is precedent that netns-scoped key handling is > acceptable. > > - tlshd attaches at handshake time, not at launch. This matters: the > keyring may be empty or freshly created when tlshd starts, so > linking it by name at startup is the wrong model. Instead NFS sets > ta_keyring to the netns .nfs keyring serial in > xs_tls_handshake_sync(), the kernel sends it as > HANDSHAKE_A_ACCEPT_KEYRING, and tlshd links that serial into its > session keyring per handshake -- the path tlshd already implements. > Linking grants tlshd possession of the keyring and, through it, of > the possessor-scoped cert and privkey keys. > > - Credential keys are created possessor-readable only (no > KEY_USR_READ). That is what makes isolation enforceable rather > than advisory: a key provisioned in namespace A is absent from B's > keyring and unreadable by B's tlshd even if its serial leaks. > > Open question, and where I most want input: userspace -- the > provisioning CLI and mount.nfs -- needs to name the kernel-held netns > keyring in order to add and search keys. Candidates, modeled on > KEYCTL_GET_PERSISTENT (security/keys/persistent.c): > > (a) a new keyctl command that links the caller's netns .nfs keyring > into a destination keyring and returns its serial; > (b) an NFS-specific request_key key type the module instantiates to > point at the netns keyring; > (c) a per-netns serial exported via procfs or netlink. > > The per-netns keyring decision itself I consider settled; the retrieval > primitive is the open one. There is also a user_namespace accounting > nuance: keys added by userspace are quota-charged against a key_user > keyed by user_namespace even though the keyring lives in nfs_net. I > would like the keyrings folks to confirm the quota and ownership > interaction is sane when the user_ns and net_ns boundaries do not > coincide. > I am all for making keyrings namespace-aware. Logically I _think_ they should be tagged per user-namespace, as this really is about the filesystem (and as such would warrant to be tagged per mount ns). Tagging it per net-namespace is not a great fit (well, for me, at least), as also block devices might require keys to present the bdev (eg nvme authentication) I might be okay to have it tagged per net-namespace, though, as all current users are in some shape or form being network related. But I'm not sure if that stays that way, so I am worried if we're not restricting ourselves to much by that choice. As really, the question is: what is the driving the namespace selection? Is it the _requesting_ layer, ie the layer issuing the mount() call? Or is it the _providing_ layer, ie the layer providing the devices/interfaces where the mount() call is operating on? If it's the former, then we need to tag is as net-namespace. If it's the latter, then we need to tag it as a user-namespace / mount-ns. We should probably ask Christian ... Cheers, Hannes -- Dr. Hannes Reinecke Kernel Storage Architect [email protected] +49 911 74053 688 SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich