In this post, I'll explain how to securely configure NFS on Debian, to mount a directory from one server on another machine. A good use case for this is if you have a storage VPS with a large amount of storage, and want to use this space from other servers.
Security
NFS is unencrypted by default. It can be encrypted if you use Kerberos, but I wouldn't recommend going through the pain of configuring Kerberos unless you're setting up a corporate network with dozens of users.
Because of this, I would recommend never exposing an NFS server directly to the internet. I'd also advise against exposing it on "internal" networks which are not isolated per customer, such as what HostHatch provides. On isolated private networks (like what BuyVM provides), it's fine to use NFS unecrypted.
To secure NFS connections over the internet or other untrusted network, I'd recommend using WireGuard. There are various guides on how to configure WireGuard (like this one) so I won't go into it in too much detail. Note that WireGuard does not have the concept of a "client" and "server" like classic VPN solutions like OpenVPN. Each node is a "peer", and the overall topology is up to you. For example, you can have a "mesh" VPN network where every machine can directly access every other machine, without a central server.
On Debian 11 (Bullseye, testing) you can simply use apt install wireguard
to get WireGuard. On Debian 10 (Buster), you'll have to enable buster-backports then do apt -t buster-backports install wireguard
.
Generate a private and public key on each system:
wg genkey | tee privatekey | wg pubkey > publickey
Then configure /etc/wireguard/wg0.conf
on each system. The [Interface]
section should have the private key for that particular system. The NFS server should have a [Peer]
section for each system that is allowed to access the NFS server, and all the other systems should have a [Peer]
section for the NFS server. It should look something like this:
[Interface]
Address = 10.123.0.2
PrivateKey = 12345678912345678912345678912345678912345678
ListenPort = 51820
[Peer]
PublicKey = 987654321987654321987654321987654321987654321
AllowedIPs = 10.123.0.1/32
Endpoint = 198.51.100.1:51820
where 10.123.0.1
and 10.123.0.2
can be any IPs of your choosing, as long as they're in the same subnet and in one of the IP ranges reserved for local networks (10.x.x.x is usually a good choice).
Enable and start the WireGuard service on each machine:
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
Run wg
to check that it's running. Make sure you can ping
the NFS server from the other servers.
NFS Server
On the NFS server, install the nfs-kernel-server
package:
apt install nfs-kernel-server
A best practice these days is to only enable NFSv4 unless you really need NFSv3. To only enable NFSv4, set the following variables in /etc/default/nfs-common
:
NEED_STATD="no"
NEED_IDMAPD="yes"
And the following in /etc/default/nfs-kernel-server
. Note that RPCNFSDOPTS
is not present by default, and needs to be added.
RPCNFSDOPTS="-N 2 -N 3 -H 10.123.0.1"
RPCMOUNTDOPTS="--manage-gids -N 2 -N 3"
10.123.0.1
should be the IP address the NFS server will listen on (the WireGuard IP).
Additionally, rpcbind is not needed by NFSv4 but will be started as a prerequisite by nfs-server.service. This can be prevented by masking rpcbind.service and rpcbind.socket:
systemctl mask rpcbind.service
systemctl mask rpcbind.socket
Next, configure your NFS exports in /etc/exports
. For example, this will export the /data/hello-world
directory and only allow 10.123.0.2
to access it:
/data/hello-world 10.123.0.2(rw,sync,no_subtree_check)
Refer to the exports(5) man page for more details.
Finally, start the NFS server:
systemctl start nfs-server
NFS Client
On the NFS client, you need to install the nfs-common
package:
apt install nfs-common
Now, you can use the mount
command to mount the directory over NFS:
mkdir -p /mnt/data/
mount -t nfs4 -o vers=4.2,async 10.123.0.1:/data/hello-world /mnt/data/
Try write some files to /mnt/data
, and it should work!
To automatically mount the directory on boot, modify /etc/fstab
:
10.123.0.1:/data/hello-world /mnt/data nfs4 auto,vers=4.2
Optional: Caching
You can optionally cache data from the NFS server on the local disk by using a transparent read-through cache called CacheFS
. The first time files are read via NFS, they will be cached locally. On subsequent reads, if the file has not been modified since the time it was cached, it will be read from the local cache rather than loading over the network. This can provide a significant performance benefit if the NFS server has slower disks and/or is physically distant from the clients.
To enable caching, first install cachefilesd
:
apt install cachefilesd
Turn it on by editing /etc/default/cachefilesd
, following the instructions in the file:
# You must uncomment the run=yes line below for cachefilesd to start.
# Before doing so, please read /usr/share/doc/cachefilesd/howto.txt.gz as
# extended user attributes need to be enabled on the cache filesystem.
RUN=yes
Modify your NFS mount in /etc/fstab
to add the fsc
(file system cache) attribute. For example:
10.123.0.1:/data/hello-world /mnt/data nfs4 auto,vers=4.2,fsc
Finally, start the service and remount your directory:
systemctl start cachefilesd
mount -o remount /mnt/data
To check that it's working, read some files from the mount, and you should see /var/cache/fscache/
growing in size:
du -sh /var/cache/fscache/
76K /var/cache/fscache/
By default, the cache will keep filling up until the disk only has 7% space left. Once the disk drops below 7% free space. If the disk space drops below 3%, caching will be turned off entirely. You can change these thresholds by modifying /etc/cachefilesd.conf
.