Ubuntu – NFS support in iptables (or other firewall)

debianfirewalliptablesnfsUbuntu

How do I enable NFS mounts/shares with iptables with DROP policy?

NFS uses dynamically assigned ports, thus it is difficult to use with firewalls.
I have an NFS server and a few clients.
I would like to accept traffic ONLY on ports that are required for NFS to work.
I have configured the NFS server to use static ports (4000-4004).

The problem, however, is that the clients still selects a random port — and because it is UDP I can only make it work if I accept all UDP traffic from the server.

I found some documentation that describes setting a /sys variable that would limit the client to use a static range of ports for NFS, however, I cannot seem to make it work.
The /sys variables mentioned are:

/sys/module/sunrpc/parameters/max_resvport 
/sys/module/sunrpc/parameters/min_resvport
/sys/module/lockd/parameters/nlm_tcpport
/sys/module/lockd/parameters/nlm_udpport
/sys/module/nfs/parameters/callback_tcpport 

I have not been able to make this work. Maybe I am approaching this the wrong way? It seems unnecessarily complicated for what I thought would be a simple task.

I use NFS on Debian and Ubuntu.

Best Answer

The fact that the client port is ephemeral isn't a problem, though as you say the mutability of the server-side port is.

Assuming you have successfully instructed the server to use only ports 4000-4004 for nfsd, you have three issues: clients must be able to talk to the RPC portmapper, the mount daemon, and the NFS service, on the server.

For the NFS service

Assuming you're right about having modified the NFS service to listen on 4000-4004, clients should add

iptables -A INPUT  -d se.rv.er.ip -p udp --dport 4000:4004 -j ACCEPT
iptables -A OUTPUT -s se.rv.er.ip -p udp --sport 4000:4004 -j ACCEPT

while the server should add

iptables -A INPUT  -p udp --dport 4000:4004 -j ACCEPT
iptables -A OUTPUT -p udp --sport 4000:4004 -j ACCEPT

If you want to permit NFS over TCP, repeat those rules with -p tcp instead of -p udp.

For the portmapper

Clients should add

iptables -A INPUT  -d se.rv.er.ip -p udp --dport 111 -j ACCEPT
iptables -A OUTPUT -s se.rv.er.ip -p udp --sport 111 -j ACCEPT

while the server should add

iptables -A INPUT  -p udp --dport 111 -j ACCEPT
iptables -A OUTPUT -p udp --sport 111 -j ACCEPT

You may need to add pairs of rules for -p tcp as well, as the portmapper usually supports TCP as well. Check your rejection logs to see what's being DROPped and adjust accordingly.

For the mount daemon

You need to find out what port it's running on on the server, with server% rpcinfo -p | grep mount; on my server it's UDP/32775 and TCP/32769. To allow those, clients should add

iptables -A INPUT  -d se.rv.er.ip -p udp --dport 32775 -j ACCEPT
iptables -A OUTPUT -s se.rv.er.ip -p udp --sport 32775 -j ACCEPT

while the server should add

iptables -A INPUT  -p udp --dport 32775 -j ACCEPT
iptables -A OUTPUT -p udp --sport 32775 -j ACCEPT

and two similar pairs with -p tcp --[ds]port 32769.

It is your responsibility to get those lines in the right place in your INPUT and OUTPUT chains; near the beginning is probably a good idea.

Edit: in the light of your answer below, I have updated the rules above.