Skip to content

Firewall rules

NFS is not a single port. A default NFSv3 deployment uses at least five services across dynamically assigned ports, making firewall configuration error-prone. This page documents every port NFS needs, how to pin the dynamic ones, and complete firewall rulesets for both nftables and iptables. It also explains why firewalling alone cannot secure NFS.

For kernel-level tunables, see Kernel parameters. For export-level hardening, see Export options reference.

The port problem

NFSv3 depends on multiple RPC services, each registering a random port with the portmapper at startup. Without explicit configuration, mountd might listen on port 43721 one boot and 51893 the next. Firewalling "the NFS port" means nothing when half the attack surface is on unpredictable ports.

Service Program Default Port Dynamic? Purpose
Portmapper / rpcbind 100000 111 (TCP+UDP) No Service discovery: maps program numbers to ports (F-5.4)
NFS 100003 2049 (TCP+UDP) No File operations (READ, WRITE, LOOKUP, etc.)
mountd 100005 random Yes Export listing, mount handle distribution (F-5.1)
NLM (lockd) 100021 random Yes File locking (NFSv3 only)
statd (NSM) 100024 random Yes Lock recovery after crashes
rquotad 100011 random Yes Disk quota queries (F-5.15)

An attacker only needs to reach portmapper on port 111 to discover every other port. Even without the portmapper, sequential port scanning finds the services quickly.

Step 1: pin dynamic ports

Before writing firewall rules, assign fixed ports to every dynamic service. Edit /etc/nfs.conf:

[mountd]
port = 20048

[lockd]
port = 32803

[statd]
port = 32765
outgoing-port = 32766

For lockd, also set kernel module parameters (some distributions ignore nfs.conf for lockd):

# /etc/modprobe.d/lockd.conf
options lockd nlm_udpport=32803 nlm_tcpport=32803

Restart NFS services after changing these:

systemctl restart nfs-server rpc-statd

Verify with rpcinfo -p localhost; every service should show its assigned port.

rquotad

rquotad is not always controlled by /etc/nfs.conf. On RHEL/CentOS, set RPCRQUOTADOPTS="-p 32769" in /etc/sysconfig/rpc-rquotad. On Debian/Ubuntu, use /etc/default/quota with RPCRQUOTADOPTS="-p 32769". If you do not need quota reporting over NFS, disable rpc-rquotad.service entirely, since it leaks per-UID disk usage without authentication.

Step 2: firewall rules

With ports pinned, the firewall configuration is straightforward. Allow the fixed set of ports from trusted client networks only.

#!/usr/sbin/nft -f

table inet nfs_filter {
    set trusted_clients {
        type ipv4_addr
        flags interval
        elements = { 10.0.1.0/24, 10.0.2.0/24 }
    }

    chain input {
        type filter hook input priority 0; policy drop;

        # Loopback
        iif "lo" accept

        # Established connections
        ct state established,related accept

        # NFS services — TCP only (UDP disabled in nfs.conf)
        ip saddr @trusted_clients tcp dport {
            111,     # portmapper
            2049,    # nfsd
            20048,   # mountd
            32803,   # lockd
            32765    # statd
        } accept

        # Log and drop everything else
        log prefix "nfs-dropped: " counter drop
    }
}
#!/usr/bin/env bash
set -euo pipefail

TRUSTED="10.0.1.0/24,10.0.2.0/24"

# Flush existing NFS rules
iptables -D INPUT -j NFS_FILTER 2>/dev/null || true
iptables -F NFS_FILTER 2>/dev/null || true
iptables -X NFS_FILTER 2>/dev/null || true

# Create NFS chain
iptables -N NFS_FILTER

# Allow from trusted clients only — TCP
for PORT in 111 2049 20048 32803 32765; do
    iptables -A NFS_FILTER -s "${TRUSTED}" -p tcp --dport "${PORT}" -j ACCEPT
done

# Drop all other NFS-related traffic
for PORT in 111 2049 20048 32803 32765; do
    iptables -A NFS_FILTER -p tcp --dport "${PORT}" -j DROP
    iptables -A NFS_FILTER -p udp --dport "${PORT}" -j DROP
done

# Insert into INPUT chain
iptables -I INPUT -j NFS_FILTER

Disable UDP

The examples above only allow TCP. UDP NFS is susceptible to source-address spoofing and amplification attacks (F-3.2). Disable UDP in /etc/nfs.conf with udp = n under [nfsd] and omit UDP dport rules entirely.

NFSv4-only simplification

NFSv4 eliminates the MOUNT protocol, the portmapper dependency, and the NLM lock manager. An NFSv4-only server needs exactly one port:

graph LR
    C["NFS Client"] -->|"TCP 2049"| S["NFS Server"]
    style C fill:#1a1a2e,stroke:#e94560,color:#fff
    style S fill:#1a1a2e,stroke:#e94560,color:#fff
Port Service Needed?
111 portmapper No. NFSv4 connects directly to 2049.
2049 nfsd Yes
20048 mountd No. NFSv4 uses PUTROOTFH + LOOKUP.
32803 lockd No. NFSv4 has built-in locking.
32765 statd No. NFSv4 handles state recovery internally.

The firewall for an NFSv4-only server is minimal:

table inet nfs_filter {
    set trusted_clients {
        type ipv4_addr
        flags interval
        elements = { 10.0.1.0/24 }
    }

    chain input {
        type filter hook input priority 0; policy drop;
        iif "lo" accept
        ct state established,related accept
        ip saddr @trusted_clients tcp dport 2049 accept
        log prefix "nfs-dropped: " counter drop
    }
}
iptables -A INPUT -s 10.0.1.0/24 -p tcp --dport 2049 -j ACCEPT
iptables -A INPUT -p tcp --dport 2049 -j DROP
iptables -A INPUT -p udp --dport 2049 -j DROP

Disable v2/v3 at the kernel level too

Firewalling port 111 prevents clients from discovering mountd, but does not prevent direct connections to port 2049 using NFSv3. Always disable NFSv2/v3 in /etc/nfs.conf alongside firewall rules; see Kernel parameters for the version control settings.

Blocking the portmapper

The portmapper on port 111 is the primary reconnaissance target. A single DUMP call returns every registered RPC service, port, version, and protocol: the complete service topology of the server (F-5.4). On UDP, the same call enables amplification attacks with a ~28x ratio (F-3.2).

For NFSv4-only servers, block port 111 entirely. For mixed v3/v4 environments where clients need mountd discovery, restrict port 111 to the trusted client set.

Never expose portmapper to the internet

An internet-facing portmapper is exploitable for DDoS amplification, full service enumeration, and NIS credential extraction (F-5.3). There is no legitimate reason for port 111 to be reachable from outside the local network.

Why firewalling is insufficient

Firewall rules restrict which hosts can reach NFS services. They do not address what those hosts can do once connected. Even with a perfectly configured firewall:

  • UID/GID spoofing works from any trusted client. A compromised or multi-user machine inside the trusted network can claim any identity via AUTH_SYS. Firewall rules cannot distinguish between a legitimate user and an attacker on the same host. (F-1.1)

  • File handles are bearer tokens. A handle captured via network sniffing or obtained from a trusted client works from any IP address the server accepts. Firewall rules bind access to IP ranges, but handles bypass this because the NFS daemon never re-checks whether the presenting client was authorized to receive the handle. (F-2.5)

  • Export escape works from inside the trusted set. Handle construction and LOOKUPP traversal require only a valid mount point. Any client inside the firewall's trusted set can escape the export and access the entire filesystem. (F-2.1)

  • Internal network threats are the common case. Most NFS attacks originate from hosts that already have network access: lateral movement from a compromised web server, a container breakout, a VPN-connected attacker. Firewalls protect the perimeter but NFS is typically deployed behind it.

Firewalling is necessary but not sufficient

Firewall rules are part of defense in depth. They prevent casual external access and reduce the attack surface. But they do not replace Kerberos authentication, proper export options, or per-export filesystem isolation. Use all of them together.