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)