37.4 NFS Security: Host-Based Access and Kerberos
Right, let’s talk about securing NFS. The default setup, which relies on hostnames and UID matching, is what I like to call “optimistically insecure.” It trusts the client to tell it who a user is. This is like a nightclub where the bouncer just asks, “You’re on the list, right?” and takes your word for it. It’s fine for a homogenous, trusted network (like a 1995 lab), but in the modern world, it’s a gaping hole. We’re going to fix that by moving from this “host-based” trust to actual user authentication with Kerberos.
The Flaw in the Original Plan: rpcsec_gss and Why UIDs Lie
The classic NFSv3 security model is sys, which uses the client’s report of its user’s UID and GID. The server then blindly trusts these identifiers. The problem is trivial to exploit: root on any client can use sudo -u #1000 to become whoever they want. The server has no way of knowing if the UID 1000 on the client is actually the same human as UID 1000 on the server. This is the core weakness.
The solution is the rpcsec_gss security flavor introduced with NFSv4. Instead of trusting a number from the client, it uses the Kerberos protocol to have the user (or service) cryptographically prove their identity to the server. The server says, “Okay, prove you are joe@EXAMPLE.COM,” and the client provides a Kerberos ticket that only the real Joe could have obtained. Now we’re authenticating the principal, not just a number.
Setting Up the Krb5 Dance Floor (The Prerequisites)
Before a single NFS mount is made, you need a functioning Kerberos realm. I’m assuming you have a KDC (Key Distribution Center) running—something like FreeIPA or a vanilla MIT Kerberos setup. Both your NFS server and all clients must be joined to this realm and have valid keytab files.
First, ensure your krb5.conf is consistent and correct across all machines. An inconsistency here will cause baffling failures that make you question your life choices.
# /etc/krb5.conf on both server and client
[libdefaults]
default_realm = EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = true
# Using modern encryption is a very good idea
permitted_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
[realms]
EXAMPLE.COM = {
kdc = kdc.example.com
admin_server = kdc.example.com
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
Next, you need principals for your machines and your NFS service. The service principal is crucial and must follow the format nfs/hostname.example.com@REALM. The hostname must be the FQDN that the client uses to connect to the server.
# On the KDC, create the server principal
kadmin: addprinc -randkey nfs/fileserver.example.com
# On the NFS server itself, extract the key to a keytab
root@fileserver:~# apt-get install krb5-user # or your distro's equivalent
root@fileserver:~# kadmin -p admin/admin -q "ktadd nfs/fileserver.example.com"
This creates /etc/krb5.keytab on the server with the key for its NFS service. Do the same for the client’s host principal (host/client.example.com), which is used for the RPCSEC_GSS context establishment.
Server-Side Configuration: /etc/exports with sec=krb5
Now for the magic. In your /etc/exports file, you replace the sec=sys option with a Kerberos security flavor. The main ones are:
sec=krb5: Authenticates the client user’s identity (this is the baseline).sec=krb5i: Adds integrity protection via cryptographic checksums, preventing data tampering in transit.sec=krb5p: Adds privacy protection via encryption. This is the whole enchilada.
You should almost always use at least krb5i. The performance hit for integrity is negligible for most uses, and the security benefit is massive. Use krb5p if you’re mounting over an untrusted network.
# /etc/exports on the server
/export/shared *.example.com(sec=krb5i,rw,sync,no_subtree_check)
After editing /etc/exports, export the shares:
root@fileserver:~# exportfs -rva
Client-Side Mounting: Actually Using the Security
The client must have a valid Kerberos ticket for the user doing the mounting. The mount command is the same, but the options specify the security flavor.
# First, get a ticket for the user
you@client:~$ kinit joe
Password for joe@EXAMPLE.COM:
# Now mount with sec=krb5i
you@client:~$ sudo mount -t nfs4 -o sec=krb5i fileserver.example.com:/export/shared /mnt/nfs
# Verify the mount used the correct security flavor
you@client:~$ mount | grep nfs
fileserver.example.com:/export/shared on /mnt/nfs type nfs4 (rw,relatime,vers=4.2,sec=krb5i,...)
If you get an “Access denied” error, the first place to look is the server’s kernel log (dmesg or /var/log/kern.log). It’s usually brutally honest about what went wrong—often a missing keytab, a mismatched service principal name, or a clock skew greater than 5 minutes (another classic Kerberos pitfall).
The Gotchas: Because Nothing is Ever Simple
- Name Mapping: Even with Kerberos, the server still needs to map the principal
joe@EXAMPLE.COMto a local UID. This is whereidmapdcomes in. Ensure/etc/idmapd.confis configured with your domain (Domain = example.com) on both server and client. If the mapping fails, you’ll end up with “nobody” as the owner. - Firewalls:
rpcsec_gssuses a dynamic port. You must open thesunrpcport (111) forrpcbindand thenfsport (2049). Locking it down further requires pinningrpc.gssdto a specific port, which is a pain. - The
rootSquabble: By default, Kerberized NFS treats therootuser as nobody special, which is a security feature. If you absolutely need root access, you must export the share with theno_root_squashoption and the client’s root user must have a valid Kerberos ticket for the server’s root principal. This is generally a terrible idea. - Debugging: When it breaks, run
rpcdebug -m nfs -s allon both ends to turn on verbose logging. Also, runexportfs -vto see what the server thinks it’s exporting.
The shift to Kerberos is a bit of a headache to set up, I won’t lie. But once it’s running, you have a file-sharing system that doesn’t just trust the network—it verifies every user and can protect every byte of data. It’s what moves NFS from a lab curiosity to a enterprise-ready filesystem.