Páginas

martes, 16 de diciembre de 2025

Cifrado por Envoltorio (túnel) del tráfico NFSv4 con Wireguard FreeBSD 14.3

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!.

sábado, 13 de diciembre de 2025

NFS sobre TLS FreeBSD 14.3

Configurar Cifrado NFS sobre TLS


El tráfico NFS está cifrado. Autenticación sigue siendo AUTH_SYS (UID/GID)

NFS sobre TLS proporciona cifrado en tránsito (cifrado del tráfico RPC/TLS) sin necesidad de Kerberos. Utiliza certificados X.509 (pueden ser auto-firmados para entornos locales).

Preparación (en el servidor y cliente) Resolución DNS correcta (tormenta.local.com -> 192.168.88.160)
El kernel en FreeBSD 14.3 soporta KTLS (está en Generic desde FreeBSD 13+)


En ambos dos editar /etc/rc.conf para habilitar los daemons TLS:

tlsservd_enable="YES"  # Demonio servidor TLS (necesario para mounts TLS)
tlsclntd_enable="YES"  # Demonio cliente TLS


En el servidor tormenta.local.com - 192.168.88.160


Instalar y configurar Unbound en el servidor


Generación y Colocación de Certificados


Generar los certificados y moverlos a la carpeta que tlsservd exige por defecto.

Crear directorio temporal para trabajar

mkdir -p /root/certs-work
cd /root/certs-work

Crear CA

openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/CN=HomeLabRootCA"

Crear Certificado Servidor (tormenta)

openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr -subj "/CN=tormenta.local.com"
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

Crear Certificado Cliente (solaris) - se usará luego

openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr -subj "/CN=solaris.local.com"
openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 02 -out client.crt

Instalacion en rutas por defecto del servidor


Crear el directorio

mkdir /etc/rpc.tlsservd

Copiar archivos con los nombres que FreeBSD espera:

cp server.crt /etc/rpc.tlsservd/cert.pem
cp server.key /etc/rpc.tlsservd/certkey.pem
cp ca.crt     /etc/rpc.tlsservd/cacert.pem

Crear lista de revocación vacía (necesaria por defecto)

touch /etc/rpc.tlsservd/crl.pem

Permisos estrictos

chmod 700 /etc/rpc.tlsservd
chmod 600 /etc/rpc.tlsservd/certkey.pem
chmod 444 /etc/rpc.tlsservd/cert.pem
chmod 444 /etc/rpc.tlsservd/cacert.pem

En el cliente (solaris.local.com) IP 192.168.88.51


Preparar directorios y certificados

mkdir /etc/rpc.tlsclntd

Copiar los archivos generados en tormenta hacia solaris y renombrarlos

scp tormenta.local.com:/root/certs-work/client.crt /etc/rpc.tlsclntd/cert.pem
scp tormenta.local.com:/root/certs-work/client.key /etc/rpc.tlsclntd/certkey.pem
scp tormenta.local.com:/root/certs-work/ca.crt /etc/rpc.tlsclntd/cacert.pem

Aplicar permisos estrictos

chmod 700 /etc/rpc.tlsclntd
chmod 600 /etc/rpc.tlsclntd/certkey.pem
chmod 444 /etc/rpc.tlsclntd/cert.pem
chmod 444 /etc/rpc.tlsclntd/cacert.pem
chmod 644 /etc/rpc.tlsclntd/crl.pem

client.crt Es el certificado público del cliente. Identifica quién es el cliente. client.key Es la clave privada del cliente. Nunca debe compartirse. Sirve para firmar la comunicación. cacert.pem Es el certificado de la Autoridad Certificadora. Sirve para que el cliente verifique que el servidor es confiable.

Es importante saber que cambiar la extensión del archivo no cambia su contenido, pero ayuda al software a entender qué formato esperar.

PEM (Privacy Enhanced Mail): Es el formato estándar de texto (Base64) que empieza con 
-----BEGIN CERTIFICATE-----

Verificar que la Clave Privada y el Certificado coinciden

Este es el paso más crítico. Ambos archivos deben tener el mismo "Módulo" (un identificador matemático único). Si los códigos hash que obtienes son idénticos, son pareja.

Ejecuta estos dos comandos:

# Extraer el módulo del certificado público
openssl x509 -noout -modulus -in cert.pem | openssl md5

# Extraer el módulo de la clave privada
openssl rsa -noout -modulus -in certkey.pem | openssl md5

Resultado esperado: Ambos comandos deben devolver exactamente la misma cadena de caracteres (ej. (stdin)= 68b19a0d...). Si son diferentes, esa clave no pertenece a ese certificado.

Verificar que el Certificado fue firmado por la CA

Ahora comprobaremos la cadena de confianza. Queremos confirmar que cert.pem es "hijo" legítimo de cacert.pem. Ejecutando

openssl verify -CAfile cacert.pem cert.pem
cert.pem: OK

Resultado esperado: Deberías ver un mensaje final que diga: cert.pem: OK

Comprobar validez de las fechas

openssl x509 -noout -dates -in cert.pem
notBefore=Dec  9 20:38:11 2025 GMT
notAfter=Dec  7 20:38:11 2035 GMT

Crear lista de revocación vacía (necesaria por defecto)

touch /etc/rpc.tlsservd/crl.pem

Modificar /etc/exports para requerir TLS

Solo la linea V4: puede tener opciones de seguridad.

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

Aplicar cambios

service nfsuserd restart
service mountd restart
service nfsd restart

Cliente solaris.local.com (192.168.88.51)


Montar con TLS (temporal)

mount -t nfs -o nfsv4,tls tormenta.local.com:/docs /punto/de/montaje

Permanente (en /etc/fstab)

...
# nfs
tormenta:/poolrecovery    /nisc4    nfs     rw,nfsv4,tls,noatime,bg    	0  	0
tormenta:/confsolaris     /nics4    nfs     rw,nfsv4,tls,noatime,bg	0  	0
tormenta:/docs            /nids4    nfs     rw,nfsv4,tls,noatime,bg    	0  	0
tormenta:/dellhome 	  /nihs4    nfs     ro,nfsv4,tls,noatime,bg	0  	0
mount -a

Servidor - configuración de servicios /etc/rc.conf


NFS y RPC

nfs_server_enable="YES"
nfsv4_server_enable="YES"
nfsuserd_enable="YES"

TLS demonio (servidor)

sysrc tlsservd_enable="YES"

Habilitar Kernel TLS

sysctl kern.ipc.tls.enable=1
echo 'kern.ipc.tls.enable=1' >> /etc/sysctl.conf

Recomendación: La seguridad debe ser en capas (Defensa en profundidad): Firewall + Restricción de IP NFS + Certificados TLS.

sysrc statd_enable="YES"   # Para bloqueo de archivos (opcional pero recomendado)
sysrc lockd_enable="YES    # Para bloqueo de archivos 
sysrc mountd_flags="-r"    # Recarga automatica al compartir datasets

Iniciar servicios

service tlsservd start
service nfsuserd restart
service mountd restart
service nfsd restart

Cliente (solaris.local.com) Configuración (/etc/rc.conf)

nfs_client_enable="YES"
nfsv4_client_enable="YES"
nfsuserd_enable="YES"

# TLS demonio (cliente)
tlsclntd_enable="YES"

Iniciar servicios

service nfsclient start
service nfsuserd start

Habilitar Kernel TLS

sysctl kern.ipc.tls.enable=1
echo 'kern.ipc.tls.enable=1' >> /etc/sysctl.conf

Iniciar cliente

service nfsuserd start
service tlsclntd start

Prueba de montaje

mkdir -p /mnt/docs
mount -t nfs -o nfsv4,tls 192.168.88.160:/docs /mnt/docs

Verificar cifrado

root@solaris:~# nfsstat -m
tormenta:/docs on /nids4
nfsv4,minorversion=2,tcp,resvport,tls,nconnect=1,hard,cto,sec=sys,acdirmin=3,acdirmax=60,acregmin=5,acregmax=60,nametimeo=60,negnametimeo=60,rsize=65536,wsize=65536,readdirsize=65536,readahead=1,wcommitsize=16777216,timeout=120,retrans=2147483647

Debe aparecer tls en las banderas (flags)

nfsstat -m
root@solaris:~# nfsstat -m
tormenta:/poolrecovery on /nisc4
nfsv4,minorversion=2,tcp,resvport,tls,nconnect=1,hard,cto,sec=sys,acdirmin=3,acdirmax=60,acregmin=5,acregmax=60,nametimeo=60,negnametimeo=60,rsize=65536,wsize=65536,readdirsize=65536,readahead=1,wcommitsize=16777216,timeout=120,retrans=2147483647

tormenta:/confsolaris on /nics4
nfsv4,minorversion=2,tcp,resvport,tls,nconnect=1,hard,cto,sec=sys,acdirmin=3,acdirmax=60,acregmin=5,acregmax=60,nametimeo=60,negnametimeo=60,rsize=65536,wsize=65536,readdirsize=65536,readahead=1,wcommitsize=16777216,timeout=120,retrans=2147483647

tormenta:/docs on /nids4
nfsv4,minorversion=2,tcp,resvport,tls,nconnect=1,hard,cto,sec=sys,acdirmin=3,acdirmax=60,acregmin=5,acregmax=60,nametimeo=60,negnametimeo=60,rsize=65536,wsize=65536,readdirsize=65536,readahead=1,wcommitsize=16777216,timeout=120,retrans=2147483647

tormenta:/dellhome on /nihs4
nfsv4,minorversion=2,tcp,resvport,tls,nconnect=1,hard,cto,sec=sys,acdirmin=3,acdirmax=60,acregmin=5,acregmax=60,nametimeo=60,negnametimeo=60,rsize=65536,wsize=65536,readdirsize=65536,readahead=1,wcommitsize=16777216,timeout=120,retrans=2147483647

Regla simple:

tlsservd (TLS Server Daemon): Es para la máquina que OFRECE los archivos (el Servidor). Escucha las peticiones de cifrado entrantes.
tlsclntd (TLS Client Daemon): Es para la máquina que MONTA los archivos (el Cliente). Inicia la petición de cifrado hacia afuera.

Verificar con tcpdump que el tráfico NFS sobre TLS está cifrado

Una vez que el montaje NFS con TLS funciona correctamente, se puede confirmar que el tráfico está realmente cifrado ejecutando tcpdump en el servidor o en el cliente.

Comando básico (en servidor)

tcpdump -i re0 -nn port 2049

O más específico, ver sólo NFS

tcpdump -i re0 -nn port 2049 and host 192.168.88.51

Qué esperar

Si TLS está funcionado correctamente - cifrado activo Verás paquetes TCP en el puerto 2049 Después del handshake TLS inicial, todo el payload de los paquetes estará cifrado.

...
13:35:28.349050 IP 192.168.88.160.2049 > 192.168.88.51.773: Flags [P.], seq 469255:469785, ack 251188, win 29128, options [nop,nop,TS val 505937474 ecr 417283551], length 530
13:35:28.349301 IP 192.168.88.51.773 > 192.168.88.160.2049: Flags [.], ack 469785, win 4344, options 
...

No verás texto legible como nombre de archivos, rutas, ni datos de ficheros. Todo será binario cifrado.

Ejemplo típico de texto claro

NFS request xid ... read fh .../poolrecovery/backup-2025-12-10.gz offset 0 count 32768
Comando más detallado para ver el handshake TLS
tcpdump -i re0 -nn -s 0 -A port 2049
-i <interfaz>
-A imprime cada paquete en formato ASCII (crucial para leer contenido)
-n No convierte direcciones IP a nombres de host (más rápido)

En el cliente solaris echo 'Garantizar cifrado' > /nids4/prueba.txt no podrás ver ni el nombre del archivo ni su contenido.

...
14:10:09.830256 IP 192.168.88.160.2049 > 192.168.88.51.890: Flags [P.], seq 1:115, ack 162, win 29128, options [nop,nop,TS val 3871689747 ecr 1810578769], length 114
E....V..@.....X...X3...z.C........q.2......
...
tcpdump -i re0 -nn port 2049 -c 50

Genera activida en el cliente solaris

ls -ltr /nihs4
...
14:12:35.627829 IP 192.168.88.160.2049 > 192.168.88.51.698: Flags [.], ack 230, win 29128, options [nop,nop,TS val 1928012123 ecr 3657311778], length 0
14:12:35.628036 IP 192.168.88.160.2049 > 192.168.88.51.698: Flags [P.], seq 1:303, ack 230, win 
...

Si solo ves números de secuencia y longitudes, sin nombres de archivos -> cifrado Si ves rutas y operaciones NFS -> sin cifrado (TLS inactivo).

FreeBSD es genial!.

viernes, 12 de diciembre de 2025

Unbound DNS Local en FreeBSD 14.3

Configurar Unbound FreeBSD 14.3

Servidor tormenta y cliente solaris /etc/hosts

...
192.168.88.160		tormenta.local.com  tormenta
192.168.88.51		solaris.local.com  solaris
...

Archivo de configuracion /usr/local/etc/unbound/unbound.conf

server:
# Interfaces y puertos
interface: 127.0.0.1       # usar unbound para la resolucion DNS
interface: 192.168.88.160
port: 53
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes

# Seguridad y acceso
access-control: 127.0.0.0/8 allow
access-control: 192.168.88.0/24 allow
access-control: 0.0.0.0/0 refuse

# Cache y rendimiento
cache-min-ttl: 3600
cache-max-ttl: 86400
prefetch: yes
prefetch-key: yes

# Privacidad
hide-identity: yes
hide-version: yes
identity: "DNS Server"
version: " "

# Validación DNSSEC - verificar autenticidad de las respuestas
auto-trust-anchor-file: "/usr/local/etc/unbound/root.key"
val-clean-additional: yes

# Archivos de zona local
include: /usr/local/etc/unbound/local-zone.conf

# Control remoto 
include: /usr/local/etc/unbound/remote-control.conf

# Redirección de consultas
forward-zone:
name: "."
forward-addr: 9.9.9.9 # Quad9 DNS
forward-addr: 8.8.8.8 # Google DNS
carlos@tormenta:~ % cat /usr/local/etc/unbound/local-zone.conf 
# Zona local local.com
local-zone: "local.com." static
local-data: "tormenta.local.com. IN A 192.168.88.160"
local-data: "solaris.local.com. IN A 192.168.88.51"
local-data: "ns.local.com. IN A 192.168.88.160"
local-data: "local.com. IN NS ns.local.com."
carlos@tormenta:~ % cat /usr/local/etc/unbound/remote-control.conf 
# Habilitar interfaz de control
remote-control:
control-enable: yes
control-interface: 127.0.0.1
control-port: 8953
control-use-cert: no
sysrc unbound_enable="YES"
service unbound start
Archivo /etc/resolv.conf (tormenta)
cat /etc/resolv.conf
# Generated by resolvconf
nameserver 127.0.0.1      # 
nameserver 192.168.88.160
domain local.com
search local.com

Archivo /etc/resolv.conf (solaris)

nameserver 192.168.88.160
domain local.com
search local.com

Consultas DNS desde el cliente (solaris.local.com)

dig @tormenta.local.com freebsd.org

; <<>> DiG 9.20.16 <<>> @tormenta.local.com freebsd.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56301
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;freebsd.org.			IN	A

;; ANSWER SECTION:
freebsd.org.		3600	IN	A	96.47.72.84

;; Query time: 58 msec
;; SERVER: 192.168.88.160#53(tormenta.local.com) (UDP)
;; WHEN: Wed Dec 10 14:12:38 CET 2025
;; MSG SIZE  rcvd: 56

Desde el servidor

dig freebsd.org @127.0.0.1
carlos@tormenta:~ % dig freebsd.org @127.0.0.1

; <<>> DiG 9.20.16 <<>> freebsd.org @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38422
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;freebsd.org.			IN	A

;; ANSWER SECTION:
freebsd.org.		3426	IN	A	96.47.72.84

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Fri Dec 12 16:01:03 CET 2025
;; MSG SIZE  rcvd: 56
FreeBSD es genial!.

sábado, 6 de diciembre de 2025

FreeBSD 14.3 Cortafuegos PF

Sintaxis actualizada (FreeBSD 14 usa PF de OpenBSD 6.9–7.0 aproximadamente, hay cambios importantes).

# Macros
lan_if   = "re0"
zt_if    = "zt1ocu1pr8e2sac"
wg_if    = "wg0"
lan_net  = "192.168.88.0/24"
zt_net   = "192.168.192.0/24"
my_zt_ip = "192.168.192.204"
lan_ip   = "192.168.88.160"

ssh_port     = "{ 22 }"
nfs_ports_tcp = "{ 111, 892, 2049 }"
nfs_ports_udp = "{ 111, 892 }"

# Opciones
set block-policy return
set ruleset-optimization none
set skip on lo0

match in all scrub (no-df random-id max-mss 1440)

# Tablas
table <bruteforce> persist
table <wg_peers> { 192.168.88.51 }

# LAN confiable
pass in quick on $lan_if from $lan_net to $lan_ip keep state

# WireGuard
pass in quick on $lan_if proto udp from <wg_peers> to $lan_ip port 51820 keep state
pass in quick on $wg_if from $lan_net to any keep state

# Anti-spoofing (excluyendo interfaces internas)
block in quick on ! { $lan_if, $zt_if, $wg_if } from { 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 } to any
block in quick from any to { 127.0.0.0/8, 0.0.0.0/8 }

# Lista negra
block in quick from <bruteforce>

# Salida
pass out quick inet keep state

# ICMP esencial
pass in inet proto icmp all icmp-type { unreach, timex, paramprob } keep state

# ====================== SSH DESDE LAN ======================
pass in quick on $lan_if proto tcp \
    from $lan_net to $lan_ip port $ssh_port \
    flags S/SFRA keep state

# SSH desde ZeroTier con rate-limit
pass in on $zt_if proto tcp from $zt_net to $my_zt_ip port $ssh_port \
    flags S/SFRA keep state \
    (max-src-conn-rate 15/5, overload <bruteforce> flush global)

# DNS para ZeroTier
pass in on $zt_if proto { tcp udp } from $zt_net to $my_zt_ip port 53

# ZeroTier control
pass in quick on $zt_if proto udp from any to any port 9993

# Bloqueo final
block drop in log all

Pasos típicos una vez aplicado el ruleset

Validar sin aplicar

pfctl -nf /etc/pf.conf

Aplicar y verificar

pfctl -f /etc/pf.conf
pfctl -sr          # reglas activas
pfctl -ss          # estados actuales
pfctl -t bruteforce -T show   # IPs en la tabla

Test desde fuera (desde otro host, no desde la propia máquina):

nmap -Pn -p 22,53,111,2049,51820,9993 192.168.88.160

Ver logs en vivo de lo bloqueado:

tcpdump -n -e -ttt -i pflog0

Integrar sshguard o pf-badhost para que <bruteforce> se llene y expire solo:

pkg install sshguard
sysrc sshguard_enable=YES

Y en /usr/local/etc/sshguard.conf apuntar el backend a PF con pfctl.

Cómo crear el archivo de configuración?
Ejecutar el siguiente comando para crear el archivo a partir de la plantilla:

cp /usr/local/etc/sshguard.conf.sample /usr/local/etc/sshguard.conf

Una vez creado, editar /usr/local/etc/sshguard.conf con los parámetros esenciales para que funcione con el firewall PF:

# Backend para PF (requerido)
BACKEND="/usr/local/libexec/sshg-fw-pf"

# Archivos de log a monitorizar
FILES="/var/log/auth.log"

# Umbral de bloqueo (ej: 120 segundos de bloqueo por defecto)
# Formato: <segundos_bloqueo>:<ruta_al_archivo_de_lista_negra>
BLACKLIST_FILE=120:/var/db/sshguard/blacklist.db

# Opcional: subnet a bloquear
IPV4_SUBNET=32
IPV6_SUBNET=128
Nota sobre el BLACKLIST_FILE: Si se encuentra que el umbral de bloqueo definido en sshguard.conf no surte efecto, puede deberse a una configuración por defecto en el script rc.d. Para solucionarlo, se añade la siguiente línea en el archivo /etc/rc.conf:
sshguard_blacklist=""
De esta forma, solo se aplicarán los valores que se definan en el archivo sshguard.conf

Habilitar e iniciar SSHGuard:

sysrc sshguard_enable=YES
service sshguard start

Hacer persistente PF en el arranque:

sysrc pf_enable=YES
sysrc pflog_enable=YES

Comprobar el estado y las IPs bloqueadas:

service sshguard status
pfctl -t sshguard -T show
FreeBSD es genial!.

martes, 23 de septiembre de 2025

Supervisar Disco con smartmontools en FreeBSD 14.4

Configurar smartmontools en FreeBSD 14.4 para supervisar el estado de discos SSD y/o NVMe y enviar correos mediante mail del sistema o los servidores de Gmail.

Requisitos previos

- FreeBSD 14.x
- smartmontools instalado.
- DMA configurado con Gmail.

Paso 1 — Instalar smartmontools

pkg install smartmontools

Habilitar el servicio smartd al arranque

sysrc smartd_enable="YES"

Paso 2 — Configurar DMA para enviar correos por Gmail

FreeBSD 14.4 usa DMA (DragonFly Mail Agent) como agente de correo por defecto. La configuración completa de DMA con Gmail (autenticación, contraseña de aplicación, smarthost) se explica en el siguiente artículo:

https://gnulinuxcodigo.blogspot.com/2024/12/freebsd-dma-dragonfly-mail-agent.html

Probar el envío de correo (directo, sin pasar por el script)

echo "Prueba de correo desde FreeBSD" | mail -s "Prueba" usuario@gmail.com
Esto utiliza mail directamente, sin pasar por el script, y sirve para verificar que DMA funciona antes de integrar smartd. Si no llega el correo, revisa /var/log/maillog y la configuración de DMA antes de continuar.

Paso 3 — Crear el helper de notificación

En FreeBSD el paquete smartmontools no instala ningún helper de notificación; hay que crearlo manualmente. Este script es el que smartd invocará cada vez que necesite enviar un correo, y se encarga de llamar a /usr/bin/mail.

cat > /usr/local/sbin/smartdnotify-mailout <<'EOF'
#!/bin/sh
# ============================================================
# smartdnotify-mailout
#
# Script de notificación para smartd.
# smartd lo invoca con las variables de entorno:
#   SMARTD_SUBJECT - asunto del mensaje
#   SMARTD_ADDRESS - dirección(es) de destino
#   SMARTD_FAILTYPE - tipo de fallo
#   SMARTD_DEVICE / SMARTD_DEVICESTRING - dispositivo afectado
#
# El cuerpo del mensaje llega por stdin.
# ============================================================

MAIL=/usr/bin/mail

exec ${MAIL} -s "${SMARTD_SUBJECT}" "${SMARTD_ADDRESS}"
EOF

Dar permisos de ejecución y propietario

chmod 755 /usr/local/sbin/smartdnotify-mailout
chown root:wheel /usr/local/sbin/smartdnotify-mailout

Verificar que el contenido es correcto

cat /usr/local/sbin/smartdnotify-mailout
El archivo debe contener el script tal cual (con ${MAIL}, ${SMARTD_SUBJECT} y ${SMARTD_ADDRESS} literales, sin expandir). Si ves esas variables ya sustituidas por valores, el heredoc se cerró mal y hay que recrear el archivo.

Prueba manual del script (simulando una llamada de smartd)

echo "Este es un mensaje de prueba enviado por smartdnotify-mailout." \
| env SMARTD_SUBJECT="TEST: prueba de smartdnotify-mailout" \
      SMARTD_ADDRESS="usuario@gmail.com" \
      /usr/local/sbin/smartdnotify-mailout
Se usa env para pasar las variables de entorno de forma portable, porque la sintaxis VAR=valor comando no funciona en csh (el shell por defecto de root en FreeBSD).

Paso 4 — Identificar los discos

smartctl --scan

/dev/da0 -d sat # /dev/da0 [SAT], ATA device
/dev/nvme0 -d nvme # /dev/nvme0, NVMe device

Comprobar que smartctl habla con el NVMe

smartctl -d nvme -a /dev/nvme0

Mostrará modelo, serial, versión NVMe, etc.

smartctl 7.5 2025-04-30 r5714 [FreeBSD 14.4-RELEASE-p8 amd64] (local build)
Copyright (C) 2002-25, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF INFORMATION SECTION ===
Model Number:                       CT500P3SSD8
Serial Number:                      2240E6733488
Firmware Version:                   P9CR30A
PCI Vendor/Subsystem ID:            0xc0a9
IEEE OUI Identifier:                0x00a075
Controller ID:                      1
NVMe Version:                       1.4
Number of Namespaces:               1
Namespace 1 Size/Capacity:          500,107,862,016 [500 GB]
...

Paso 5 — Configurar /usr/local/etc/smartd.conf

Este es el contenido completo del fichero de configuración de smartd, adaptado a los dos discos del sistema: el SSD NVMe principal y el disco USB de backup.

# ============================================================
# /usr/local/etc/smartd.conf
# Monitorización SMART
# ============================================================
# Documentación: man 5 smartd.conf
#
# Directivas comunes:
#   -a            Equivale a: -H -f -t -l error -l selftest -l selfteststs -C 197 -U 198
#   -o on         Activa el auto offline data collection test
#   -S on         Activa el auto-save de atributos SMART
#   -n standby,q  No despierta el disco si está dormido
#   -s ...        Programa self-tests (S=corto, L=largo)
#   -m ...        Destinatario del correo
#   -M exec ...   Ejecuta el script indicado para enviar el correo
#   -M test       Envía un correo de prueba al arrancar (solo para verificar)
# ============================================================

# ------------------------------------------------------------
# SSD NVMe principal (sistema)
# Self-test corto diario a las 02:00, largo los domingos a las 03:00
# ------------------------------------------------------------
/dev/nvme0 -d nvme \
    -a -o on -S on \
    -s (S/../.././02|L/../../7/03) \
    -m usuario@gmail.com \
    -M exec /usr/local/sbin/smartdnotify-mailout

# ------------------------------------------------------------
# Disco USB de backup (Toshiba MQ04UBD200)
# Self-test CORTO diario a las 04:00 (el largo no es viable por USB)
# El disco es extraíble, por eso -d removable
# ------------------------------------------------------------
/dev/da0 -d removable \
    -a -o on -S on \
    -n standby,q \
    -s (S/../.././04) \
    -m usuario@gmail.com \
    -M exec /usr/local/sbin/smartdnotify-mailout

Claves de la configuración

Directiva Motivo
/dev/nvme0 en lugar de /dev/nda0 El dispositivo de caracteres NVMe es el correcto para SMART en FreeBSD
-d nvme en el NVMe Fuerza explícitamente el tipo de dispositivo
-d removable en el USB Permite a smartd arrancar aunque el disco esté desconectado
-n standby,q en el USB No despierta el disco si está en standby
-s (S/...|L/...) para NVMe Self-test corto diario, largo semanal
-s (S/...) solo para USB Únicamente self-test corto (el largo se aborta por el puente USB)
-M exec Ejecuta el script smartdnotify-mailout para enviar el correo

Paso 6 — Verificar la sintaxis sin reiniciar el servicio

smartd -q onecheck

Interpretación de la validación

Configuration file /usr/local/etc/smartd.conf parsed.       ← Sintaxis correcta
Device: /dev/nvme0, opened                                   ← NVMe accesible
Device: /dev/nvme0, CT500P3SSD8, S/N:2240E6733488, FW:P9CR30A
Device: /dev/nvme0, is SMART capable. Adding to "monitor" list.
Device: /dev/da0 [SAT], opened                              ← USB accesible
Device: /dev/da0 [SAT], TOSHIBA MQ04UBD200, S/N:Y0JKT0QOT, ... 2.00 TB
Device: /dev/da0 [SAT], enabled SMART Attribute Autosave.   ← -S on aplicado
Device: /dev/da0 [SAT], enabled SMART Automatic Offline Testing.  ← -o on aplicado
Monitoring 1 ATA/SATA, 0 SCSI/SAS and 1 NVMe devices        ← Los dos discos
Device: /dev/nvme0, previous short self-test completed without error
Device: /dev/da0 [SAT], previous self-test completed without error
Started with '-q onecheck' option. All devices successfully checked once.
smartd is exiting (exit status 0)                            ← Salida limpia
Si en algún momento añades temporalmente -M test a la configuración, en esta salida aparecerá también una línea del tipo: Test of /usr/local/sbin/smartdnotify-mailout to usuario@gmail.com: successful. Es la confirmación de que smartd ha ejecutado el helper correctamente.

Paso 7 — Reiniciar smartd

service smartd restart
Stopping smartd.
Waiting for PIDS: 29540.
Starting smartd.                                             ← Arrancado sin errores

Verificar que arrancó bien

service smartd status
smartd is running as pid 32393.

Comprobar la actividad en el log

tail -10 /var/log/messages | grep smartd
Sep 24 02:06:19 tormenta smartd[32393]: Device: /dev/nvme0, starting scheduled Short Self-Test (NSID 0xffffffff).
Sep 24 02:36:19 tormenta smartd[32393]: Device /dev/nvme0, previous short self-test completed without error
Sep 24 03:06:19 tormenta smartd[32393]: Device: /dev/da0 [SAT], is back in ACTIVE or IDLE mode, resuming checks (9 checks skipped)

Comprobación final — smartd invoca al helper

Para verlo en tiempo real, se puede arrancar smartd en primer plano con modo debug (salir con Ctrl+C):

smartd -d -c /usr/local/etc/smartd.conf
smartd 7.5 2025-04-30 r5714 [FreeBSD 14.4-RELEASE-p8 amd64] (local build)
Copyright (C) 2002-25, Bruce Allen, Christian Franke, www.smartmontools.org

Opened configuration file /usr/local/etc/smartd.conf
Configuration file /usr/local/etc/smartd.conf parsed.
Device: /dev/nvme0, opened
Device: /dev/nvme0, CT500P3SSD8, S/N:2240E6733488, FW:P9CR30A
Device: /dev/nvme0, is SMART capable. Adding to "monitor" list.
Device: /dev/da0 [SAT], opened
Device: /dev/da0 [SAT], TOSHIBA MQ04UBD200, S/N:Y0JKT0QOT, WWN:0-000000-000000000, FW:JT001U, 2.00 TB
Device: /dev/da0 [SAT], found in smartd database 7.5/5706: Toshiba 2.5" HDD MQ04UBD...
Device: /dev/da0 [SAT], enabled SMART Attribute Autosave.
Device: /dev/da0 [SAT], enabled SMART Automatic Offline Testing.
Device: /dev/da0 [SAT], is SMART capable. Adding to "monitor" list.
Monitoring 1 ATA/SATA, 0 SCSI/SAS and 1 NVMe devices
Device: /dev/nvme0, opened NVMe device
Device /dev/nvme0, previous short self-test completed without error
Device: /dev/da0 [SAT], opened ATA device
Device: /dev/da0 [SAT], previous self-test completed without error
Cualquier problema de salud detectado (temperatura alta, sectores reasignados, errores de lectura, etc.) se notificará automáticamente por correo a usuario@gmail.com.
FreeBSD es genial!.

martes, 26 de agosto de 2025

Poudriere en FreeBSD ZFS 14.2

Instalación y configuración de poudriere FreeBSD 14.2 ZFS


Referencias bibliográficas:

https://freebsdfoundation.org/wp-content/uploads/2023/11/reushling_poudrier.pdf
https://www.digitalocean.com/community/tutorials/how-to-set-up-a-poudriere-build-system-to-create-packages-for-your-freebsd-servers


Intel core i5-8350U 1.7 GHz
Procesador 4 cores
Procesador 8 hilos
RAM 16 GB
SWAP 16 GB
NVMe Pcie 4.0

ZPOOL=zroot
ZROOTFS=/poudriere
BASEFS=/usr/local/poudriere

FreeBSD 14.2-RELEASE (amd64) instalado El dataset raíz de poudriere estará en zroot/poudriere y se montará en /usr/local/poudriere

Paquetes mínimos

pkg install -y poudriere \
	       git-lite
	       vim
	       screen
	       curl
	       ca_root_nss

Crear datasets ZFS.

Ventajas: posibilidad de hacer snapshots, rollback y compression por separado

zfs create -o mountpoint=/usr/local/poudriere -o compression=lz4 zroot/poudriere
zfs create zroot/poudriere/jails
zfs create zroot/poudriere/ports
zfs create zroot/poudriere/data
zfs create zroot/poudriere/data/packages
zfs create zroot/poudriere/data/logs

Archivo /usr/local/etc/poudriere.conf

ZPOOL=zroot
ZROOTFS=/poudriere
FREEBSD_HOST=ftp://ftp.freebsd.org
RESOLV_CONF=/etc/resolv.conf
BASEFS=/usr/local/poudriere
POUDRIERE_DATA=${BASEFS}/data
USE_TMPFS="wrkdir data localbase"
DISTFILES_CACHE=/usr/ports/distfiles
CHECK_CHANGED_OPTIONS=verbose
CHECK_CHANGED_DEPS=yes
CCACHE_DIR=/var/cache/ccache
ALLOW_MAKE_JOBS_PACKAGES="llvm* rust firefox gcc*"
PARALLEL_JOBS=2 # 2 hilos logicos
KEEP_OLD_PACKAGES=yes
MAX_JOBS=2
NOHANG_TIME=7200
NOCOLOR=no

Descargar y crear la jail base (14.2-RELEASE)

poudriere jail -c -j 142amd64 -v 14.2-RELEASE -a amd64

Esto descarga sets.txz y crea un dataset zroot/poudriere/jails/142amd64

Crear de nuevo arbol de puertos (quarterly)

poudriere ports -c -p default -m git+https -U \
https://git.FreeBSD.org/ports.git -B 2026Q1     

poudriere ports -l

PORTSTREE METHOD    TIMESTAMP           PATH
default   git+https 2025-08-24 18:50:19 /usr/local/poudriere/ports/default

poudriere jails -l

JAILNAME VERSION         ARCH  METHOD TIMESTAMP           PATH
142amd64 14.2-RELEASE-p5 amd64 http   2025-08-24 18:42:25 /usr/local/poudriere/jails/142amd64

Ajustes globales

Archivo /usr/local/etc/poudriere.d/142amd64-make.conf

# Opciones generales para la jaula 142amd64
DEFAULT_VERSIONS+=ssh=openssl
OPTIONS_UNSET+=DOCS NLS EXAMPLES IPV6
DISABLE_LICENSES=yes

Crear listas de paquetes

Archivo /usr/local/etc/poudriere.d/port-list.txt

security/sudo

Archivo de configuracion de /var/cache/ccache/ccache.conf

max_size = 5.0G
cache_dir = /var/cache/ccache
compression = true
compression_level = 1
sloppiness = file_stat_matches, time_macros, include_file_mtime
hash_dir = false
run_second_cpp = false

Primer bulk build

poudriere bulk -j 142amd64 -p default -f /usr/local/etc/poudriere.d/port-list.txt

Servidor de paquetes local (nginx)

pkg install nginx
sysrc nginx_enable=YES

Archivo de configuración de nginx

$ sed -e '/^[ ]*#/d' -e '/^$/d' /usr/local/etc/nginx/nginx.conf
    
    server {
        listen       80 default;
        server_name  localhost;
	    root /usr/local/share/poudriere/html;
        
        location /data {
            alias  /usr/local/poudriere/data/logs/bulk;
            autoindex on;
        }
	
	location /packages {
		root /usr/local/poudriere/data;
		autoindex on;
	}
}

Configurar pkg en la máquina anfitriona

mkdir /usr/local/etc/pkg/repos

Archivo /usr/local/etc/pkg/repos/poudriere.conf

poudriere {
  url: "http://127.0.0.1/packages/142amd64-default/",
  enabled = yes,
  mirror_type: "http",
  signature_type: "pubkey",
  pubkey: "/usr/local/etc/poudriere/poudriere.pub",
}

Generar clave (una sola vez)

Crear el directorio donde se guardarán las claves

mkdir -p /usr/local/etc/poudriere

Generar la clave privada (4096 bits)

openssl genrsa -out /usr/local/etc/poudriere/poudriere.key 4096
chmod 600 /usr/local/etc/poudriere/poudriere.key

Extraer la clave pública

openssl rsa -in /usr/local/etc/poudriere/poudriere.key -pubout -out \
/usr/local/etc/poudriere/poudriere.pub
chmod 644 /usr/local/etc/poudriere/poudriere.pub

Asegúrarse de que poudiere la use al firmar.

Añadir esta línea al archivo /usr/local/etc/poudriere.conf

PKG_REPO_SIGNING_KEY=/usr/local/etc/poudriere/poudriere.key

El archivo /usr/local/etc/poudriere.conf queda así:

ZPOOL=zroot
ZROOTFS=/poudriere
FREEBSD_HOST=ftp://ftp.freebsd.org
RESOLV_CONF=/etc/resolv.conf
BASEFS=/usr/local/poudriere
POUDRIERE_DATA=${BASEFS}/data
USE_TMPFS=all # usa RAM + swap para las builds
DISTFILES_CACHE=/usr/ports/distfiles
CHECK_CHANGED_OPTIONS=verbose
CHECK_CHANGED_DEPS=yes
ALLOW_MAKE_JOBS_PACKAGES="pkg ccache py* rust llvm*"
PARALLEL_JOBS=8 # 8 hilos logicos
PKG_REPO_SIGNING_KEY=/usr/local/etc/poudriere/poudriere.key

Firmar el repositorio con la clave nueva

poudriere bulk -j 142amd64 -p default -f /usr/local/etc/poudriere.d/port-list.txt

Monitorear durante la compilación


poudriere status

SET PORTS JAIL BUILD STATUS  QUEUE BUILT FAIL SKIP IGNORE FETCH REMAIN TIME     LOGS
- default 142amd64 2025-08-26_08h57m21s parallel_build  3  0  0  0 0 0  3 00:00:22 \
/usr/local/poudriere/data/logs/bulk/142amd64-default/2025-08-26_08h57m21s

poudriere jail -l

JAILNAME VERSION         ARCH  METHOD TIMESTAMP           PATH
142amd64 14.2-RELEASE-p5 amd64 http   2025-08-25 23:10:54 /usr/local/poudriere/jails/142amd64

poudriere ports -l

PORTSTREE METHOD    TIMESTAMP           PATH
default   git+https 2025-08-26 08:55:04 /usr/local/poudriere/ports/default
local     null      2025-08-24 16:41:27 /usr/local/poudriere/ports/default
root@solaris:/usr/local/etc/pkg/repos# poudriere 
FreeBSD.conf        packagesite.yaml    
poudriere.conf     

Logs en tiempo real

poudriere log -f -j 142amd64 -p default 

Configurar pkg en los clientes, (o en la misma máquina), para que use la clave pública

mkdir -p /usr/local/etc/pkg/repos/
$ cat /usr/local/etc/pkg/repos/poudriere.conf
poudriere {
  url: "http://127.0.0.1/packages/142amd64-default/",
  enabled = yes,
  mirror_type: "http",
  signature_type: "pubkey",
  pubkey: "/usr/local/etc/poudriere/poudriere.pub",
}

Tras esto, pkg install validará las firmas sin errores.

Comandos útiles de rescate. Limpiar paquetes obsoletos

poudriere pkgclean -j 142amd64 -p default -f port-list.txt

Destruir jail y volver a crear (por ejemplo, tras nueva RELEASE)

poudriere jail -d -j 142amd64
poudriere jail -c -j 142amd64 -v 14.3-RELEASE -a amd64

Actualizar paquetes

pkg update -f
Updating FreeBSD repository catalogue...
Fetching meta.conf: 100%    179 B   0.2kB/s    00:01    
Fetching data.pkg: 100%   10 MiB  10.4MB/s    00:01    
Processing entries: 100%
FreeBSD repository update completed. 35669 packages processed.
Updating poudriere repository catalogue...
Fetching meta.conf: 100%    179 B   0.2kB/s    00:01    
Fetching data.pkg: 100%    2 KiB   2.0kB/s    00:01    
Processing entries: 100%
poudriere repository update completed. 3 packages processed.
All repositories are up to date.
pkg upgrade
curl -I http://127.0.0.1/
HTTP/1.1 200 OK
Server: nginx/1.28.0
Date: Tue, 26 Aug 2025 12:07:38 GMT
Content-Type: text/html
Content-Length: 13036
Last-Modified: Sat, 09 Aug 2025 09:58:45 GMT
Connection: keep-alive
ETag: "68971bd5-32ec"
Accept-Ranges: bytes

Actualizar el árbol existente (lo mantiene, solo descarga nuevos commits)

poudriere ports -u -p default

Listo para compilar y servir paquetes locales con poudriere en FreeBSD 14.2


Opciones

Poudriere no guarda automáticamente las elecciones que se hacen al ejecutar poudriere options, pero le indicamos con la bandera -z myset que lo guarde.

Ejecutar una vez poudriere options para el port que quiera

Las opciones seleccionadas se guardan en /usr/local/etc/poudriere.d/myset-options

poudriere options -z myset -c security/sudo

el flag -c fuerza que aparezca el menú incluso si ya existen opciones guardadas.


Un directorio "myset-options" es generado automaticamente por make config

/usr/local/etc/poudriere.d/myset-options/

y dentro del directorio los directorios categoria_paquete

ls /usr/local/etc/poudriere.d/myset-options
converters_libiconv/ devel_autoconf/      devel_m4/            lang_tcl86/
databases_sqlite3/   devel_kyua/          lang_lua54/          security_sudo/
devel_atf/           devel_lutok/         lang_perl5.42/

y dentro de esos directorio un archivo llamado options contiene las opciones de configuración.


cat /usr/local/etc/poudriere.d/myset-options/security_sudo/options
# This file is auto-generated by 'make config'.
# Options for sudo-1.9.17p2
_OPTIONS_READ=sudo-1.9.17p2
_FILE_COMPLETE_OPTIONS_LIST=AUDIT DISABLE_AUTH DISABLE_ROOT_SUDO DOCS EXAMPLES \
INSULTS LDAP NLS NOARGS_SHELL OPIE PAM PYTHON SSL GSSAPI_BASE GSSAPI_HEIMDAL \
GSSAPI_MIT
OPTIONS_FILE_SET+=AUDIT
OPTIONS_FILE_UNSET+=DISABLE_AUTH
OPTIONS_FILE_UNSET+=DISABLE_ROOT_SUDO
OPTIONS_FILE_UNSET+=DOCS
OPTIONS_FILE_UNSET+=EXAMPLES
OPTIONS_FILE_SET+=INSULTS
OPTIONS_FILE_UNSET+=LDAP
OPTIONS_FILE_UNSET+=NLS
OPTIONS_FILE_UNSET+=NOARGS_SHELL
OPTIONS_FILE_UNSET+=OPIE
OPTIONS_FILE_SET+=PAM
OPTIONS_FILE_UNSET+=PYTHON
OPTIONS_FILE_SET+=SSL
OPTIONS_FILE_UNSET+=GSSAPI_BASE
OPTIONS_FILE_UNSET+=GSSAPI_HEIMDAL
OPTIONS_FILE_UNSET+=GSSAPI_MIT

...

Compilar paquetes

poudriere bulk -j 142amd64 -p default -z myset -f /usr/local/etc/poudriere.d/port-list.txt

Para que pkg use los paquetes que acaba de compilar poudriere necesitas:
- Que poudriere haya terminado de firmar y publicar los paquetes.
- Un repositorio local servido por nginx (u otro)
- Un fichero /usr/local/etc/pkg/repos/poudriere.conf correctamente escrito

fichero /usr/local/etc/pkg/repos/poudriere.conf

poudriere {
  url: "http://127.0.0.1/packages/142amd64-default/",
  # url = "file:///usr/local/poudriere/data/packages/142amd64-default",
  enabled = yes,
  mirror_type: "http",
  signature_type: "pubkey",
  pubkey: "/usr/local/etc/poudriere/poudriere.pub",
}
root@solaris:/usr/local/poudriere/data/packages/142amd64-default/All# pkg repo .
Creating repository in .: 100%
Packing files for repository: 100%
root@solaris:/usr/local/poudriere/data/packages/142amd64-default/All# ls
data.pkg            meta.conf           pkg-2.2.2.pkg
data.tzst           packagesite.pkg     pkgconf-2.4.3,1.pkg
meta                packagesite.tzst    sudo-1.9.17p2.pkg

Desactivar el repositorio oficial (opcional)

#
# To disable this repository, instead of modifying or removing this file,
# create a /usr/local/etc/pkg/repos/FreeBSD.conf file:
#
#   mkdir -p /usr/local/etc/pkg/repos
#   echo "FreeBSD: { enabled: no }" > /usr/local/etc/pkg/repos/FreeBSD.conf
#

FreeBSD: {
 url: "pkg+https://pkg.FreeBSD.org/${ABI}/quarterly",
# url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
 mirror_type: "srv",
 signature_type: "fingerprints",
 fingerprints: "/usr/share/keys/pkg",
 enabled: no
}

Otra opción. La prioridad define el orden en que se utilizan estos repositorios

Por defecto, se utiliza el repositorio upstream de FreeBSD.org. Pero como queremos que nuestros propios paquetes tengan precedencia, le daremos una prioridad más alta que cero para que use nuestro repositorio de paquetes primero.

Quedaría así:

#
# To disable this repository, instead of modifying or removing this file,
# create a /usr/local/etc/pkg/repos/FreeBSD.conf file:
#
#   mkdir -p /usr/local/etc/pkg/repos
#   echo "FreeBSD: { enabled: no }" > /usr/local/etc/pkg/repos/FreeBSD.conf
#

FreeBSD: {
 url: "pkg+https://pkg.FreeBSD.org/${ABI}/quarterly",
# url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
 mirror_type: "srv",
 signature_type: "fingerprints",
 fingerprints: "/usr/share/keys/pkg",
 enabled: yes
}

Archivo /usr/local/etc/pkg/repos/poudriere.conf

poudriere {
  url: "http://127.0.0.1/packages/142amd64-default/",
  # url = "file:///usr/local/poudriere/data/packages/142amd64-default",
  enabled = yes,
  priority: 23,
  mirror_type: "http",
  signature_type: "pubkey",
  pubkey: "/usr/local/etc/poudriere/poudriere.pub",
}

Veamos en acción

 pkg update
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
Updating poudriere repository catalogue...
Fetching meta.conf: 100%    179 B   0.2kB/s    00:01    
Fetching data.pkg: 100%    2 KiB   2.0kB/s    00:01    
Processing entries: 100%
poudriere repository update completed. 3 packages processed.
All repositories are up to date.
root@solaris:/usr/local/etc/pkg/repos# pkg upgrade
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
Updating poudriere repository catalogue...
poudriere repository is up to date.
All repositories are up to date.
Checking for upgrades (206 candidates): 100%
Processing candidates (206 candidates): 100%
The following 4 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
	p5-Crypt-URandom: 0.54 [FreeBSD]

Installed packages to be UPGRADED:
	nss: 3.115 -> 3.115.1 [FreeBSD]
	p5-Authen-SASL: 2.18 -> 2.1900 [FreeBSD]
	perl5: 5.40.2_2 -> 5.40.3_2 [FreeBSD]

Number of packages to be installed: 1
Number of packages to be upgraded: 3

18 MiB to be downloaded.

Proceed with this action? [y/N]: y
[1/4] Fetching nss-3.115.1.pkg: 100%    2 MiB   2.3MB/s    00:01    
[2/4] Fetching p5-Crypt-URandom-0.54.pkg: 100%   17 KiB  17.6kB/s    00:01    
[3/4] Fetching perl5-5.40.3_2.pkg: 100%   16 MiB  16.4MB/s    00:01    
[4/4] Fetching p5-Authen-SASL-2.1900.pkg: 100%   44 KiB  44.7kB/s    00:01    
Checking integrity... done (0 conflicting)
[1/6] Deinstalling p5-Authen-SASL-2.18...
[1/6] Deleting files for p5-Authen-SASL-2.18: 100%
[2/6] Deinstalling perl5-5.40.2_2...
[2/6] Deleting files for perl5-5.40.2_2: 100%
[3/6] Upgrading nss from 3.115 to 3.115.1...
[3/6] Extracting nss-3.115.1: 100%
[4/6] Installing perl5-5.40.3_2...
[4/6] Extracting perl5-5.40.3_2: 100%
[5/6] Installing p5-Crypt-URandom-0.54...
[5/6] Extracting p5-Crypt-URandom-0.54: 100%
[6/6] Installing p5-Authen-SASL-2.1900...
[6/6] Extracting p5-Authen-SASL-2.1900: 100%
tree /usr/local/poudriere/data/packages/
/usr/local/poudriere/data/packages/
└── 142amd64-default
    ├── All -> .latest/All
    ├── Latest -> .latest/Latest
    ├── data
    ├── data.pkg -> .latest/data.pkg
    ├── data.tzst -> .latest/data.tzst
    ├── logs -> .latest/logs
    ├── meta -> .latest/meta
    ├── meta.conf -> .latest/meta.conf
    ├── packagesite.pkg -> .latest/packagesite.pkg
    ├── packagesite.tzst -> .latest/packagesite.tzst
    └── packagesite.yaml

5 directories, 8 files

Instalar paquete desde repositorio local

pkg install sudo
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
Updating poudriere repository catalogue...
poudriere repository is up to date.
All repositories are up to date.
Checking integrity... done (0 conflicting)
The following 1 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
	sudo: 1.9.17p2 [poudriere]

Number of packages to be installed: 1

The process will require 2 MiB more space.

Proceed with this action? [y/N]: y
[1/1] Installing sudo-1.9.17p2...
[1/1] Extracting sudo-1.9.17p2: 100%
Comprobar security con INSULTS activado:
sudo vim /etc/rc.conf                 
Password:
Quizás si usaras más de dos dedos...
Password:
¡Este hombre no sabe cuándo está vencido! Tampoco sabe cuándo está ganando. 
No tiene... una especie de... aparato sensorial...
Password:
FreeBSD es genial!.

viernes, 4 de julio de 2025

Borrar Disco FreeBSD 14.2

Borrar disco

Arranque el dispositivo objetivo desde el medio de instalación. En el menú de arranque de FreeBSD, presione s para seleccionar el modo de usuario único de arranque.

Use geom para identificar discos

 geom disk list
Geom name: nda0
Providers:
1. Name: nda0
...

Use dd para sobrescribir todo el disco con ceros:

 dd if=/dev/zero of=/dev/nda0 bs=4M conv=fsync oflag=direct status=progress

Esta operación tomará un poco de tiempo. En mi nvme 500G, por ejemplo, tardó aproximadamente 14 minutos en borrar completamente el disco. Sea paciente.

Instalación

Después de que termine la operación de borrado, ingrese el comando reboot para volver al Menú de Arranque. Presione <Enter> para iniciar el instalador.

 reboot
En la pantalla de bienvenida, elija Install.
FreeBSD es genial!.

jueves, 3 de julio de 2025

VLAN Mikrotik Basado en Puertos

VLANs Mikrotik - Basada en Puertos. Tenemos dos routers Mikrotik (RouterOS).

Internet

El router Mikrotik esta conectado desde el puerto ehter1 (WAN_ether1) a uno de los puertos ethernet del router ISP, usando DHCP Client.

[admin@R1] /ip dhcp-client> print
Flags: X - disabled, I - invalid, D - dynamic 
 #   INTERFACE    USE-PEER-DNS ADD-DEFAULT-ROUTE STATUS      ADDRESS           
 0   ether1        yes          yes               bound       192.168.88.12/24  

Todos los puertos de un switch por defecto pertenecen a la la vlan 1. El switch necesita de un router para mover (forwarding) el tráfico entre las vlan. Eso permite la comunicacion entre diferentes VLAN.

Las VLAN ID solo aparecen en Switch administrables y funcionan en capa 2. La segmentación se produce en el Switch.

El Switch tiene una base de datos llamada tabla CAM donde realiza un seguimiento de cada computadora conectada:

- MAC Address
- Numero de Puerto
- VLAN ID - Solo en Switches Administrables

Las VLANs se utilizan se utilizan para segmentar una red, lo que permite mejorar la seguridad, el rendimiento y la gestion de la red. Los dispositivos de una VLAN pueden ser configurados y administrados de forma separada.

Los dispositivos CRS (tienen capacidad de capa 3) se pueden utilizar como enrutador y conmutador al mismo tiempo, útil para redes que se centran en el rendimiento de la red interna.

VLAN - Laboratorio de pruebas

Ambos routers estan en blanco - System - Reset Configuration - No Default Configuration - Reset Configuration.

Tenemos un router 1 (R1) donde se crean las VLAN, vlan10 con vlan-id=10 y vlan20 con vlan-id=20 en la interfaz Ether3, cada una con su direccionamineto IP y un DHCP Server. Esas VLAN se envían Tagged (Trunk en Cisco) a través de Ether3 y se reciben Tagged en el router 2 (R2) en el puerto Ether1. Luego se entregan como Untagged (Access Port) en los puertos Ether3 y Ether4.

Configuración vía comandos del Router 1.

Nota: La dirección IP del dns-server correspnde al servidor DNS de mi red interna. Puede utilizar DNS públicos si no tiene un servidor DNS local, por ejemplo, 1.1.1.1 o los del propio router Mikrotik, por defecto 192.168.88.1.

/interface
add interface=ether2 name=vlan10 vlan-id=10
add interface=ether2 name=vlan20 vlan-id=20
/ip pool
add name=dhcp_pool0 ranges=10.10.10.200-10.10.10.254
add name=dhcp_pool1 ranges=172.24.1.200-172.24.1.254
/ip dhcp-server
add address-pool=dhcp_pool0 disable=no interface=vlan10 name=dhcp1
add address-pool=dhcp_pool1 disable=no interface=vlan20 name=dhcp2
/ip address
add address=10.10.10.1/24 interface=vlan10 network=10.10.10.0
add address=172.24.1.1/24 interface=vlan20 network=172.24.1.0
/ip dhcp-server network
add address=10.10.10.0/24 dns-server=192.168.88.200 gateway=10.10.10.1
add address=172.24.1.0/24 dns-server=192.168.88.200 gateway=172.24.1.1

En el Router R2 se crean las VLAN en el puerto Ether1 que es donde recibimos las VLAN con el nombre y vlan-id correspondiente, vlan10 vlan-id=10 y vlan20 vlan-id=20. Teniendo en cuenta que para que las VLAN se comuniquen entre dispositivos el id debe ser el mismo. Tambien hay que crear un bridge por cada VLAN Bridge_VLAN10 y Bridge_VLAN20. En la pestaña Bridge - Bridge - Ports, agregar dos interfaces: la propia VLAN y el puerto físico donde quiero entregarla como acceso (Access Port). En el caso de la VLAN vlan10 los puertos son: la propia VLAN vlan10 y el puerto Ether3. En la VLAN vlan20 los puertos son: la propia VLAN vlan20 y el puerto Ether4. Evidentemente, si quiero entregar la misma VLAN en más puertos este último paso se repite para todos los puertos involucrados.

Configuracion vía comandos Router 2 (R2)

/interface vlan
add interface=ether1 name=vlan10 vlan-id=10
add interface=ether1 name=vlan20 vlan-id=20
/interface bridge
add name=Bridge_VLAN10
add name=Bridge_VLAN20
/interface Bridge port
add bridge=Bridge_VLAN10 interface=vlan10
add bridge=Bridge_VLAN10 interface=ether3
add bridge=Bridge_VLAN20 interface=vlan20
add bridge=Bridge_VLAN20 interface=ether4

IP Route R1

[admin@R1] /ip route> print
Flags: X - disabled, A - active, D - dynamic, 
C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, 
B - blackhole, U - unreachable, P - prohibit 
 #      DST-ADDRESS        PREF-SRC        GATEWAY         DISTANCE
 0 ADS  0.0.0.0/0                          192.168.88.1           1
 2 ADC  10.10.10.0/24      10.10.10.1      vlan10                 0
 3 ADC  172.24.1.0/24      172.24.1.1      vlan20                 0
 4 ADC  192.168.88.0/24    192.168.88.12   ether1                 0
 

Conecto un PC a Ether3 del router R2 y recibe una IP del DHCP-Server configurado en R1

[admin@R1] /ip dhcp-server lease print
Flags: X - disabled, R - radius, D - dynamic, B - blocked 
 #   ADDRESS              MAC-ADDRESS       H SE.. R STATUS  LAST-SEEN            
 0 D 10.10.10.251         E4:B9:7A:6B:96:CD s dh..   bound   4m6s  
 

(Opcional) -¿Cómo Permitir tráfico de VLANs?

/ip firewall filter
add chain=forward src-address=10.10.10.0/24 dst-address=172.24.1.0/24 \
connection-state=new,established action=accept comment="Permitir VLAN10 \
a VLAN20"
/ip firewall filter
add chain=forward src-address=172.24.1.0/24 dst-address=10.10.10.0/24 \
connection-state=new,established action=accept comment="Permitir VLAN20 \
a VLAN10"

Ubicación de las reglas:

Antes de cualquier regla que bloquee tráfico (ej. action=drop).
Usa el menú Drag & Drop en Winbox para ordenarlas.

Explicacion:

Las opciones connection-state=new,established permiten:
new: Conexiones iniciadas desde el origen.
established: Respuestas al tráfico iniciado.

Permitir acceso administrativo sólo desde la red 10.10.10.1/24

add chain=input src-address=10.10.10.1/24 protocol=tcp dst-port=22,8291,8728,8729 \
action=accept comment="Acceso administrativo (Winbox/SSH/API)"

Reglas de cortafuegos R1

admin@R1VLAN] > /ip firewall filter print
Flags: X - disabled, I - invalid, D - dynamic 
 0    ;;; Permitir conexiones establecidas
      chain=input action=accept connection-state=established,related log=no log-prefix="" 

 1    ;;; Permitir ICMP
      chain=input action=accept protocol=icmp log=no log-prefix="" 

 2    ;;; Permitir conexiones establecidas
      chain=forward action=accept connection-state=established,related log=no log-prefix="" 

 3    ;;; Bloquear conexiones inva/ip lidas
      chain=forward action=drop connection-state=invalid log=no log-prefix="" 

 4    ;;; Permitir trafico forward in wlan1 y out vlan10
      chain=forward action=accept in-interface=wlan1 out-interface=vlan10 log=no log-prefix="" 

 5    ;;; Permitir forward in vlan10 y out wlan1
      chain=forward action=accept in-interface=vlan10 out-interface=wlan1 log=no log-prefix="" 

 6    ;;; Permitir trafico forward in wlan1 y out vlan20
      chain=forward action=accept in-interface=wlan1 out-interface=vlan20 log=no log-prefix="" 

 7    ;;; Perimtir trafico forward in vlan20 out wlan1
      chain=forward action=accept in-interface=vlan20 out-interface=wlan1 log=no log-prefix="" 

 8    ;;; Bloquear trafico no permitido
      chain=forward action=drop log=no log-prefix="" 
FreeBSD es genial!.