Example configurations¶
Three complete /etc/exports configurations, from maximum security to minimum acceptable. Each example includes the full export file, a line-by-line explanation, the findings it mitigates, and the residual risks that remain.
Pick the profile that matches your environment, then use nfswolf analyze to verify the result.
Maximum security¶
This configuration is appropriate for environments handling sensitive data (financial, medical, classified) where NFS access is restricted to a small set of known hosts with Kerberos infrastructure already deployed.
/etc/exports¶
# Maximum security NFS exports
# Requirements: Kerberos KDC, separate filesystem per export, NFSv4 only
/srv/finance gss/krb5p(ro,sync,no_subtree_check,all_squash,anonuid=65534,anongid=65534)
/srv/medical gss/krb5p(ro,sync,no_subtree_check,all_squash,anonuid=65534,anongid=65534)
/srv/shared gss/krb5p(rw,sync,no_subtree_check,root_squash,anonuid=65534,anongid=65534)
/etc/nfs.conf¶
[nfsd]
# Disable NFSv2 and NFSv3 entirely -- only v4 served
vers2 = n
vers3 = n
vers4 = y
vers4.0 = y
vers4.1 = y
vers4.2 = y
# Limit concurrent threads
threads = 8
[exportd]
# No mountd needed for NFSv4-only
manage-gids = y
[mountd]
# Fixed port in case mountd still runs (v4-only should not need it)
port = 20048
Option Explanation¶
| Option | Purpose |
|---|---|
gss/krb5p |
Kerberos privacy: authentication + integrity + encryption. No AUTH_SYS fallback. |
ro |
Read-only. Blocks all write operations. |
sync |
Commits writes to stable storage before replying. |
no_subtree_check |
Safe because each export is its own filesystem root; nothing to escape to. |
all_squash |
Maps every identity to nobody (65534). Used on read-only exports. |
root_squash |
On the rw export, maps UID 0 to nobody while preserving per-user Kerberos identity. |
anonuid=65534,anongid=65534 |
Explicit anonymous mapping target. Defensive default. |
Filesystem Layout¶
# Each export is the root of its own LVM volume
lvcreate -L 50G -n finance vg0 && mkfs.ext4 /dev/vg0/finance
lvcreate -L 50G -n medical vg0 && mkfs.ext4 /dev/vg0/medical
lvcreate -L 20G -n shared vg0 && mkfs.ext4 /dev/vg0/shared
mount /dev/vg0/finance /srv/finance
mount /dev/vg0/medical /srv/medical
mount /dev/vg0/shared /srv/shared
Findings Mitigated¶
F-1.1 (Kerberos replaces AUTH_SYS), F-1.2 (all_squash + Kerberos), F-1.3 (groups from KDC), F-1.5 (replay protection), F-1.6 (v2/v3 disabled), F-1.7 (no AUTH_SYS fallback), F-2.1 (separate filesystems), F-2.6 (no bind mounts), F-3.1 (krb5p encrypts wire), F-4.1 (root_squash + read-only), F-4.2 (read-only prevents upload), F-5.1 (no mountd for v4-only)
Residual Risks¶
What this does NOT protect against
- A compromised Kerberos KDC grants access to all exports
- Users with valid Kerberos tickets can still read files permitted to
nobody(theall_squashtarget) - The
/srv/sharedexport allows authenticated write access; a compromised principal can modify shared files - NFSv4 pseudo-filesystem traversal (F-5.5) may reveal export paths
- Server-side logging of NFS operations remains limited (F-7.6)
Practical security¶
This configuration balances security with operational reality. Kerberos is preferred, but AUTH_SYS is permitted for legacy clients. Network restrictions limit access. Suitable for internal enterprise environments where a full Kerberos migration is in progress.
/etc/exports¶
# Practical security NFS exports
# Kerberos preferred, AUTH_SYS allowed for legacy clients
/srv/engineering 192.168.10.0/24(rw,sync,no_subtree_check,root_squash,sec=krb5p:krb5i:krb5:sys)
/srv/builds 192.168.10.0/24(ro,sync,no_subtree_check,root_squash,sec=krb5p:krb5i:krb5:sys)
/srv/iso 192.168.0.0/16(ro,sync,no_subtree_check,all_squash,sec=sys)
/etc/nfs.conf¶
[nfsd]
# Allow v3 for legacy clients, prefer v4
vers2 = n
vers3 = y
vers4 = y
vers4.1 = y
vers4.2 = y
threads = 16
[mountd]
port = 20048
manage-gids = y
[statd]
port = 32765
[lockd]
port = 32803
udp-port = 32803
Option Explanation¶
| Option | Purpose |
|---|---|
192.168.10.0/24 |
Restricts mounting to the engineering VLAN. |
rw / ro |
Read-write for engineering workspace; read-only for builds and ISOs. |
root_squash |
Default, made explicit. UID 0 maps to nobody. |
sec=krb5p:krb5i:krb5:sys |
Kerberos preferred, AUTH_SYS as last resort for legacy clients. |
all_squash |
On the ISO export, all identities map to nobody. |
sec=sys |
ISO export uses AUTH_SYS only; content is public read-only. |
Filesystem Layout¶
# Separate filesystems prevent export escape
lvcreate -L 100G -n engineering vg0 && mkfs.xfs /dev/vg0/engineering
lvcreate -L 200G -n builds vg0 && mkfs.xfs /dev/vg0/builds
lvcreate -L 50G -n iso vg0 && mkfs.ext4 /dev/vg0/iso
mount /dev/vg0/engineering /srv/engineering
mount /dev/vg0/builds /srv/builds
mount /dev/vg0/iso /srv/iso
Findings Mitigated¶
F-1.1 (partial: Kerberos clients protected, AUTH_SYS clients not), F-2.1 (separate filesystems), F-4.1 (root_squash), F-4.2 (builds/ISO read-only), F-5.1 (mountd firewalled), F-7.1 (subnet restrictions), F-1.6 (v2 disabled)
Residual Risks¶
What this does NOT protect against
- AUTH_SYS clients on the
192.168.10.0/24subnet can spoof any non-root UID (F-1.1) - The engineering export is writable; a compromised host can upload SUID binaries (blocked only by
root_squash, not bynosuidon the client mount) - IP-based restrictions can be bypassed by an attacker who compromises a host on the authorized subnet or spoofs its IP (F-3.3)
- NFSv3 traffic without Kerberos is unencrypted (F-3.1)
- The
sec=krb5p:...:sysordering is a preference hint: the client chooses, and a malicious client always choosessys
Migration path to maximum security
- Audit which clients still use AUTH_SYS: check
nfswolf scan targetoutput for auth flavor negotiation results - Deploy Kerberos keytabs to remaining clients
- Remove
sysfrom thesec=list on each export as clients are migrated - Once all exports are
sec=krb5ponly, disable NFSv3 in/etc/nfs.conf
Minimum acceptable¶
This is the absolute floor for an NFS deployment that is not trivially exploitable. It uses AUTH_SYS only (no Kerberos), but applies every other available control. It is suitable for isolated lab environments, development networks, or legacy systems where Kerberos deployment is not feasible.
AUTH_SYS is fundamentally insecure
Without Kerberos, any host on the authorized network can impersonate any non-root user. This configuration limits the blast radius but cannot prevent identity spoofing. Treat it as a temporary posture, not a final state.
/etc/exports¶
# Minimum acceptable NFS exports
# AUTH_SYS only -- every other control applied
/srv/devdata 10.0.5.0/24(rw,sync,no_subtree_check,root_squash,sec=sys)
/srv/packages 10.0.5.0/24(ro,sync,no_subtree_check,all_squash,sec=sys)
/srv/backups 10.0.5.10(ro,sync,no_subtree_check,root_squash,sec=sys)
/etc/nfs.conf¶
[nfsd]
# Disable v2 -- no legitimate reason to allow it
vers2 = n
vers3 = y
vers4 = y
vers4.1 = y
vers4.2 = y
threads = 8
[mountd]
port = 20048
manage-gids = y
[statd]
port = 32765
[lockd]
port = 32803
udp-port = 32803
Option Explanation¶
| Option | Purpose |
|---|---|
10.0.5.0/24 / 10.0.5.10 |
Subnet restriction for dev; single-host restriction for backups. |
rw / ro |
Only devdata allows writes. Packages and backups are read-only. |
root_squash |
Explicit on all exports. UID 0 maps to nobody. |
all_squash |
Packages export maps all UIDs to nobody. |
sec=sys |
AUTH_SYS: weakest option, only choice without Kerberos. |
Filesystem Layout¶
The key hardening control in this profile is separate filesystems. Without Kerberos, this is the strongest remaining defense because it eliminates the export escape attack.
# Each export on its own filesystem -- THIS IS NON-NEGOTIABLE
lvcreate -L 100G -n devdata vg0 && mkfs.ext4 /dev/vg0/devdata
lvcreate -L 50G -n packages vg0 && mkfs.ext4 /dev/vg0/packages
lvcreate -L 200G -n backups vg0 && mkfs.xfs /dev/vg0/backups
mount /dev/vg0/devdata /srv/devdata
mount /dev/vg0/packages /srv/packages
mount /dev/vg0/backups /srv/backups
Never use bind mounts as a substitute
mount --bind /data/dev /srv/devdata does NOT create filesystem isolation. Bind mounts share the parent filesystem's fsid, so the export escape attack works through them as if the bind mount did not exist (F-2.6).
Firewall Rules¶
# nftables: allow NFS only from authorized subnet, block portmapper externally
nft add rule inet filter input ip saddr 10.0.5.0/24 tcp dport { 2049, 20048, 32765, 32803 } accept
nft add rule inet filter input tcp dport 111 drop
nft add rule inet filter input udp dport 111 drop
Findings Mitigated¶
F-2.1 (separate filesystems), F-2.6 (no bind mounts), F-4.1 (root_squash), F-1.6 (v2 disabled), F-5.4 (portmapper firewalled), F-3.2 (amplification blocked), F-7.1 (subnet/host restrictions), F-4.2 (packages/backups read-only; devdata still writable)
Residual Risks¶
Accepted risks without Kerberos
- UID/GID spoofing is fully possible (F-1.1): any host on
10.0.5.0/24can claim any non-root UID - Auxiliary group injection (F-1.3): clients can claim membership in any group
- Wire sniffing (F-3.1): all data transmitted in cleartext
- IP spoofing (F-3.3): export ACLs rely on source IP, which can be spoofed on the local network
- SUID upload on devdata (F-4.2): writable export with
root_squashstill allows non-root SUID binaries - No audit trail (F-7.6): NFS operations are not logged
Quick wins to improve this posture
- Add
nosuid,nodevto client mount options: prevents SUID/device-node attacks even on writable exports - Deploy NFS over TLS (kernel 6.x+): encrypts the wire without Kerberos
- Move packages and backups to NFSv4-only: eliminates mountd exposure for those exports
- Begin Kerberos deployment: even one export converted to
sec=krb5pis progress
Comparison¶
| Control | Maximum | Practical | Minimum |
|---|---|---|---|
| Authentication | krb5p only |
krb5p preferred, sys fallback |
sys only |
| Wire encryption | Yes (krb5p) | Partial | No |
| Separate filesystems | Yes | Yes | Yes |
| NFSv2/v3 | Disabled | v2 disabled, v3 enabled | v2 disabled, v3 enabled |
| UID spoofing blocked | Yes | Partial | No |
| Export escape blocked | Yes | Yes | Yes |
Verification¶
After applying any configuration, validate it: