Implementar cifrado por envoltorio (Wireguard) y proteger conexiones NFS entre un servidor y su(s) cliente(s).
Objetivos:
NFSv4 puro y limpio (solo puerto 2049).
Acceso exclusivo a través del túnel WireGuard (cifrado punto a punto).
Imposible acceder a los recursos desde la LAN física (bloqueado por PF).
Montajes persistentes y estables en el cliente.
Todo seguro, mínimo y sin componentes innecesarios corriendo.
Cómo implementar el cifrado por envoltorio: WireGuard
El servidor NFSv4 corre FreeBSD 14.3 ZFS, red 192.168.88.0/24, dominio local.com, nombre de host tormenta.local.com, dirección IP 192.168.88.160, interfaz re0, cortafuegos PF.
El cliente es un portátil con sistema operativo FreeBSD 14.3 ZFS, dirección IP 192.168.88.51, nombre de host solaris.local.com, interfaz de red em0. El archivo /etc/exports actual
V4: /nfsv4 -tls
/nfsv4/dellhome -maproot=root -network 192.168.88.0/24
/nfsv4/poolrecovery -maproot=root -network 192.168.88.0/24
/nfsv4/docs -maproot=root -network 192.168.88.0/24
/nfsv4/confsolaris -maproot=root -network 192.168.88.0/24
/nfsv4/conftormenta -maproot=root -network 192.168.88.0/24
root@tormenta:~ # zfs list | grep /nfsv4
zroot/nfsv4 103G 212G 7.80G /nfsv4
zroot/nfsv4/confsolaris 283M 212G 4.76M /nfsv4/confsolaris
zroot/nfsv4/conftormenta 916K 212G 852K /nfsv4/conftormenta
zroot/nfsv4/dellhome 56.6G 212G 56.6G /nfsv4/dellhome
zroot/nfsv4/docs 7.80G 212G 7.80G /nfsv4/docs
zroot/nfsv4/poolrecovery 1.88G 212G 1.88G /nfsv4/poolrecovery
La idea es crear un túnel VPN punto a punto entre el servidor (tormenta.local.com, 192.168.88.160) y el cliente (solaris.local.com, 192.168.88.51). De esta forma, el tráfico NFS se enruta exclusivamente a través del túnel encriptado de WireGuard, y configurar el servidor NFS para que solo escuche en la IP del túnel (evitando accesos directos por la LAN).
Forzar el cifrado para NFS, ya que ambos hosts están en la misma red LAN (192.168.88.0/24). Usar IPs privadas para el túnel: 10.66.66.1/32 para el servidor y 10.66.66.2/32 para el cliente. No es necesario forwarding ni NAT, ya que es un túnel simple para cifrar el tráfico local.
Verificar que no haya conflictos con reglas PF existentes. Si se usa NFSv4 puro, no se necesita rpcbind ni mountd, lo que simplifica las cosas
Detener servicios TLS
root@tormenta:/home/carlos # service tlsservd status
tlsservd is running as pid 1278.
root@tormenta:/home/carlos # service tlsservd stop
Stopping tlsservd.
Waiting for PIDS: 1278.
root@solaris:~# service tlsclntd status
tlsclntd is running as pid 1596.
root@solaris:~# service tlsclntd stop
Stopping tlsclntd.
Waiting for PIDS: 1596, 1596.
Desactivar TLS
root@tormenta:/home/carlos # sysrc tlsservd_enable="NO"
tlsservd_enable: YES -> NO
root@solaris:~# sysrc tlsclntd_enable="NO"
tlsclntd_enable: YES -> NO
Instalar wireguard-tools en el servidor tormenta y el cliente solaris
pkg install wireguard-tools
sysrc wireguard_enable="YES"
sysrc wireguard_interfaces="wg0"
En el servidor tormenta Generar claves privadas y públicas
mkdir /usr/local/etc/wireguard
cd /usr/local/etc/wireguard
umask 077
wg genkey | tee server_priv.key | wg pubkey > server_pub.key
wg genkey | tee client_priv.key | wg pubkey > client_pub.key
ls -l /usr/local/etc/wireguard
-rw------- 1 root wheel 45 Dec 11 18:28 client_priv.key
-rw-r--r-- 1 root wheel 45 Dec 11 18:28 client_pub.key
-rw------- 1 root wheel 45 Dec 11 18:27 server_priv.key
-rw-r--r-- 1 root wheel 45 Dec 11 18:27 server_pub.key
Esto genera claves para servidor y cliente
Transferir las claves al cliente solaris (IP 192.168.88.51), usando scp
scp root@tormenta:/usr/local/etc/wireguard/client_priv.key /usr/local/etc/wireguard/client_priv.key
scp root@tormenta:/usr/local/etc/wireguard/client_pub.key /usr/local/etc/wireguard/client_pub.key
Asegurar permisos estrictos (chmod 600 *.key)
cd /usr/local/etc/wireguard/
chmod 600 *.key
ls -l /usr/local/etc/wireguard/
-rw------- 1 root wheel 45 Dec 11 18:30 client_priv.key
-rw------- 1 root wheel 45 Dec 11 18:31 client_pub.key
Configurar WireGuard en el servidor tormenta
Archivo /usr/local/etc/wireguard/wg0.conf
###########################################
[Interface]
Address = 10.66.66.1/32
PrivateKey = <contenido de server_priv.key>
ListenPort = 51820
[Peer]
PublicKey = <contenido de client_pub.key>
AllowedIPs = 10.66.66.2/32
Reemplazar las claves por las claves reales
[Interface]
Address = 10.66.66.1/32
PrivateKey = oPtSkHSk8zNe9d3Z6gExZro9Cf8BELoahgZDsrc/7mk=
ListenPort = 51820
# conexion con clientes
[Peer]
PublicKey = tuu6a6EAPrQTC0tPatWJx2qcS6fEPcg4Z0u/8vY1pTs=
AllowedIPs = 10.66.66.2/32
###########################################
Habilitar e iniciar el servicio
sysrc wireguard_interfaces="wg0"
sysrc wireguard_enable="YES"
service wireguard start
|
Acvitar desactivar interfaz wg0
wg-quick up wg0
wg-quick down wg0
Comprobar con wg show, debería mostrar la interfaz wg0 levantada
root@tormenta:/home/carlos # wg show
interface: wg0
public key: UYbDhuNsq0geqJdsVKThSnNLzdc2oHWgUT8Gb0G3VXU=
private key: (hidden)
listening port: 51820
peer: tuu6a6EAPrPTC0tPatWJx2qcS6fEPcg4Z0u/8vY1pTs=
allowed ips: 10.66.66.2/32
Configurar WireGuard en el cliente (solaris)
Archivo /usr/local/etc/wireguard/wg0.conf
###########################################
[Interface]
Address = 10.66.66.2/32
PrivateKey = <contenido de client_priv.key>
[Peer]
PublicKey = <contenido de server_pub.key>
AllowedIPs = 10.66.66.1/32
Endpoint = 192.168.88.160:51820
Reemplazar con las claves reales
[Interface]
Address = 10.66.66.2/32
PrivateKey = sAvZnwh/8xrLLNTxBu75UfvUPB/AhFQTJsUGqYTfUU0=
[Peer]
PublicKey = UYbDhuNsq0geqWdsVKXhSnNLzdc2oHWgUT8Zb0G3VXU=
AllowedIPs = 10.66.66.1/32
Endpoint = 192.168.88.160:51820
###########################################
sysrc wireguard_interfaces="wg0"
sysrc wireguard_enable="YES"
service wireguard start
Levantar interfaz wg0
# wg-quick up wg0
[#] ifconfig wg create name wg0
[#] wg setconf wg0 /dev/stdin
[#] ifconfig wg0 inet 10.66.66.2/32 alias
[#] ifconfig wg0 mtu 1420
[#] ifconfig wg0 up
[#] route -q -n add -inet 10.66.66.1/32 -interface wg0
[+] Backgrounding route monitor
Comprobar wg show debería mostrar el peer conectado.
wg show
interface: wg0
public key: tuu6a6EAPrPTC0tPatWJx2qcS6fEPcg4Z0u/8vY1pTs=
listening port: 26481
peer: UYbDhuNsq0geqWdsVKThSnNLzdc2oHWgUT8Gb0G3VXU=
endpoint: 192.168.88.160:51820
allowed ips: 10.66.66.1/32
Reglas cortafuegos PF (tormenta)
# Regla PF Wireguard antes Anti-spoofing + bogons - permite solo desde el cliente
pass in quick on $lan_if proto udp from 192.168.88.51 to 192.168.88.160 port 51820 keep state
# permite todo dentro del tunel
pass in quick on wg0 keep state # <-- esta es la importante
<-p>
Recargar PF
service pf reload
pfctl -f /etc/pf.conf
Verificar ping, desde el cliente solaris
root@solaris:~# ping 10.66.66.1
PING 10.66.66.1 (10.66.66.1): 56 data bytes
64 bytes from 10.66.66.1: icmp_seq=0 ttl=64 time=0.463 ms
64 bytes from 10.66.66.1: icmp_seq=1 ttl=64 time=0.524 ms
--- 10.66.66.1 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.463/0.494/0.524/0.031 ms
Verificar ping, desde el servidor tormenta
root@tormenta:~# ping -c2 10.66.66.2
PING 10.66.66.2 (10.66.66.2): 56 data bytes
64 bytes from 10.66.66.2: icmp_seq=0 ttl=64 time=0.429 ms
64 bytes from 10.66.66.2: icmp_seq=1 ttl=64 time=0.526 ms
--- 10.66.66.2 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.429/0.478/0.526/0.049 ms
ping bidireccional exitoso.
Configurar el cortafuegos PF en el servidor (tormenta)
Para permitir el tráfico WireGuard (UDP puerto 51820), agregar reglas a /etc/pf.conf. Asume que ya tienes PF configurado.
pass in quick on re0 proto udp from 192.168.88.51 to 192.168.88.160 port 51820 keep state
Recargar PF
service pf reload
pfctl -p /etc/pf.conf
Configurar NFSv4 para que solo escuche en la IP del túnel
En el servidor (tormenta), asegurar NFSv4 puro en /etc/rc.conf
nsf_server_enable="YES"
nfsv4_server_enable="YES"
nfs_server_flags="-h 10.66.66.1" # Bind sólo a la IP del túnel
nfsuserd_enable="YES" # necesario si se usa dominio
Iniciar servicios
service nfsd start
service nfsuserd start
Es es para que nfsd sólo escuche en 10.66.66.1, (no en 192.168.88.160).
Deshabilitar rpcbind_enable y mountd_enable, no son necesarios para NFSv4 puro.
Realizar los cambios en /etc/exports para restringir accesos sólo al cliente del túnel
(cambiar de red a host -network a -host).
############################################
V4: /nfsv4
/nfsv4/dellhome -maproot=root 10.66.66.2
/nfsv4/poolrecovery -maproot=root 10.66.66.2
/nfsv4/docs -maproot=root 10.66.66.2
/nfsv4/confsolaris -maproot=root 10.66.66.2
/nfsv4/conftormenta -maproot=root 10.66.66.2
############################################
Esto permite solo desde 10.66.66.2 (el cliente vía túnel).
Reiniciar NFS y nfsuserd
service nfsd restart
service nfsuserd restart
Montar NFS en el cliente (solaris) a través del tŕunel
En el cliente montar usando la IP del túnel del servidor, por ejemplo:
mount -t nfs -o nfsv4 10.66.66.1:/docs /mnt/docs
Para montajes persistentes, se utiliza /etc/fstab
10.66.66.1:/poolrecovery /nisc4 nfs rw,nfsv4,noatime,bg 0 0
10.66.66.1:/confsolaris /nics4 nfs rw,nfsv4,noatime,bg 0 0
10.66.66.1:/conftormenta /nits4 nfs rw,nfsv4,noatime,bg 0 0
10.66.66.1:/docs /nids4 nfs rw,nfsv4,noatime,bg 0 0
10.66.66.1:/dellhome /nihs4 nfs ro,nfsv4,noatime,bg 0 0
Consideraciones adicionales
Asi queda el archivo /etc/rc.conf (solaris)
$ gsed -e '/^#/d' -e '/^\s*$/d'
syslogd_flags="-ss"
clear_tmp_enable="YES"
hostname="solaris.local.com"
gateway_enable="NO"
ifconfig_em0="inet 192.168.88.51 netmask 255.255.255.0"
defaultrouter="192.168.88.1"
background_dhclient=YES
defaultroute_delay=3
defaultroute_carrier_delay=3
sshd_enable="NO"
ntpd_enable="YES"
ntpd_sync_on_start="YES"
powerd_enable="YES"
powerd_flags="-n hadp"
loader_conf_files="/boot/loader.conf /boot/loader.conf.local"
boot_efi_enable="YES"
webcamd_enable="YES"
moused_port=/dev/ums0
moused_enable="YES"
dumpdev="NO"
zfs_enable="YES"
dbus_enable="YES"
kld_list="i915kms"
nfs_client_enable="YES"
nfsuserd_enable="YES"
nfsv4_client_enable="YES"
nfsv4_domain="local.com"
tlsclntd_enable="NO"
hostid_enable="YES"
hostid_file="/etc/hostid"
autofs_enable="YES"
devd_enable="YES"
devfs_system_ruleset="system"
pf_enable="NO"
pf_rules="/etc/pf.conf"
pflog_enable="YES"
pflog_logfile="/var/log/pflog"
pflog_flags=""
wireguard_enable="YES"
wireguard_interface="wg0"
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
lightdm_enable="YES"
zerotier_enable="YES"
vm_dir="zfs:zroot/vm"
vm_list=""
vm_delay="3"
poudriered_enable="YES"
nginx_enable="YES"
memcached_enable="YES"
memcached_flags="-l localhost -m 8192"
jackett_enable="YES"
linux_enable="NO"
ubuntu_enable="YES"
Asi queda /etc/pf.conf
lan_if = "re0"
lan_net = "192.168.88.0/24"
lan_ip = "192.168.88.160"
ssh_port = "{ 22 }"
vpn_if="wg0"
set block-policy return # responde ICMP/TCP-RST a paquetes bloqueados
set ruleset-optimization none # en FreeBSD 14 no usa "basic" ni "high-latency"
set skip on lo0
match in all scrub (no-df random-id max-mss 1440)
table <bruteforce> persist # aquí meterá sshguard, pf-badhost, etc.
pass in quick on $lan_if from $lan_net to $lan_ip keep state
pass in quick on $lan_if proto udp from 192.168.88.51 to 192.168.88.160 port 51820 keep state
pass in quick on wg0 keep state # <-- esta es la importante
block in quick on ! $lan_if from $lan_net to any
block in quick on ! $lan_if from 192.168.0.0/16 to any
block in quick on ! $lan_if from 172.16.0.0/12 to any
block in quick on ! $lan_if from 10.0.0.0/8 to any
block in quick from any to { 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, ::/128, 0.0.0.0/8 }
block in quick from <bruteforce>
pass quick on lo0all
pass out quick inet keep state
pass in on $lan_if inet proto icmp from $lan_net to $lan_ip icmp-type echoreq keep state
pass out on $lan_if inet proto icmp from $lan_ip to $lan_net icmp-type echorep keep state
pass in on $lan_if proto tcp from $lan_net to $lan_ip port $ssh_port flags S/SFRA keep state
pass quick on $lan_if proto { tcp udp } from $lan_net to $lan_ip port 53
pass out proto udp from any to port 123 keep state
block log all
Asi queda /etc/exports
$ gsed -e '/^#/d' -e '/^\s*$/d' /etc/exports
V4: /nfsv4
/nfsv4/dellhome -maproot=root 10.66.66.2
/nfsv4/poolrecovery -maproot=root 10.66.66.2
/nfsv4/docs -maproot=root 10.66.66.2
/nfsv4/confsolaris -maproot=root 10.66.66.2
/nfsv4/conftormenta -maproot=root 10.66.66.2
Commprobaciones finales
En el servidor tormenta no debe aparecer rpcbind ni statd
root@tormenta:/etc # ps aux | grep -E "(statd|rpcbind|nfs)"
root 1192 0.0 0.0 14140 2380 - Is 09:34 0:00.00 nfsuserd: master (nfsuserd)
root 1194 0.0 0.0 14140 2384 - I 09:34 0:00.02 nfsuserd: server (nfsuserd)
root 1195 0.0 0.0 14140 2384 - S 09:34 0:00.02 nfsuserd: server (nfsuserd)
root 1196 0.0 0.0 14140 2380 - I 09:34 0:00.00 nfsuserd: server (nfsuserd)
root 1197 0.0 0.0 14140 2384 - I 09:34 0:00.01 nfsuserd: server (nfsuserd)
root 2398 0.0 0.0 13756 2472 - Ss 12:23 0:00.01 nfsd: master (nfsd)
root 2399 0.0 0.0 13756 2756 - S 12:23 0:00.08 nfsd: server (nfsd)
En el cliente solaris remontar directorios
root@solaris:~# umount /nisc4 /nics4 /nits4 /nids4 /nihs4
root@solaris:~# mount -a
root@solaris:~# mount | grep 10.66.66.1
10.66.66.1:/poolrecovery on /nisc4 (nfs, noatime, nfsv4acls)
10.66.66.1:/confsolaris on /nics4 (nfs, noatime, nfsv4acls)
10.66.66.1:/conftormenta on /nits4 (nfs, noatime, nfsv4acls)
10.66.66.1:/docs on /nids4 (nfs, noatime, nfsv4acls)
NFSv4 puro, solo puerto 2049 a través del túnel WireGuard, sin dependencias de rpcbind ni demonios extra.
FreeBSD es genial!.