If you don't think the security measures apply to you, you're probably wrong. In Section 6.1 we'll cover securing the portmapper, server and client security in Section 6.2 and Section 6.3 respectively. Finally, in Section 6.4 we'll briefly talk about proper firewalling for your nfs server.
Finally, it is critical that all of your nfs daemons and client programs are current. If you think that a flaw is too recently announced for it to be a problem for you, then you've probably already been compromised.
A good way to keep up to date on security alerts is to subscribe to the bugtraq mailinglists. You can read up on how to subscribe and various other information about bugtraq here: http://www.securityfocus.com/forums/bugtraq/faq.html
Additionally searching for NFS at securityfocus.com's search engine will show you all security reports pertaining to NFS.
You should also regularly check CERT advisories. See the CERT web page at www.cert.org.
strings /sbin/portmap | grep hosts. |
On a securable machine it comes up something like this:
/etc/hosts.allow /etc/hosts.deny @(#) hosts_ctl.c 1.4 94/12/28 17:42:27 @(#) hosts_access.c 1.21 97/02/12 02:13:22 |
First we edit /etc/hosts.deny. It should contain the line
portmap: ALL |
which will deny access to everyone. While it is closed run:
rpcinfo -p |
Closing the portmapper for everyone is a bit drastic, so we open it again by editing /etc/hosts.allow. But first we need to figure out what to put in it. It should basically list all machines that should have access to your portmapper. On a run of the mill Linux system there are very few machines that need any access for any reason. The portmapper administers nfsd, mountd, ypbind/ypserv, rquotad, lockd (which shows up as nlockmgr), statd (which shows up as status) and 'r' services like ruptime and rusers. Of these only nfsd, mountd, ypbind/ypserv and perhaps rquotad,lockd and statd are of any consequence. All machines that need to access services on your machine should be allowed to do that. Let's say that your machine's address is 192.168.0.254 and that it lives on the subnet 192.168.0.0, and that all machines on the subnet should have access to it (for an overview of those terms see the the Networking-Overview-HOWTO). Then we write:
portmap: 192.168.0.0/255.255.255.0 |
... eth0 Link encap:Ethernet HWaddr 00:60:8C:96:D5:56 inet addr:192.168.0.254 Bcast:192.168.0.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:360315 errors:0 dropped:0 overruns:0 TX packets:179274 errors:0 dropped:0 overruns:0 Interrupt:10 Base address:0x320 ... |
Kernel routing table Destination Gateway Genmask Flags Metric Ref Use Iface ... 192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 174412 eth0 ... |
The /etc/hosts.deny and /etc/hosts.allow files are described in the manual pages of the same names.
IMPORTANT: Do not put anything but IP NUMBERS in the portmap lines of these files. Host name lookups can indirectly cause portmap activity which will trigger host name lookups which can indirectly cause portmap activity which will trigger...
Versions 0.2.0 and higher of the nfs-utils package also use the hosts.allow and hosts.deny files, so you should put in entries for lockd, statd, mountd, and rquotad in these files too. For a complete example, see Section 3.2.2.
The above things should make your server tighter. The only remaining problem is if someone gains administrative access to one of your trusted client machines and is able to send bogus NFS requests. The next section deals with safeguards against this problem.
/home slave1(rw,root_squash) |
You enter the above options in the options column, with the rsize and wsize, separated by commas.
Describing how to set up a Linux firewall is well beyond the scope of this document. Interested readers may wish to read the Firewall-HOWTO or the IPCHAINS-HOWTO. For users of kernel 2.4 and above you might want to visit the netfilter webpage at: http://netfilter.filewatcher.org. If you are already familiar with the workings of ipchains or netfilter this section will give you a few tips on how to better setup your NFS daemons to more easily firewall and protect them.
A good rule to follow for your firewall configuration is to deny all, and allow only some - this helps to keep you from accidentally allowing more than you intended.
In order to understand how to firewall the NFS daemons, it will help to breifly review how they bind to ports.
When a daemon starts up, it requests a free port from the portmapper. The portmapper gets the port for the daemon and keeps track of the port currently used by that daemon. When other hosts or processes need to communicate with the daemon, they request the port number from the portmapper in order to find the daemon. So the ports will perpetually float because different ports may be free at different times and so the portmapper will allocate them differently each time. This is a pain for setting up a firewall. If you never know where the daemons are going to be then you don't know precisely which ports to allow access to. This might not be a big deal for many people running on a protected or isolated LAN. For those people on a public network, though, this is horrible.
In kernels 2.4.13 and later with nfs-utils 0.3.3 or later you no longer have to worry about the floating of ports in the portmapper. Now all of the daemons pertaining to nfs can be "pinned" to a port. Most of them nicely take a -p option when they are started; those daemons that are started by the kernel take some kernel arguments or module options. They are described below.
Some of the daemons involved in sharing data via nfs are already bound to a port. portmap is always on port 111 tcp and udp. nfsd is always on port 2049 TCP and UDP (however, as of kernel 2.4.17, NFS over TCP is considered experimental and is not for use on production machines).
The other daemons, statd, mountd, lockd, and rquotad, will normally move around to the first available port they are informed of by the portmapper.
To force statd to bind to a particular port, use the -p portnum option. To force statd to respond on a particular port, additionally use the -o portnum option when starting it.
To force mountd to bind to a particular port use the -p portnum option.
For example, to have statd broadcast of port 32765 and listen on port 32766, and mountd listen on port 32767, you would type:
# statd -p 32765 -o 32766 # mountd -p 32767 |
lockd is started by the kernel when it is needed. Therefore you need to pass module options (if you have it built as a module) or kernel options to force lockd to listen and respond only on certain ports.
If you are using loadable modules and you would like to specify these options in your /etc/modules.conf file add a line like this to the file:
options lockd nlm_udpport=32768 nlm_tcpport=32768 |
The above line would specify the udp and tcp port for lockd to be 32768.
If you are not using loadable modules or if you have compiled lockd into the kernel instead of building it as a module then you will need to pass it an option on the kernel boot line.
It should look something like this:
vmlinuz 3 root=/dev/hda1 lockd.udpport=32768 lockd.tcpport=32768 |
The port numbers do not have to match but it would simply add unnecessary confusion if they didn't.
If you are using quotas and using rpc.quotad to make these quotas viewable over nfs you will need to also take it into account when setting up your firewall. There are two rpc.rquotad source trees. One of those is maintained in the nfs-utils tree. The other in the quota-tools tree. They do not operate identically. The one provided with nfs-utils supports binding the daemon to a port with the -p directive. The one in quota-tools does not. Consult your distribution's documentation to determine if yours does.
For the sake of this discussion lets describe a network and setup a firewall to protect our nfs server. Our nfs server is 192.168.0.42 our client is 192.168.0.45 only. As in the example above, statd has been started so that it only binds to port 32765 for incoming requests and it must answer on port 32766. mountd is forced to bind to port 32767. lockd's module parameters have been set to bind to 32768. nfsd is, of course, on port 2049 and the portmapper is on port 111.
We are not using quotas.
Using IPCHAINS, a simple firewall might look something like this:
ipchains -A input -f -j ACCEPT -s 192.168.0.45 ipchains -A input -s 192.168.0.45 -d 0/0 32765:32768 -p 6 -j ACCEPT ipchains -A input -s 192.168.0.45 -d 0/0 32765:32768 -p 17 -j ACCEPT ipchains -A input -s 192.168.0.45 -d 0/0 2049 -p 17 -j ACCEPT ipchains -A input -s 192.168.0.45 -d 0/0 2049 -p 6 -j ACCEPT ipchains -A input -s 192.168.0.45 -d 0/0 111 -p 6 -j ACCEPT ipchains -A input -s 192.168.0.45 -d 0/0 111 -p 17 -j ACCEPT ipchains -A input -s 0/0 -d 0/0 -p 6 -j DENY -y -l ipchains -A input -s 0/0 -d 0/0 -p 17 -j DENY -l |
The equivalent set of commands in netfilter is:
iptables -A INPUT -f -j ACCEPT -s 192.168.0.45 iptables -A INPUT -s 192.168.0.45 -d 0/0 32765:32768 -p 6 -j ACCEPT iptables -A INPUT -s 192.168.0.45 -d 0/0 32765:32768 -p 17 -j ACCEPT iptables -A INPUT -s 192.168.0.45 -d 0/0 2049 -p 17 -j ACCEPT iptables -A INPUT -s 192.168.0.45 -d 0/0 2049 -p 6 -j ACCEPT iptables -A INPUT -s 192.168.0.45 -d 0/0 111 -p 6 -j ACCEPT iptables -A INPUT -s 192.168.0.45 -d 0/0 111 -p 17 -j ACCEPT iptables -A INPUT -s 0/0 -d 0/0 -p 6 -j DENY --syn --log-level 5 iptables -A INPUT -s 0/0 -d 0/0 -p 17 -j DENY --log-level 5 |
The first line says to accept all packet fragments (except the first packet fragment which will be treated as a normal packet). In theory no packet will pass through until it is reassembled, and it won't be reassembled unless the first packet fragment is passed. Of course there are attacks that can be generated by overloading a machine with packet fragments. But NFS won't work correctly unless you let fragments through. See Section 7.8 for details.
The other lines allow specific connections from any port on our client host to the specific ports we have made available on our server. This means that if, say, 192.158.0.46 attempts to contact the NFS server it will not be able to mount or see what mounts are available.
With the new port pinning capabilities it is obviously much easier to control what hosts are allowed to mount your NFS shares. It is worth mentioning that NFS is not an encrypted protocol and anyone on the same physical network could sniff the traffic and reassemble the information being passed back and forth.
/home 127.0.0.1(rw) |
# ssh root@192.168.0.42 -L 250:localhost:2049 -f sleep 60m # ssh root@192.168.0.42 -L 251:localhost:32767 -f sleep 60m |
localhost:/home /mnt/home nfs rw,hard,intr,port=250,mountport=251 0 0 |
It may also be possible to use IPSec to encrypt network traffic between your client and your server, without compromising any local security on the server; this will not be taken up here. See the FreeS/WAN home page for details on using IPSec under Linux.