Legacy NFS v3 on Debian Wheezy

This is basically just a note for myself. There are times when you simply don’t need fancy “new” things like NFS v4, and v3 will do just fine. So why allow access via v4 then? Here’s how to set it up on Debian 7 (Wheezy), including some simple firewalling.

Initial setup

Once you’ve installed the nfs-kernel-server, nfs-common and rpcbind packages, and started fired up their services, you’ll be seeing a process list like this:

root   [nfsd4]
root   [nfsd4_callbacks]
root   [lockd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   /sbin/rpcbind -w
root   /usr/sbin/rpc.idmapd
root   /usr/sbin/rpc.mountd --manage-gids
statd  /sbin/rpc.statd

And listening tcp/udp ports similar to this:

tcp  0.0.0.0:111    rpcbind
tcp  0.0.0.0:2049   -
tcp  0.0.0.0:49425  -
tcp  0.0.0.0:49546  rpc.mountd
tcp  0.0.0.0:53457  rpc.statd
tcp  0.0.0.0:57298  rpc.mountd
tcp  0.0.0.0:58384  rpc.mountd
udp  0.0.0.0:2049   -
udp  0.0.0.0:111    rpcbind
udp  0.0.0.0:896    rpcbind
udp  0.0.0.0:42342  rpc.mountd
udp  0.0.0.0:43437  -
udp  0.0.0.0:43640  rpc.mountd
udp  0.0.0.0:51389  rpc.statd
udp  0.0.0.0:53773  rpc.mountd
udp  127.0.0.1:931  rpc.statd

Restart the services, and you’ll see most of those ports change — not very firewall friendly. Fortunately, it’s not too complicated to modify this behaviour.

/etc/default/nfs-common

Set a static listening port for the rpc.statd daemon.

-STATDOPTS=
+STATDOPTS='--port 2046

You can also set the outgoing port with the –outgoing-port argument, but I don’t find that useful since I don’t do egress firewalling in this case.

The rpc.idmapd and rpc.gssd daemons are not needed if you run NFS v3.

-NEED_IDMAPD=
-NEED_GSSD=
+NEED_IDMAPD=no
+NEED_GSSD=no

/etc/default/nfs-kernel-server

Disable NFS v4 in the kernel server. The nfs-kernel-server init script does not feature a RPCNFSDOPTS variable or similar, but this one works just as well, since this variable is appended to the rpc.nfsd command line:

-RPCNFSDCOUNT=8
+RPCNFSDCOUNT='--no-nfs-version 4 8'

Disable NFS v4 in the rpc.mountd daemon, and set a static listening port:

-RPCMOUNTDOPTS=--manage-gids
+RPCMOUNTDOPTS='--manage-gids --no-nfs-version 4 --port 2048'

sysctl

The ports used by the kernel server is configured either via the kernel module arguments, the kernel command line, or via the sysctl interface. I prefer the latter, especially in case of virtualized systems running kernels without modules support.

Simply add a new file in /etc/sysctl.d with a .conf file extension, containing these settings:

fs.nfs.nlm_tcpport = 2050
fs.nfs.nlm_udpport = 2050

Make sure to set the parameters using the sysctl tool, or apply the ones found in the newly created file via the tool’s -p option.

/etc/services

Just to make output of netstat, tcpdump etc. a bit prettier if not run with numeric options, adding something like this to the services file is nice:

nfs.mountd 2048/tcp
nfs.mountd 2048/udp
nfs.lockd 2050/tcp
nfs.lockd 2050/udp

Post re-configuration

Restart the services, and you’ll end up with a bit smaller process list:

root   [nfsd4]
root   [nfsd4_callbacks]
root   [lockd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   [nfsd]
root   /sbin/rpcbind -w
root   /usr/sbin/rpc.mountd --manage-gids --no-nfs-version 4 --port 2048
statd  /sbin/rpc.statd --port 2046

And a bit cleaner list of listening tcp/udp ports:

tcp  0.0.0.0:111    rpcbind
tcp  0.0.0.0:2046   rpc.statd
tcp  0.0.0.0:2048   rpc.mountd
tcp  0.0.0.0:2049   -
tcp  0.0.0.0:2050   -
udp  0.0.0.0:111    rpcbind
udp  0.0.0.0:738    rpcbind
udp  0.0.0.0:2046   rpc.statd
udp  0.0.0.0:2048   rpc.mountd
udp  0.0.0.0:2049   -
udp  0.0.0.0:2050   -
udp  127.0.0.1:773  rpc.statd

rpcbind and rpc.statd still opens some dynamic low-range ports (that are not listed in the output of rpcinfo -p). If these ports are bothering you, you might want to have a look at the hack provided at http://wiki.metawerx.net/wiki/setrpcrandomport.

iptables

In your filter table, add two simple rules to your INPUT chain:

-i interface -p udp -m multiport --dports 111,2046,2048,2049,2050 -m state --state NEW -j ACCEPT
-i interface -p tcp -m multiport --dports 111,2046,2048,2049,2050 -m state --state NEW -j ACCEPT

You could also filter on hosts etc. A simple way to do that is to change the ACCEPT target to a custom chain, in which you could place some source address filtering. Ever better – simply use IP sets, which could be used for both the incoming ports and the source addresses in this case.

Obviously, you would want to add some default policy or some REJECT rules in the end of the INPUT chain.

No IPv6?

You should probably run IPv6, but if you don’t, and IPv6 is disabled in your kernel (or via the sysctl interface), you might see some nasty messages upon starting the nfs-kernel-server service:

[....] Starting NFS kernel daemon:
nfsdrpc.nfsd: address family inet6 not supported by protocol TCP
mountdrpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
. ok

To fix this, have a look in /etc/netconfig and disable (comment out) the udp6 and tcp6 entries.