| |

LFCE 67 ๐Ÿง NFS Server โ€” Exports and Security

โฑ๏ธ Estimated read: 13 min | |

The previous chapter configured a basic NFS export. This chapter covers the options that control who can access the share, what they can do with it, and how the server maps their identity. The /etc/exports syntax is deceptively simple, and a single space in the wrong place changes the meaning of the line. The security optionsโ€”root_squash, all_squash, secure, and the Kerberos modesโ€”determine whether a client’s root user has root access on the server, whether the anonymous mapping applies to every user, and whether the traffic is authenticated and encrypted.

The LFCE exam objectives require configuring a system to provide network shares, and the security dimension is inseparable from the configuration. An export that grants no_root_squash to the world is a privilege-escalation vector. An export that relies on IP addresses alone is vulnerable to IP spoofing. The options in this chapter are the controls that determine whether the share is a convenience or a liability.

Key point: The root_squash option is the default and maps the client’s UID 0 to the anonymous user. The no_root_squash option disables this mapping and allows a remote root user to change any file on the share, which is a significant security risk and should be limited to trusted clients . The all_squash option maps every client user to the anonymous UID, which is appropriate for public shares where the server does not need to preserve the client’s identity . The secure option requires the client to use a reserved port (below 1024), which is the default; the insecure option allows ports above 1024, which is sometimes necessary for containerized clients .


Why export options require explicit security decisions

The root-squash problem.

NFS identifies users by UID and GID, not by username. Without root_squash, a client’s root user (UID 0) is treated as root on the server, and can read, modify, or delete any file on the exported filesystem. The root_squash option maps UID 0 to the anonymous user (typically nobody or nfsnobody), which prevents this escalation . The option is the default, but it can be disabled with no_root_squash, and a configuration that disables it for a broad client range is a serious misconfiguration. The Red Hat documentation explicitly warns that no_root_squash allows a remote root user to change any file and leave trojaned applications for other users .

The all-squash problem.

The root_squash option only affects UID 0. A client user with UID 1000 on the client is still UID 1000 on the server, and if that UID exists on the server, the client user has access to the server user’s files. The all_squash option maps every client user, regardless of UID, to the anonymous user. This is the correct choice for a public share where the server does not need to distinguish between client users . The anonuid and anongid options specify the UID and GID that the anonymous user should have, which is useful when the server’s nobody user has a different UID than the client expects.

The secure-port problem.

The secure option requires the client to originate the NFS request from a reserved port (below 1024). On a traditional Unix system, only root can bind to a reserved port, so the option was intended as a weak authentication mechanism. However, the Red Hat documentation notes that on the network, anyone can easily become root on a client machine, so the assumption that traffic on reserved ports is privileged is not safe . The option has limited value, and the better controls are Kerberos, firewall rules, and restricting the export to specific clients. The insecure option allows the client to use a port above 1024, which is sometimes required for containerized clients that do not have the CAP_NET_BIND_SERVICE capability .


a. The identity-mapping options

The identity-mapping options control how the server translates the client’s UID and GID into the server’s UID and GID. The default is root_squash with no_all_squash.

# /etc/exports
# Default: root_squash, no_all_squash
/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check)

# Disable root squashing for a trusted diskless client
/srv/nfs/diskless 192.168.1.50(rw,sync,no_root_squash)

# Map all users to the anonymous UID
/srv/nfs/public 192.168.1.0/24(rw,sync,all_squash,anonuid=65534,anongid=65534)

The first line uses the defaults: the client’s root is mapped to nobody, and other users retain their UIDs. The second line disables root squashing for a single trusted host, which is appropriate for a diskless workstation that needs to write files as root. The third line maps every client user to UID 65534 (the nobody user on many systems), which is appropriate for a public share where the server does not care about the client’s identity .

The anonuid and anongid options specify the UID and GID that the anonymous user should have. The nobody user’s UID varies between distributions (65534 on Debian and RHEL, 99 on some older systems), and the anonuid option makes the mapping explicit.


b. The access-control options

The access-control options determine whether the client is allowed to mount the share and whether the share is read-only or read-write. The ro and rw options are the primary controls, and the secure and insecure options control the port requirement.

# Read-only export
/srv/nfs/readonly 192.168.1.0/24(ro,sync)

# Read-write export with insecure ports for containerized clients
/srv/nfs/containers 192.168.1.0/24(rw,sync,insecure,no_subtree_check)

# Restrict to a single host
/srv/nfs/private 192.168.1.50(rw,sync,root_squash)

The ro option prevents the client from writing to the share, which is the correct setting for a software repository or a documentation directory. The rw option allows writing, and the Red Hat documentation warns that it should be used carefully because it increases the attack surface . The insecure option is sometimes necessary for containerized clients, but it should be limited to the specific client range that requires it.

The no_subtree_check option disables the subtree check, which is a performance optimization. The subtree check verifies that the requested file is within the exported directory, and it is the default in older NFS versions. The no_subtree_check option is safe when the entire directory is exported and is the recommended setting for most configurations .


c. Kerberos security modes

The sec= option selects the security flavor for the export. The default is sec=sys, which uses local UNIX UIDs and GIDs and provides no authentication or encryption. The Kerberos modes provide authentication and, optionally, integrity checking and encryption.

# Kerberos authentication only
/srv/nfs/secure 192.168.1.0/24(rw,sync,sec=krb5)

# Kerberos authentication and integrity
/srv/nfs/secure 192.168.1.0/24(rw,sync,sec=krb5i)

# Kerberos authentication, integrity, and encryption
/srv/nfs/secure 192.168.1.0/24(rw,sync,sec=krb5p)

The sec=krb5 mode authenticates the user with Kerberos but does not encrypt the traffic or protect against modification. The sec=krb5i mode adds integrity checking, which prevents tampering. The sec=krb5p mode adds encryption, which protects the data from eavesdropping . The krb5p mode is the most secure but has a performance cost, and it requires a Kerberos infrastructure with a keytab on the server and tickets on the client.

The Kerberos configuration is complex and requires a KDC, a service principal for the NFS server, and the rpc.gssd daemon on the client . For a small network without Kerberos, the sec=sys default is the only option, and the security depends on the firewall rules and the export restrictions rather than on cryptographic authentication.


Complete Example Session

# ============================================
# PART 1: CREATE THE SHARED DIRECTORIES
# ============================================
sudo mkdir -p /srv/nfs/shared /srv/nfs/public /srv/nfs/readonly
sudo chown nobody:nogroup /srv/nfs/shared /srv/nfs/public
sudo chmod 777 /srv/nfs/shared /srv/nfs/public
sudo chmod 755 /srv/nfs/readonly

# ============================================
# PART 2: CONFIGURE THE EXPORTS WITH SECURITY OPTIONS
# ============================================
sudo tee /etc/exports > /dev/null <<'EOF'
# Read-write with default root_squash
/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check)

# Public share: all users mapped to anonymous
/srv/nfs/public 192.168.1.0/24(rw,sync,all_squash,anonuid=65534,anongid=65534,no_subtree_check)

# Read-only share
/srv/nfs/readonly 192.168.1.0/24(ro,sync,no_subtree_check)

# Trusted diskless client: no root_squash
/srv/nfs/diskless 192.168.1.50(rw,sync,no_root_squash,no_subtree_check)
EOF

# ============================================
# PART 3: APPLY THE EXPORTS
# ============================================
sudo exportfs -ra
sudo exportfs -v

# ============================================
# PART 4: VERIFY THE EXPORTS
# ============================================
sudo showmount -e localhost
# Output lists all four exports

# ============================================
# PART 5: CHECK THE DEFAULT SQUASH BEHAVIOR
# ============================================
# The shared export uses root_squash: a client root write is mapped to nobody
# The public export uses all_squash: all client users are mapped to nobody

# ============================================
# PART 6: TEST WITH A CLIENT
# ============================================
# On the client:
# sudo mount -t nfs 192.168.1.10:/srv/nfs/shared /mnt/shared
# sudo touch /mnt/shared/root-file
# ls -ln /mnt/shared/root-file
# Output: the file is owned by the nobody UID, not root

# ============================================
# PART 7: TEST THE PUBLIC SHARE
# ============================================
# On the client:
# sudo mount -t nfs 192.168.1.10:/srv/nfs/public /mnt/public
# sudo -u someuser touch /mnt/public/user-file
# ls -ln /mnt/public/user-file
# Output: the file is owned by the anonymous UID, not someuser

# ============================================
# PART 8: RESTRICT THE FIREWALL
# ============================================
# Open only NFSv4 port 2049 to the storage subnet
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="2049" protocol="tcp" accept'
sudo firewall-cmd --reload

# ============================================
# PART 9: VERIFY THE FIREWALL
# ============================================
sudo firewall-cmd --list-rich-rules
# Output shows the rule that allows 2049 from the subnet

# ============================================
# PART 10: CLEAN UP
# ============================================
# On the client: sudo umount /mnt/shared /mnt/public
# On the server: sudo exportfs -ua

The ten parts demonstrate the exports and security configuration: the directories, the export options, the application, the verification, the squash behavior, the client tests, the firewall restriction, and the cleanup. The critical verification is Part 6: the file created by the client’s root is owned by nobody on the server, which confirms that root_squash is working.


Quick Reference

Identity-Mapping Options

OptionBehavior
root_squashMap client UID 0 to anonymous (default)
no_root_squashAllow client UID 0 to be root
all_squashMap all client users to anonymous
no_all_squashPreserve client UIDs (default)
anonuid=NSet the anonymous UID
anongid=NSet the anonymous GID

Access-Control Options

OptionBehavior
roRead-only
rwRead-write
secureRequire reserved ports (default)
insecureAllow ports above 1024
no_subtree_checkDisable subtree checking

Security Modes

ModeAuthenticationIntegrityEncryption
sec=sysUID/GID onlyNoNo
sec=krb5KerberosNoNo
sec=krb5iKerberosYesNo
sec=krb5pKerberosYesYes

Firewall Ports

VersionPorts
NFSv4TCP 2049
NFSv3111, 2049, mountd, statd, lockd

Best Practices

โœ… Do This:

# Keep root_squash for general shares
/srv/nfs/shared 192.168.1.0/24(rw,sync,root_squash)           # โœ…

# Use all_squash for public shares
/srv/nfs/public 192.168.1.0/24(rw,sync,all_squash)            # โœ…

# Restrict to the specific client subnet
/srv/nfs/shared 192.168.1.0/24(rw,sync)                       # โœ…

# Use no_subtree_check for exported directories
/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check)      # โœ…

# Open only the required firewall ports
firewall-cmd --add-service=nfs                                 # โœ…

# Use Kerberos for sensitive data
/srv/nfs/secure 192.168.1.0/24(rw,sync,sec=krb5p)             # โœ…

โŒ Don’t Do This:

# Allow no_root_squash for the world
/srv/nfs *(rw,sync,no_root_squash)    # Privilege escalation   # โŒ

# Use a space between the client and the options
192.168.1.0/24 (rw)    # Applies to a different client       # โŒ

# Export to any client without restriction
/srv/nfs *(rw,sync)    # No access control                    # โŒ

# Rely on secure ports alone for security
# The reserved-port assumption is not safe                  # โŒ

# Expose port 2049 to the internet
# NFS is not designed for public exposure                    # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Client root has root accessno_root_squash in the exportRemove it; use root_squash
All files owned by nobodyall_squash appliedRemove it if UID preservation is needed
Client cannot mountFirewall blocks 2049Open the NFS service
Write fails despite rwServer directory permissionschmod and chown the directory
Export not visibleexportfs not runexportfs -ra
Space in exports lineTypoclient(rw), not client (rw)

Real-World Examples

1. Default export

/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check)

2. Public share

/srv/nfs/public 192.168.1.0/24(rw,sync,all_squash,anonuid=65534,anongid=65534)

3. Trusted client

/srv/nfs/diskless 192.168.1.50(rw,sync,no_root_squash)

4. Read-only share

/srv/nfs/readonly 192.168.1.0/24(ro,sync)

5. Insecure for containers

/srv/nfs/containers 192.168.1.0/24(rw,sync,insecure)

6. Kerberos authentication

/srv/nfs/secure 192.168.1.0/24(rw,sync,sec=krb5)

7. Kerberos encryption

/srv/nfs/secure 192.168.1.0/24(rw,sync,sec=krb5p)

8. Apply exports

exportfs -ra

9. Verify exports

showmount -e localhost

10. Restrict firewall

firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="2049" protocol="tcp" accept'

Visual

Identity Mapping

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THE SQUASH OPTIONS CONTROL UID MAPPING                      โ”‚
โ”‚                                                              โ”‚
โ”‚  Client user (UID 0) writes a file                           โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€ root_squash (default)                              โ”‚
โ”‚       โ”‚     โ””โ”€โ”€ File owned by nobody on the server           โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ””โ”€โ”€ no_root_squash                                     โ”‚
โ”‚             โ””โ”€โ”€ File owned by root on the server             โ”‚
โ”‚                                                              โ”‚
โ”‚  Client user (UID 1000) writes a file                        โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€ no_all_squash (default)                            โ”‚
โ”‚       โ”‚     โ””โ”€โ”€ File owned by UID 1000 on the server         โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ””โ”€โ”€ all_squash                                         โ”‚
โ”‚             โ””โ”€โ”€ File owned by nobody on the server           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Exports Syntax Trap

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  A SPACE CHANGES THE MEANING                                 โ”‚
โ”‚                                                              โ”‚
โ”‚  /srv/nfs 192.168.1.0/24(rw)                                 โ”‚
โ”‚  โ””โ”€โ”€ Client gets rw access                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  /srv/nfs 192.168.1.0/24 (rw)                                โ”‚
โ”‚  โ””โ”€โ”€ Client gets default (ro) access                         โ”‚
โ”‚  โ””โ”€โ”€ A second client named "(rw)" gets default access        โ”‚
โ”‚                                                              โ”‚
โ”‚  No space between the client and the opening parenthesis.    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Kerberos Security Modes

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THREE LEVELS OF KERBEROS PROTECTION                         โ”‚
โ”‚                                                              โ”‚
โ”‚  sec=krb5:                                                   โ”‚
โ”‚  โ””โ”€โ”€ Authentication only                                     โ”‚
โ”‚  โ””โ”€โ”€ Traffic is not encrypted                                โ”‚
โ”‚  โ””โ”€โ”€ Tampering is not detected                               โ”‚
โ”‚                                                              โ”‚
โ”‚  sec=krb5i:                                                  โ”‚
โ”‚  โ””โ”€โ”€ Authentication + integrity                              โ”‚
โ”‚  โ””โ”€โ”€ Tampering is detected                                   โ”‚
โ”‚  โ””โ”€โ”€ Traffic is not encrypted                                โ”‚
โ”‚                                                              โ”‚
โ”‚  sec=krb5p:                                                  โ”‚
โ”‚  โ””โ”€โ”€ Authentication + integrity + encryption                 โ”‚
โ”‚  โ””โ”€โ”€ Traffic is encrypted                                    โ”‚
โ”‚  โ””โ”€โ”€ Highest security, highest overhead                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Default root mappingroot_squash
Disable root mappingno_root_squash (risky)
Map all usersall_squash
Anonymous UIDanonuid=N
Read-onlyro
Read-writerw
Reserved portssecure (default)
Allow high portsinsecure
No subtree checkno_subtree_check
Kerberos authsec=krb5
Kerberos auth + integritysec=krb5i
Kerberos auth + encryptionsec=krb5p
NFSv4 firewall portTCP 2049

Key takeaways:

  • root_squash is the default and the safe choice. It maps the client’s UID 0 to the anonymous user, preventing a remote root from having root access on the server. no_root_squash should be limited to trusted clients, because it allows a remote root user to change any file .
  • all_squash maps every client user to anonymous. It is appropriate for public shares where the server does not need to distinguish between client users. The anonuid and anongid options set the anonymous identity .
  • The secure option requires reserved ports. It is the default, but its security value is limited because anyone can become root on a client. The insecure option allows ports above 1024, which is sometimes necessary for containerized clients .
  • The exports syntax has no space between the client and the options. client(rw) applies the options to the client. client (rw) applies the default options to the client and exports to a different client named (rw) .
  • Kerberos provides authentication and encryption. The sec=krb5 mode authenticates, sec=krb5i adds integrity, and sec=krb5p adds encryption. The default sec=sys provides none of these .
  • The firewall should open only the required ports. NFSv4 uses TCP 2049. NFSv3 requires the portmapper and the mountd, statd, and lockd ports, which should be pinned to static values in /etc/nfs.conf .

Remember: The /etc/exports file is the access-control layer. The root_squash and all_squash options determine the identity mapping. The ro and rw options determine the write access. The secure and insecure options determine the port requirement. The sec= option determines the authentication and encryption. The client specification determines who can mount the share. A single space in the syntax changes the meaning of the line. The default root_squash should be kept, the no_root_squash should be limited, and the firewall should open only the ports that the NFS version requires.


Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!