Securing NFSv4 with SSH port forwarding
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.
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
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_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
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_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 - 126.96.36.199. 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
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:
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,
-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
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
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
getfacl to include functionality for NFSv4 ACLs.
The illumos ZFS Administration Guide has more details on NFSv4 ACLs.
NFSv2 and NFSv3 comprised several separate daemons:
- nfsd (
- rpcbind (
- mountd (
- statd (
- lockd (
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 ... # 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]#
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
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 V4: /zdata/photos/family/ -sec=sys 127.0.0.1 /zdata/photos/family/lightroom -mapall=1088 127.0.0.1
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,
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
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]#
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
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 firstname.lastname@example.org 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 ~ %
-N switch instructs ssh to not execute a remote command. The
-f option backgrounds ssh.
-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 %
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
A mitigation technique is to add
~/.ssh/authorized_keys for all users who should not be able to mount NFS shares.
More information in man page
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.
Chapter 58, Network file and resource sharing and the TCP/IP network file system (NFS)
The TCP/IP Guide: A Comprehensive, Illustrated Internet Protocols Reference 1st Edition by Charles M. Kozierok
The Linux NFS NOWTO - 6. Security and NFS (Published: 2002-07-16)