Als je bestanden op je Lan deelt tussen Linux-machines, doe je dat normaal met NFS. Op een thuisnetwerk voegen velen hier geen beveiliging aan toe, omdat de klassieke manier met Kerberos hen afschrikt. Er is een eenvoudiger alternatief: beveilig het NFS-verkeer met een WireGuard-tunnel.

Door: Koen Vervloesem

Heb je thuis een NFS-server draaien om bestanden van je Linux-server in je lan te delen, dan ben je waarschijnlijk begonnen zonder enige encryptie of authenticatie. Misschien vertrouw je je thuisnetwerk voldoende om alles in cleartext te delen, maar toch raden we dat niet aan.

Beveilig je NFS-server

In bedrijfsomgevingen wordt NFS klassiek met Kerberos beveiligd, maar voor thuisgebruikers is dat wellicht wat te complex. Gelukkig bestaat er ook een eenvoudigere manier, dankzij het lichtgewicht VPN-systeem WireGuard (https://www.wireguard.com): maak voor elke verbinding tussen client en server een VPN-tunnel aan, waarover het NFS-verkeer versleuteld verloopt.

WireGuard werkt met authenticatie via sleutelparen. Elke client heeft een privésleutel en publieke sleutel, en de VPN-server kent de publieke sleutel van de client. Tegelijk kent de VPN-server aan elke client een IP-adres in het virtuele netwerk toe. Als de vpn-server een netwerkpakket van dat IP-adres kan decrypteren met de publieke sleutel van de client, weet hij dat dit was versleuteld met de overeenkomende privésleutel. Daarmee is de client dus geauthentiseerd. Je kunt dan in je NFS-server de toegang tot een netwerkshare beperken tot het IP-adres van de client op het VPN, want je bent zeker dat alleen de juiste client met dit IP-adres verbonden is.

In de rest van dit artikel tonen we je hoe je op een Ubuntu 20.04-server een NFS-server opzet, en hoe je vanaf een Ubuntu 20.04-client via een versleutelde WireGuard-tunnel toegang tot de bestanden op je NFS-server krijgt.

Installeer NFS op de server

Installeer eerst op de servermachine de NFS-server:

server $ sudo apt install nfs-kernel-server

Bekijk dan met man exports hoe je in het bestand /etc/exports aangeeft hoe je directory’s deelt. Wil je eenvoudigweg een hele directory met iedereen in je netwerk delen met zowel lees- als schrijfrechten, zet hierin dan het volgende:

/mnt/data *(rw)

Maak deze directory ook aan, maak ze eigendom van je normale gebruiker en exporteer ze via NFS:

server $ sudo mkdir /mnt/data
server $ sudo chown koan:koan /mnt/data/
server $ exportfs -a

Installeer NFS op de client

Dan dien je nu op de client nfs-ondersteuning toe te voegen. Afhankelijk van je desktopomgeving kan dat standaard al geïnstalleerd zijn, en misschien zelfs met integratie in de grafische omgeving, maar we installeren in dit voorbeeld de commandlinebasisprogramma’s:

client $ sudo apt install nfs-common

Als je nu toegang wilt tot het bestandssysteem /mnt/data op je server, maak je een directory aan en koppel je het bestandssysteem daarop aan via NFS:

client $ sudo mkdir /mnt/server
client $ sudo mount -t nfs server:/mnt/data /mnt/server

Je toegang tot de gedeelde bestanden wordt bepaald door de permissies van de bestanden op de server, en dus ook van je gebruikers-ID. De meeste Linux-distributies geven de eerst gemaakte gebruiker ID 1000. Het beste is dus dat je zowel op je server als op je clients gebruikers aanmaakt met hetzelfde ID. Als je dan op je server die gebruiker eigenaar maakt van bestanden, krijgt diezelfde gebruiker op de client volledige toegang tot die bestanden via NFS.

Als dit werkt, kun je er ook voor zorgen dat het bestandssysteem automatisch via NFS aangekoppeld wordt nadat je clientmachine is opgestart. Voeg daarvoor de volgende regel toe aan /etc/fstab op de client:

server:/mnt/data /mnt/server nfs auto 0 0

Schakel over naar NFSv4

Standaard ondersteunt de NFS-server NFS-versies 2, 3 en 4. Maar als we de toegang tot de server nu verder gaan beveiligen, kunnen we het beter zo eenvoudig mogelijk maken en alleen versie 4 ondersteunen. Verander daarvoor de volgende variabelen in het bestand /etc/default/nfs-common op de server:

NEED_STATD="no"
NEED_IDMAPD="yes"

En ook de volgende variabelen in /etc/default/nfs-kernel-server:

RPCNFSDOPTS="-N 2 -N 3"
RPCMOUNTDOPTS="--manage-gids -N 2 -N 3"

De eerste variabele dien je zelf toe te voegen, van de tweede dien je de waarde te veranderen. Daarna schakelen we rpcbind uit en stoppen deze, want die hebben we niet meer nodig als we alleen maar NFSv4 gebruiken:

server $ sudo systemctl mask rpcbind.service
server $ sudo systemctl stop rpcbind.service
server $ sudo systemctl mask rpcbind.socket
server $ sudo systemctl stop rpcbind.socket

En herstart de nfs-server:

server $ sudo systemctl restart nfs-server

Ontkoppel voordat je verdergaat eerst de NFS-share op de client:

client $ sudo umount /mnt/server

Als je de share weer opnieuw aankoppelt op de client, zou je nog altijd toegang tot je NFS-share moeten hebben. Het enige verschil is dat de communicatie nu alleen poort 2049 nodig heeft voor NFSv4.

Installeer WireGuard en genereer sleutels

WireGuard is aanwezig in de standaardrepository van Ubuntu 20.04, dus de installatie is eenvoudig:

$ sudo apt install wireguard

De kernelmodule wireguard wordt niet automatisch geladen, dus doe dat alsnog:

$ sudo modprobe wireguard

Creëer nu een sleutelpaar. Stel daarvoor eerst een strikte umask in zodat alleen de eigenaar de bestanden kan lezen, schrijven en uitvoeren:

$ umask 077
$ wg genkey > privatekey
$ wg pubkey < privatekey > publickey

Nadat je dit alles op de server hebt gedaan, doe je exact hetzelfde op elke client. WireGuard kent eigenlijk niet het concept van een server en een client: elke machine in het VPN is gewoon een peer met zijn eigen sleutelpaar. Het enige wat een peer een server maakt in onze ogen, is de configuratie.

Configureer de server

Creëer op je server een configuratiebestand /etc/wireguard/wg-nfs.conf met de volgende configuratie van je VPN:

[Interface]
PrivateKey = PRIVATE_KEY_VAN_SERVER 
Address = 10.10.0.1/24
ListenPort = 51820
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; iptables -A FORWARD -m conntrack --ctstate NEW -s 10.10.0.1/24 -m policy --pol none --dir in -j ACCEPT; iptables -t nat -A POSTROUTING -s 10.10.0.1/24 -m policy --pol none --dir out -j MASQUERADE; iptables -A INPUT -p udp --dport 51820 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = PUBLIC_KEY_VAN_CLIENT
AllowedIPs = 10.10.0.2/32

De sectie Interface beschrijft de kant van je server. Aan de variabele PrivateKey ken je de inhoud van het bestand privatekey toe dat je eerder op je server genereerde. Je kent hier ook het adres toe dat je server op je vpn krijgt. PostUp en PostDown zijn de firewallopdrachten om uit te voeren nadat je virtuele netwerkinterface up respectievelijk down is. Verander hierin eth0 door de netwerkinterface van je server.

In de sectie Peer zet je de informatie voor de client. Aan PublicKey ken je de inhoud toe van het bestand publickey dat je op de client genereerde. Verder ken je ook het adres toe dat je client op je VPN krijgt. Door de combinatie van de publieke sleutel en het IP-adres in deze sectie, heeft je NFS-server de waarborg dat elke toegang van IP-adres 10.10.0.2 van de client komt met de privésleutel die aan de publieke sleutel in deze configuratie verbonden is.

Als je nu extra clients nodig hebt, voeg je gewoon voor elke client een sectie Peer toe met een publieke sleutel en IP-adres.

Configureer de client

Maak nu ook op de client een configuratiebestand /etc/wireguard/wg-nfs.conf aan. Dit is eenvoudiger:

[Interface]
Address = 10.10.0.2/24
PrivateKey = PRIVATE_KEY_VAN_CLIENT

[Peer]
PublicKey = PUBLIC_KEY_VAN_SERVER
AllowedIPs = 10.10.0.0/24
Endpoint = IPADRES:51820

Hier definieer je in de sectie Interface alleen het IP-adres van de client op het VPN en zijn privésleutel (de inhoud van het bestand privatekey op de client). In de sectie Peer voeg je de publieke sleutel van je server toe (het bestand publickey op de server). Met AllowedIPs vertel je WireGuard op de client om alle verkeer naar het subnet 10.10.0.0/24 naar deze server te forwarden. In Endpoint vul je het IP-adres van je server in op je lokale netwerk (dus op je eth0 of hoe je netwerkkaart heet), samen met de standaardpoort 51820.

Heb je meerdere clients, maak dan op elke client zo’n bestand aan.

Automatische tunnel

Dan laten we nu nog de zojuist geconfigureerde WireGuard-tunnel op zowel de server als de client automatisch opstarten. Schakel op beide machines WireGuard in met je configuratie voor NFS, en start de tunnel:

$ sudo systemctl enable wg-quick@wg-nfs.service
$ sudo systemctl start wg-quick@wg-nfs.service

Je zou nu van de client (10.10.0.2) naar de server (10.10.0.1) moeten kunnen pingen via het VPN, en andersom.

Beperk de toegang tot NFS

Dan gaan we nu de NFS-server dichttimmeren zodat hij alleen maar via het VPN bereikbaar is. Open op de server het configuratiebestand van de NFS-server:

server $ sudo nano /etc/default/nfs-kernel-server

En voeg het IP-adres van de WireGuard-interface toe aan de variabelen RPCNFSDOPTS en RPCMOUNTDOPTS:

RPCNFSDOPTS="-N 2 -N 3 -H 10.10.0.1"
RPCMOUNTDOPTS="--manage-gids -N 2 -N 3 -H 10.10.0.1"

Herstart tot slot de NFS-server:

server $ sudo systemctl restart nfs-kernel-server.service

Vanaf nu is de NFS-server alleen bereikbaar via het VPN.

Automatisch aankoppelen

Vervang tot slot op je client de regel in /etc/fstab om de share automatisch aan te koppelen door:

10.10.0.1:/mnt/data /mnt/server nfs noauto,x-systemd.automount,x-systemd.requires=wg-quick@wg-nfs.service 0 0

Herlaad nu je systemd-configuratie en herstart het target remote-fs dat het aankoppelen van bestandssystemen afhandelt:

client $ sudo systemctl daemon-reload
client $ sudo systemctl restart remote-fs.target

Zodra je nu je NFS-share benadert (bijvoorbeeld door een ls /mnt/server), wordt deze dankzij de regel in /etc/fstab automatisch aangekoppeld, en dat volledig versleuteld over het VPN tussen je client en server.

En verder

Verdere beperkingen kun je nu eenvoudig op basis van het IP-adres van de client opleggen, want dankzij WireGuard ben je er zeker van wie welk IP-adres heeft. Zet bijvoorbeeld op je server in /etc/exports het volgende:

/mnt/data *(ro)
/mnt/data 10.11.0.3(rw)

Als je nu de directory herexporteert met sudo exportfs -ra, zie je dat ze voor de ‘wereld’ (in dit geval dus alleen het vpn) read-only beschikbaar is, en voor 10.11.0.3 leesbaar en beschrijfbaar.