Securing NFSv4 with SSH port forwarding

10 minute read

About

We store our family photos on a Network Attached Storage (NAS) server. There are several available protocols for securely sharing these photos on the home network. I have implemented an architecture based on the Network File System (NFS) protocol. Using NFS in 2022 may seem outdated, but for our use case it’s sufficient.

NOTE:
For now, the architecture depends on SSH port forwarding. In the future, I plan to implement native NFSv4 security features.

The Network File System (NFS) protocol

NFS is a distributed file system protocol used by clients to access files on a remote NFS server. NFS is an old protocol. Version 2 was defined in 1989, and the latest version, NFS 4.2, was published in 2016. The NFS design goals were performance, simplicity, and cross-vendor compatibility. A secure NFS architecture, however, is somewhat complex, and requires a good understanding of the protocol.

When NFSv4 was released in 2000 it was virtually a rewrite of NFS and introduced multiple changes. Most notably, a greater emphasis on security. Also, NFSv4 changed the default transport protocol to TCP, and integrated file locking and the mount protocol into NFS. In addition, when using NFSv4, it is not necessary to use the RPC binding protocols like portmappper.

NFSv4 is defined in RFC7530 - Network File System (NFS) Version 4 Protocol.

NFSv4 security features

Traditionally, an NFS client has to satisfy two requirements to gain access to exported files on a remote NFS server:

  • mount access
  • file access

First, NFS restricts which clients that are permitted to mount file systems, by IP address or by host name. Thus, from an authentication perspective, the NFS server relied on the client to authenticate the end-user. Second, the NFS server enforces file system permissions for the end-user. The end-user’s effective user ID and group ID are used to check access to the requested files.

These requirements are implemented in the AUTH_SYS (or AUTH_UNIX) security flavour. AUTH_SYS is the default security flavor for all versions of NFS.

NFS network communication is plain-text by default.
As mentioned in RFC1813 - NFS Version 3 Protocol Specification (8.Security Considerations)

As with the previous protocol revision (version 2), NFS version 3 defers to the authentication provisions of the supporting RPC protocol [RFC1057], and assumes that data privacy and integrity are provided by underlying transport layers as available in each implementation of the protocol.

Note: NFSv4 also exchange information in plain-text when using AUTH_SYS.

RPC security

The NFS protocol makes use of Remote Procedure Calls (RPC). Using RPC, a client calls a procedure on a remote host across the network. Such procedures could be open (opens a regular file), or read (reads data from a file on remote server, and returns the content to the client). For NFSv2 and NFSv3, RPC implementations have included AUTH_NONE, AUTH_SYS, AUTH_DH, and AUTH_KRB4 as security flavors.

For NFSv4, the RPCSEC_GSS (RFC4121) security flavor was added to secure NFS server operations. RPCSEC_GSS uses functionality of GSS-API (RFC2743), and introduces a security model which provides end-to-end authentication. The end-user on the client mutually authenticates to a principal on an NFS server.

NFSv4 may support multiple security mechanisms. NFSv4 servers and clients must support the Kerberos V5 framework which provides authentication, integrity, and privacy. Details in RFC7530 - 3.2.1.1. Kerberos V5 as a Security Triple

AUTH_SYS security considerations

Based on your threat model, the AUTH_SYS security flavor may not provide sufficient security.

According to RFC5531 - RPC: Remote Procedure Call Protocol Specification Version 2:

AUTH_SYS as described in Appendix A is known to be insecure due to the lack of a verifier to permit the credential to be validated. AUTH_SYS SHOULD NOT be used for services that permit clients to modify data.

The alternative: SSH port forwarding

I have yet to implement RPCSEC_GSS on my NAS. An alternative solution is to use SSH port forwarding in combination with AUTH_SYS to protect the authenticity, integrity and confidentiality of NFS server operations. This blog post describes how to implement NFSv4 + SSH Port Forwarding.

Exported filesystems (/etc/exports)

The NFS server defines remote mounts points and security parameters in /etc/exports:

V4: <NFSv4 root path on server> -sec=<security flavor> <ip address>
<directory path on server> -mapall=<uid> <ip address>

From a security perspective, the interesting parameters are:

  • -sec=<security flavor>
  • <ip address>
  • -mapall=<uid>

The first component, <NFSv4 root path on server>, defines the NFSv4 tree root. This parameter is used in combination with <directory path on server> to export NFSv4 mount points.

The second component, -sec= and -mapall=<uid>, configures the security flavour and sets a mapping for all client UIDs (including root) to an UID on the NFS server. The third component (<ip address>), specifies the host to which the line applies.

Additional configuration options are described in man page exports(5)

The alternative: sharing filesystems using ZFS

ZFS supports sharing datasets. Currently, I prefer to use /etc/exports due to old habits.

Additional details in man page zfs-share(8)

Access control lists (ACLs)

To access files on an NFS share, the end-user must have the required file system permissions. On FreeBSD, the ACLs can be defined as Access Controls Lists (POSIX®.1e compatible) or NFSv4 ACL.

NFSv4 ACLs are based on NT-style ACLs, and provide a more granular set of access privileges. FreeBSD has extended setfacl/getfacl to include functionality for NFSv4 ACLs.

The illumos ZFS Administration Guide has more details on NFSv4 ACLs.

Firewalling

NFSv2 and NFSv3 comprised several separate daemons:

  • nfsd (2049/udp and 2049/tcp)
  • rpcbind (111/udp and 111/tcp)
  • mountd (<mountd_port>/udp and <mountd_port>/tcp)
  • statd (<statd_port>/udp and <statd_port>/tcp)
  • lockd (<lockd_port>/udp and <lockd_port>/tcp)

NFSv4 simplifies firewall configuration by only requiring 2049/tcp to be accessible to clients.

The alternative: SSH port forwarding

When using SSH port forwarding, all the aforementioned ports should be blocked in the firewall. The SSH protocol requires that 22/tcp is accessible to clients.

NFS configuration on NAS server

The NAS server is running FreeBSD 13.

To configure a NFS server that supports NFSv4 only, the following variables are required in /etc/rc.conf

# /etc/rc.conf
...
# NFS
nfs_server_enable="YES"
nfsv4_server_enable="YES"
nfsv4_server_only="YES"
nfsuserd_enable="YES"
mountd_enable="YES"
rpc_lockd_enable="NO"
rpc_statd_enable="NO"
rpcbind_enable="NO"
...

Start and verify nfs services

# Restart nfs and mountd
root@nas: [/root]# service nfsd restart && service mountd restart
Stopping nfsd.
Waiting for PIDS: 953 956.
Starting nfsd.
Stopping mountd.
Waiting for PIDS: 946.
NFSv4 only server
Starting mountd.
root@nas: [/root]#

# Verify running daemon
root@nas: [/root]# sockstat -l | egrep '2049'
root     nfsd       4384  5  tcp4   *:2049                *:*
root     nfsd       4384  6  tcp6   *:2049                *:*
root@nas: [/root]#

# Verify running mountd
root@nas: [/root]# ps aux | grep -i mount
root    4406   0.0  0.0  12912  2580  -  Is   10:35      0:00.00 /usr/sbin/mountd -r -S -R /etc/exports /etc/zfs/exports
root    4431   0.0  0.0   4676  2172  0  R+   10:36      0:00.01 grep -i mount
root@nas: [/root]#

The mountd daemon is started with -R since the mount protocol is not used by NFSv4.

Exporting the file systems

The file systems on the NAS server is exported using NFSv4, Thus, ZFS sharenfs is set to off.

root@nas: [/root]# zfs get -p sharenfs zdata/photos
NAME          PROPERTY  VALUE     SOURCE
zdata/photos  sharenfs  off       default
root@nas: [/root]# 

The shared file systems are defined in /etc/exports:

# /etc/exports
V4: /zdata/photos/family/ -sec=sys 127.0.0.1
/zdata/photos/family/lightroom -mapall=1088 127.0.0.1

Note:
The authorized client IP is 127.0.0.1. When using SSH port forwarding, the client connects to NFS from a local IP address.

Every end-user on the client is mapped to a local NAS user account, photos-mac.

root@nas: [/root]# cat /etc/exports
photos-mac:\*:1088:1088:photos-mac:/home/photos-mac:/usr/local/libexec/git-core/git-shell
root@nas: [/root]# 

Define access control lists

The file system permissions are enforced using NFSv4 ACL. Thus, the acltype is set to nfsv4.

root@nas: [/root]# zfs get -p acltype zdata/photos
NAME          PROPERTY  VALUE     SOURCE
zdata/photos  acltype   nfsv4     default
root@nas: [/root]#

root@nas: [/root]# mount -v | egrep 'zdata/photos'
zdata/photos on /zdata/photos (zfs, NFS exported, local, nfsv4acls, fsid eebb0c06dec104a6)
root@nas: [/root]#

Grant read, write, append and delete permissions on all files and directories in the family photos directory:

# Grant read, write, append and delete permissions 
root@nas: [/root]# setfacl -m u:photos-mac:rx::allow /zdata/
root@nas: [/root]# setfacl -m u:photos-mac:rx::allow /zdata/photos/
root@nas: [/root]# setfacl -m u:photos-mac:rx::allow /zdata/photos/family/
root@nas: [/root]# setfacl -m u:photos-mac:rx::allow /zdata/photos/family/lightroom/
root@nas: [/root]# setfacl -R -m u:photos-mac:rwxpdaARc::allow /zdata/photos/family/lightroom/

# Verify permissions
root@nas: [/root]# getfacl /zdata/photos/family/lightroom/Masters/
# file: /zdata/photos/family/lightroom/Masters/
# owner: root
# group: wheel
   user:photos-mac:rwxp-daAR-c---:-------:allow
            owner@:rwxp--aARWcCos:-------:allow
            group@:r-x---a-R-c--s:-------:allow
         everyone@:r-x---a-R-c--s:-------:allow
root@nas: [/root]#

The parameters are described in man page setfacl(1).

Connect from client to NAS server

The client is running macOS and has a regular user account photos. This user establishes a SSH forwarding tunnel, and mounts the exported NFS share as a local file system:

# Set up a SSH forwarding tunnel 
photos@Mac-mini ~ % ssh -fN -L 3049:localhost:2049 photos-mac@192.168.1.222
Enter passphrase for key '/Users/photos/.ssh/id_rsa':
photos@Mac-mini ~ %

# Verify tunnel
photos@Mac-mini ~ % netstat -na | egrep '192.168.1.222.22'
tcp4       0      0  192.168.1.122.53313    192.168.1.222.22        ESTABLISHED
photos@Mac-mini ~ % nc -z -v localhost 3049
Connection to localhost port 3049 [tcp/nsws] succeeded!
photos@Mac-mini ~ %

The -N switch instructs ssh to not execute a remote command. The -f option backgrounds ssh.
The -L option tunnels a local application port on the client to a port on a remote server machine.

After establishing the SSH tunnel, the end-user can mount the NFS share:

# Mount NFS share on client
photos@Mac-mini ~ % mount -v -t nfs -o port=3049,vers=4 localhost:/lightroom /Users/photos/mnt
localhost:/lightroom on /Users/photos/mnt (nfs, nodev, nosuid, mounted by photos)
photos@Mac-mini ~ %

# Verify NFS share access
photos@Mac-mini ~ % cd mnt
photos@Mac-mini mnt % ls -la
total 81
drwx------   4 root    wheel      7 Aug  1 15:13 .
drwxr-x---+ 23 photos  staff    736 Aug  1 18:48 ..
-rw-r--r--   1 root    wheel  14340 May 15 15:28 .DS_Store
-rw-r--r--   1 root    wheel      0 Jan 24  2014 .localized
drwxr-xr-x  15 root    wheel     16 Jul 31 11:37 Masters
photos@Mac-mini mnt %
photos@Mac-mini mnt % file Masters/2022/2022-01-03/P1090547.RW2
Masters/2022/2022-01-13/P0000000.RW2: data
photos@Mac-mini mnt %


Security considerations

SSH port forwarding and NFS

The combination of SSH port forwarding and NFS provides confidentiality and integrity for the network traffic between client and server, and authenticates the end-user. A potential security issue is that every user on the NAS server who can establish a SSH forwarding tunnel, can mount the exported NFS file systems with the same permissions as the photos-mac user. Thus, it requires that you trust every user account on the NAS server. This also applies to users with a restricted shell like /usr/local/libexec/git-core/git-shell.

NOTE:
A mitigation technique is to add no-port-forwarding to ~/.ssh/authorized_keys for all users who should not be able to mount NFS shares.

More information in man page sshd(8).

File locking

The NFS client sends hostname (caller_name), in each file lock request. When using SSH port forwarding, every request is from source IP address localhost. Thus, NFS cannot differentiate clients, and locking will no longer work.

Recommended resources

Updated: