Si tienes un servidor doméstico (MiniNAS) ejecutando servicios críticos o un clúster de Kubernetes (como K3s), seguro que te has encontrado con el dilema clásico: ¿cómo hacer que dispositivos concretos de la red local (como una televisión o un móvil) salgan a internet a través de una VPN (como ProtonVPN) sin romper ni interferir con los servicios internos o el clúster?

En este artículo veremos cómo convertir el MiniNAS en una pasarela VPN selectiva de manera limpia, eficiente y transparente.

La Arquitectura de la Solución#

El objetivo principal es conseguir que cualquier dispositivo de la red local (192.168.1.0/24) que utilice una IP secundaria del MiniNAS como Gateway (Puerta de enlace) salga automáticamente a través del túnel VPN (tun0), mientras que:

  1. El tráfico general del MiniNAS y el clúster K3s continúa saliendo por el router principal (192.168.1.1).
  2. No se utilizan direcciones IP fijas en el código de los scripts, haciéndolo totalmente universal para cualquier cliente de la red.

Paso 1: Añadir una IP secundaria al MiniNAS#

Para que el MiniNAS pueda actuar como gateway de otros dispositivos, le asignamos una IP secundaria a la interfaz física (enp2s0). Crearemos un servicio de systemd para que sea persistente:

sudo tee /etc/systemd/system/mininas-vpn-ip.service << 'EOF'

[Unit]
Description=Add secondary IP for VPN routing
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/ip addr add 192.168.1.121/24 dev enp2s0
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

EOF

sudo systemctl daemon-reload
sudo systemctl enable --now mininas-vpn-ip.service

Paso 2: Configurar OpenVPN con Policy-Based Routing#

Usamos la directiva route-noexec en la configuración de OpenVPN para que el demonio no altere la tabla de rutas principal del sistema. Además, configuramos los scripts up y down para gestionar la tabla personalizada (100) y el NAT solo cuando el tráfico provenga de la red local entrando por la interfaz física.

Script de inicio (/etc/openvpn/up-vpn.sh)#

#!/bin/bash

# Activar el reenvío IP
sysctl -w net.ipv4.ip_forward=1

# 1. Si viene de la red local y entra por la tarjeta física, deriva a la tabla 100
ip rule add from 192.168.1.0/24 iif enp2s0 table 100

# 2. Rutas de la tabla 100
ip route add 192.168.1.0/24 dev enp2s0 table 100
ip route add default dev $1 table 100

# 3. Permitir tráfico por el cortafuegos y hacer NAT genérico
iptables -A FORWARD -i enp2s0 -o $1 -j ACCEPT
iptables -A FORWARD -i $1 -o enp2s0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -o $1 -j MASQUERADE

Script de limpieza (/etc/openvpn/down-vpn.sh)#

#!/bin/bash

ip rule del from 192.168.1.0/24 iif enp2s0 table 100 2>/dev/null

ip route flush table 100 2>/dev/null

iptables -D FORWARD -i enp2s0 -o tun+ -j ACCEPT 2>/dev/null
iptables -D FORWARD -i tun+ -o enp2s0 -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null
iptables -t nat -D POSTROUTING -o tun+ -j MASQUERADE 2>/dev/null

Paso 3: Configurar los Dispositivos Clientes (TVs, Móviles)#

Una vez levantado el servicio de OpenVPN en el MiniNAS, configurar cualquier dispositivo es tan fácil como asignar una IP estática al cliente con estos parámetros:

  • Dirección IP: La que quieras de tu subred (ej: 192.168.1.50 o 192.168.1.250)
  • Máscara: 255.255.255.0
  • Puerta de enlace (Gateway): 192.168.1.121 (La IP secundaria del MiniNAS)
  • DNS: 8.8.8.8 o Cloudflare (1.1.1.1)

Paso 4: Monitorización con HetrixTools#

Para llevar el control del consumo de datos separando el tráfico local del de la VPN, puedes aprovechar las variables personalizadas de HetrixTools creando un pequeño script ejecutado por cron cada minuto:

#!/bin/bash

TUN_RX=$(cat /sys/class/net/tun0/statistics/rx_bytes 2>/dev/null || echo 0)
TUN_TX=$(cat /sys/class/net/tun0/statistics/tx_bytes 2>/dev/null || echo 0)

ETH_RX=$(cat /sys/class/net/enp2s0/statistics/rx_bytes 2>/dev/null || echo 0)
ETH_TX=$(cat /sys/class/net/enp2s0/statistics/tx_bytes 2>/dev/null || echo 0)

cat << JSON > /etc/hetrixtools/custom_variables.json
{
  "VPN_Down_MB": $((TUN_RX / 1024 / 1024)),
  "VPN_Up_MB": $((TUN_TX / 1024 / 1024)),
  "Local_Down_MB": $((ETH_RX / 1024 / 1024)),
  "Local_Up_MB": $((ETH_TX / 1024 / 1024))
}
JSON