# [FAQ / HOWTO] NFS with firewalling

## Auka

NFS with firewalling

Category: Networking & Security | Firewalling

** Edit:

  - 2005-02-20: Edit /etc/sysctl.conf to add a static port-mapping for lockd (i.e. assign port 4001 for tcp and udp)

    # TCP Port for lock manager

    sys.fs.nfs.nlm_tcpport = 4001

    # UDP Port for lock manager

    sys.fs.nfs.nlm_udpport = 4001

Abstract

When trying to use NFS over a firewall (such as an iptables setup) you will encounter difficulties because some of the TCP/IP and UDP ports which are beeing used by nfs components, are "randomly assigned" as part of the "SunRPC" mechanism.

The following explanations will show you how to set up NFS in such a way that it is possible to configure a meaningful and tight firewall policy. 

This guide will not cover how to configure and set up NFS and neither does it explain how to create firewall rulesets with iptables.

status quo

Without any reconfiguration, when issuing rpcinfo -p you will get an output similar to this:

```
# rpcinfo -p

   program vers proto   port

    100000    2   tcp    111  portmapper

    100000    2   udp    111  portmapper

    100024    1   udp    656  status

    100024    1   tcp    659  status

    100003    2   udp   2049  nfs

    100003    3   udp   2049  nfs

    100003    2   tcp   2049  nfs

    100003    3   tcp   2049  nfs

    100021    1   udp  40273  nlockmgr

    100021    3   udp  40273  nlockmgr

    100021    4   udp  40273  nlockmgr

    100021    1   tcp  37807  nlockmgr

    100021    3   tcp  37807  nlockmgr

    100021    4   tcp  37807  nlockmgr

    100005    1   udp    675  mountd

    100005    1   tcp    678  mountd

    100005    2   udp    675  mountd

    100005    2   tcp    678  mountd

    100005    3   udp    675  mountd

    100005    3   tcp    678  mountd

```

basic nfs and portmap theory

The ports for nfs (2049) as well as portmap (111) are statically assigned, these are well know ports, and defined in /etc/services. Yet the ports for mountd, nlockmgr and status are not well known ports but are beeing dynamically assigned by the portmapper - and that is exactly what is causing problems with firewall setups. (To be exact the portmapper doesn't really allocate ports. A server binds to a port and then registers the port with the portmapper/rpcbind). But we will get back to this and what to do later on in detail. 

First lets have a second short look at the table above. You will see that portmapper as well as nfs use both tcp as well as udp ports. Depending on your NFS kernel setup you might use NFS over udp (which is the default) or NFS over tcp only. If you have enabled NFS tcp support in the kernel you can mount your NFS partitions over tcp by adding "tcp" to your mount options on the client.

So what should be obvious so far is that both the portmapper (111 udp/tcp) as well as the NFS port (2049 for the moment lets also say both udp/tcp) should be enabled at you firewall.

hands-on experiences

Now lets come to what this is all about: Instead of having rpc.statd, rpc.mountd and rpc.nlockmgr register dynamic ports with the portmapper we tell them to always bind to the same port. Then we can use these ports for our firewall setup. Yes, it is really just as simple as it seems.  :Wink: 

1.) First of all you have to edit /etc/conf.d/nfs which should look similar to this:

```
# Config file for /etc/init.d/nfs

# Number of servers to be started up by default

RPCNFSDCOUNT=8

# Options to pass to rpc.mountd

# ex. RPCMOUNTDOPTS="-p 32767

RPCMOUNTDOPTS=""

# Options to pass to rpc.statd

# ex. RPCSTATDOPTS="-p 32765 -o 32766"

RPCSTATDOPTS=""

# OPTIONS to pass to rpc.rquotad

# ex. RPCRQUOTADOPTS="-p 32764"

RPCRQUOTADOPTS=""

```

Have a look at the lines where you can add options to rpcmountd, rpcstatd and rpcquotad. See the comments? Gentoo Developers rule, don't they? As man rpc.mountd and the other manpages would tell you, -p (and -o) are exactly the options you will need to have these services use static ports.  If you don't explicitly want, or have to, use different tcp ports, you can just stick to the default tcp port range 32764-32767. Then just add the options exactly as shown in the comments above.

2.) Time to restart you NFS services on your NFS server. 

```
/etc/init.d/nfs restart
```

3.) Now check the RPC port assignments by issuing 

```
rpcinfo -p
```

  the ports should have changed:

```

    100000    2   tcp    111  portmapper

    100000    2   udp    111  portmapper

    100024    1   udp  32765  status

    100024    1   tcp  32765  status

    100003    2   udp   2049  nfs

    100003    3   udp   2049  nfs

    100003    2   tcp   2049  nfs

    100003    3   tcp   2049  nfs

    100021    1   udp  40315  nlockmgr

    100021    3   udp  40315  nlockmgr

    100021    4   udp  40315  nlockmgr

    100021    1   tcp  55755  nlockmgr

    100021    3   tcp  55755  nlockmgr

    100021    4   tcp  55755  nlockmgr

    100005    1   udp  32767  mountd

    100005    1   tcp  32767  mountd

    100005    2   udp  32767  mountd

    100005    2   tcp  32767  mountd

    100005    3   udp  32767  mountd

    100005    3   tcp  32767  mountd

```

That's it! The ports are what we just defined. Now you can add your firewall rules. 

4.) Now simply add your firewall rules for the ports listed above. I prefer using the excellent Firewall Builder GUI (net-firewall/fwbuilder), if you also do, then generate a group of services for these ports:

```

- udp port 111 (portmap)

- tcp port 111 (portmap)

- tcp port 2049 (nfs over TCP)

- udp port 2049 (nfs over UDP)

- tcp port 32764 (rpc.quotad)

- tcp port 32765 and 32766 (rpc.statd)

- tcp port 32767 (rpc.mountd)

```

Have fun...  :Wink: Last edited by Auka on Sun Feb 20, 2005 12:01 pm; edited 3 times in total

----------

## irony

Won't there still be problems with the lockd ports?

I found a howto that suggests passing lockd port options at boot:

```

root=/dev/hda1 lockd.udpport=32768 lockd.tcpport=32768

```

Is there a simpler way?

----------

## irony

Apparently, passing the lockd.udpport and lockd.tcpport options at boot doesn't seem to work, it still assigns them to different ports every time nfs starts and stops.

----------

## Auka

Hmm...lockd...so far I didn't think about that one to be honest.  :Wink: 

A quick glance tells me that I don't have rpc.lockd running, just kernel lockd. Also "man lockd" says:

```
The  rpc.lockd  program starts the NFS lock manager (NLM) on kernels that don't start it automatically.  However, since most kernels do start it  automatically,  rpc.lockd.   is usually not required.  Even so, running it anyway is harmless.
```

lsof -i shows that on my machine lockd doesn't seem to open or use any ports at all.

----------

## Cocker68

The lockd can be bound to a specific port via /etc/modules.conf:

options lockd nlm_udpport=32768 nlm_tcpport=32768

edit:

Or better: Create a file named /etc/modules.d/nfs with this content.  (This way it will survive a modules-update)

- Cocker :wqLast edited by Cocker68 on Tue Oct 05, 2004 11:13 am; edited 1 time in total

----------

## primero.gentoo

 *irony wrote:*   

> Apparently, passing the lockd.udpport and lockd.tcpport options at boot doesn't seem to work, it still assigns them to different ports every time nfs starts and stops.

 

lockd port configuration depends on kernel configuration, if it is compiled as module or statically in the kernel

```

(compiled as modules)

add "options lockd nlm_udpport=4001 nlm_tcpport=4001" in /etc/modules.conf

(compiled in kernel)

add  "lockd.udpport=4001 lockd.tcpport=4001" to kernel line in the bootloader

```

For quotad instead you need at least 3.08 version (that is in portage) and need to modify "/etc/services" adding the following lines

```

    

      rquotad 4003/tcp

      rquotad 4003/udp

```

and (if ther is not yet) to /etc/rpc

```

rquotad 100011 rquotaprog quota rquota

```

configuring the others daemon via /etc/init.d/nfs you'll have alll your nfs-related daemon statically linked to the ports you choose and you'll be able to configure the firewall in the right way.

Bye

Primero

----------

## violatr

Hey,

I've been looking through the gentoo forums for awhile concerning iptables and NFS, I have been trying to get things functional for quite a while now, but no matter how I modify this script I can't seem to get things right.. it always hangs when attempting to mount shares I'm not sure exactly what the problem is, something to do with sunrpc I think... 

Anyways here is the script.. and any ideas would be very much appreciated  :Smile: 

#!/sbin/runscript

IPTABLES=/sbin/iptables

IPTABLESSAVE=/sbin/iptables-save

IPTABLESRESTORE=/sbin/iptables-restore

FIREWALL=/etc/firewall.rules

DNS1=203.123.69.15

DNS2=203.123.73.59

#inside

IIP=192.168.0.250

IINTERFACE=eth0

LOCAL_NETWORK=192.168.0.0/24

#outside

OIP="`$IFCONFIG $PPP0`"

OINTERFACE=ppp0

opts="${opts} showstatus panic save restore showoptions rules"

depend() {

  need net

}

rules() {

  stop

  ebegin "Setting internal rules"

  einfo "Setting default rule to drop"

  $IPTABLES -P FORWARD DROP

  $IPTABLES -P INPUT   DROP

  $IPTABLES -P OUTPUT  DROP

  #default rule

  einfo "Creating states chain"

  $IPTABLES -N allowed-connection

  $IPTABLES -F allowed-connection

  $IPTABLES -A allowed-connection -m state --state ESTABLISHED,RELATED -j ACCEPT

  $IPTABLES -A allowed-connection -i $IINTERFACE -m limit -j LOG --log-prefix \

"Bad packet from ${IINTERFACE}:"

  $IPTABLES -A allowed-connection -j DROP

  #ICMP traffic

  einfo "Creating icmp chain"

  $IPTABLES -N icmp_allowed

  $IPTABLES -F icmp_allowed

$IPTABLES -A icmp_allowed -m state --state NEW -p icmp --icmp-type \

time-exceeded -j ACCEPT

  $IPTABLES -A icmp_allowed -m state --state NEW -p icmp --icmp-type \

destination-unreachable -j ACCEPT

  $IPTABLES -A icmp_allowed -p icmp -j LOG --log-prefix "Bad ICMP traffic:"

  $IPTABLES -A icmp_allowed -p icmp -j DROP

  #Incoming traffic

  einfo "Creating incoming ssh traffic chain"

  $IPTABLES -N allow-ssh-traffic-in

  $IPTABLES -F allow-ssh-traffic-in

  #Flood protection

  $IPTABLES -A allow-ssh-traffic-in -m limit --limit 1/second -p tcp --tcp-flags \

ALL RST --dport ssh -j ACCEPT

  $IPTABLES -A allow-ssh-traffic-in -m limit --limit 1/second -p tcp --tcp-flags \

ALL FIN --dport ssh -j ACCEPT

  $IPTABLES -A allow-ssh-traffic-in -m limit --limit 1/second -p tcp --tcp-flags \

ALL SYN --dport ssh -j ACCEPT

  $IPTABLES -A allow-ssh-traffic-in -m state --state RELATED,ESTABLISHED -p tcp --dport ssh -j ACCEPT

  einfo "Creating incoming nfs chain"

  $IPTABLES -N allow-nfs

  $IPTABLES -F allow-nfs

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p tcp --dport 111 -j ACCEPT

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p udp --dport 111 -j ACCEPT

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p tcp --dport 1034 -j ACCEPT

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p udp --dport 1034 -j ACCEPT

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p tcp --dport 2049 -j ACCEPT

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p udp --dport 2049 -j ACCEPT

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p udp --dport 32767 -j ACCEPT

  $IPTABLES -A allow-nfs -s 192.168.0.1 -d 0/0 -p tcp --dport 32767 -j ACCEPT

  #outgoing traffic

  einfo "Creating outgoing ssh traffic chain"

  $IPTABLES -N allow-ssh-traffic-out

  $IPTABLES -F allow-ssh-traffic-out

  $IPTABLES -A allow-ssh-traffic-out -p tcp --dport ssh -j ACCEPT

  einfo "Creating outgoing dns traffic chain"

$IPTABLES -N allow-dns-traffic-out

  $IPTABLES -F allow-dns-traffic-out

  $IPTABLES -A allow-dns-traffic-out -p udp -d $DNS1 --dport domain \

-j ACCEPT

  $IPTABLES -A allow-dns-traffic-out -p udp -d $DNS2 --dport domain \

-j ACCEPT

  einfo "Creating outgoing http/https traffic chain"

  $IPTABLES -N allow-www-traffic-out

  $IPTABLES -F allow-www-traffic-out

  $IPTABLES -A allow-www-traffic-out -p tcp --dport www -j ACCEPT

  $IPTABLES -A allow-www-traffic-out -p tcp --dport https -j ACCEPT

  #Catch portscanners

  einfo "Creating portscan detection chain"

  $IPTABLES -N check-flags

  $IPTABLES -F check-flags

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL FIN,URG,PSH -m limit \

--limit 5/minute -j LOG --log-level alert --log-prefix "NMAP-XMAS:"

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL ALL -m limit --limit \

5/minute -j LOG --log-level 1 --log-prefix "XMAS:"

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL ALL -j DROP

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG \

-m limit --limit 5/minute -j LOG --log-level 1 --log-prefix "XMAS-PSH:"

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL NONE -m limit \

--limit 5/minute -j LOG --log-level 1 --log-prefix "NULL_SCAN:"

  $IPTABLES -A check-flags -p tcp --tcp-flags ALL NONE -j DROP

  $IPTABLES -A check-flags -p tcp --tcp-flags SYN,RST SYN,RST -m limit \

--limit 5/minute -j LOG --log-level 5 --log-prefix "SYN/RST:"

  $IPTABLES -A check-flags -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

  $IPTABLES -A check-flags -p tcp --tcp-flags SYN,FIN SYN,FIN -m limit \

--limit 5/minute -j LOG --log-level 5 --log-prefix "SYN/FIN:"

  $IPTABLES -A check-flags -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

  # Apply and add invalid states to the chains

  einfo "Applying chains to INPUT"

  $IPTABLES -A INPUT -m state --state INVALID -j DROP

  $IPTABLES -A INPUT -j icmp_allowed

  $IPTABLES -A INPUT -j check-flags

  $IPTABLES -A INPUT -i lo -j ACCEPT

  $IPTABLES -A INPUT -j allow-ssh-traffic-in

  $IPTABLES -A INPUT -j allowed-connection

  $IPTABLES -A INPUT -j allow-nfs

  einfo "Applying chains to FORWARD"

  $IPTABLES -A FORWARD -m state --state INVALID -j DROP

  $IPTABLES -A FORWARD -j icmp_allowed

  $IPTABLES -A FORWARD -j check-flags

  $IPTABLES -A FORWARD -o lo -j ACCEPT

  $IPTABLES -A FORWARD -j allow-ssh-traffic-in

  $IPTABLES -A FORWARD -j allow-www-traffic-out

  $IPTABLES -A FORWARD -j allowed-connection

  einfo "Applying chains to OUTPUT"

  $IPTABLES -A OUTPUT -m state --state INVALID -j DROP

  $IPTABLES -A OUTPUT -j icmp_allowed

  $IPTABLES -A OUTPUT -j check-flags

  $IPTABLES -A OUTPUT -o lo -j ACCEPT

  $IPTABLES -A OUTPUT -j allow-ssh-traffic-out

  $IPTABLES -A OUTPUT -j allow-dns-traffic-out

  $IPTABLES -A OUTPUT -j allow-www-traffic-out

  $IPTABLES -A OUTPUT -j allowed-connection

  #Allow client to route through via NAT (Network Address Translation)

  $IPTABLES -t nat -A POSTROUTING -o $IINTERFACE -j MASQUERADE

  eend $?

}

start() {

  ebegin "Starting firewall"

  if [ -e "${FIREWALL}" ]; then

  restore

  else

    einfo "${FIREWALL} does not exists. Using default rules."

    rules

  fi

  eend $?

}

stop() {

  ebegin "Stopping firewall"

  $IPTABLES -F

  $IPTABLES -t nat -F

  $IPTABLES -X

  $IPTABLES -P FORWARD ACCEPT

  $IPTABLES -P INPUT   ACCEPT

  $IPTABLES -P OUTPUT  ACCEPT

  eend $?

}

showstatus() {

  ebegin "Status"

  $IPTABLES -L -n -v --line-numbers

  einfo "NAT status"

  $IPTABLES -L -n -v --line-numbers -t nat

  eend $?

}

panic() {

  ebegin "Setting panic rules"

  $IPTABLES -F

  $IPTABLES -X

  $IPTABLES -t nat -F

  $IPTABLES -P FORWARD DROP

  $IPTABLES -P INPUT   DROP

 $IPTABLES -P OUTPUT  DROP

  $IPTABLES -A INPUT -i lo -j ACCEPT

  $IPTABLES -A OUTPUT -o lo -j ACCEPT

  eend $?

}

save() {

  ebegin "Saving Firewall rules"

  $IPTABLESSAVE > $FIREWALL

  eend $?

}

restore() {

  ebegin "Restoring Firewall rules"

  $IPTABLESRESTORE < $FIREWALL

  eend $?

}

restart() {

  svc_stop; svc_start

}

showoptions() {

  echo "Usage: $0 {start|save|restore|panic|stop|restart|showstatus}"

  echo "start)      will restore setting if exists else force rules"

  echo "stop)       delete all rules and set all to accept"

  echo "rules)      force settings of new rules"

  echo "save)       will store settings in ${FIREWALL}"

  echo "restore)    will restore settings from ${FIREWALL}"

  echo "showstatus) Shows the status"

}

----------

## Hagar

The correct parameters depend on the kernel version.

2.4.*: lockd.udpport=4001 lockd.tcpport=4001

2.6.*: lockd.nlm_udpport=4001 lockd.nlm_tcpport=4001

----------

## solarium_rider

 *violatr wrote:*   

> Hey,
> 
> Anyways here is the script.. and any ideas would be very much appreciated 
> 
> ...
> ...

 

I know this thread has been dead for a while, but at least one of the reasons why your this script isn't working for nfs is because you have allow-nfs after the allowed-connection chain.  The allowed-connection chain must be last (for reasons I don't fully understand.)

I'm currently try to lockdown nfs to certain ports and ran across this thread =)

----------

## ikke

Thanks for this great tutorial. I was confronted with this problem yesterday, thanks to this howto I was able to fix it, and now I understand better how NFS works. It'd have taken me several hours to find out what to do without this howto.

----------

## flipnode

 *Auka wrote:*   

> NFS with firewalling
> 
> ** Edit:
> 
>   - 2005-02-20: Edit /etc/sysctl.conf to add a static port-mapping for lockd (i.e. assign port 4001 for tcp and udp)
> ...

 

The above isn't correct, it should be

```

    # TCP Port for lock manager

    fs.nfs.nlm_tcpport = 4001

    # UDP Port for lock manager

    fs.nfs.nlm_udpport = 4001

```

After you make the change then run the command

```

#sysctl -p

```

for some reason the sysctl.conf file has the wrong or old variables defined.

----------

## jamapii

I use "rpcinfo -p | grep $j | grep $i | cut -b 22-29" to find the real ports, where i and j are protocol (tcp or udp) and rpcinfo's "Program" number or name, e.g. "i=tcp; j=portmapper" or "i=tcp; j=100000".

You could even write a wrapper function and specify "if portnumber >= 100000 then it's rpc" and possibly "if the port string isn't in /etc/services then it's rpc". Also there's /etc/rpc

----------

## GuyPaddock

Good tutorial that provided almost exactly what I needed, but I wanted to let you know you're missing one command during step 2:

```

/etc/init.d/rpc.statd restart

```

Without that, "status" doesn't pick up the port changes.

----------

