VPN de oficina: permitir acceso a un solo servidor/puerto (principio de menor privilegio)

¿Te fue útil?

La VPN de la oficina funciona. Todos están contentos. Hasta que alguien descubre que “acceso VPN” es básicamente “una autopista privada hacia toda la red corporativa.”
Entonces recibes el mensaje a medianoche: “¿Por qué el contratista puede llegar a la base de datos?” y te das cuenta de que construiste un túnel, no una barandilla.

El objetivo aquí es brutalmente simple: una vez que un usuario se conecta a la VPN, debería poder alcanzar exactamente un servidor en exactamente un puerto.
No “idealmente”. No “a menos que conozcan la subred”. Exactamente. Cualquier otra cosa es un accidente esperando una invitación de calendario.

Qué significa realmente “un solo servidor/puerto”

El principio de menor privilegio sobre una VPN no es “los usuarios solo pueden ver la subred que necesitan.” Eso es una ficción piadosa. Las subredes son para enrutamiento; los privilegios son para seguridad.
Cuando dices “un solo servidor/puerto”, en realidad estás diciendo:

  • El cliente VPN recibe una IP (o no), pero no se confía únicamente en el enrutamiento para hacer cumplir nada.
  • La pasarela VPN y/o el servidor destino aplican reglas de permiso y por defecto deniegan.
  • Todos los demás destinos y puertos están bloqueados, incluidos servicios “internos” como DNS, SMB, SSH, RDP y endpoints de métricas.
  • Existe observabilidad: registros, contadores y una forma de demostrar qué se bloqueó y por qué.

El anti-patrón es “solo empujamos una ruta”. Eso no es menor privilegio; es una sugerencia. Si el cliente puede añadir rutas manualmente,
o si hay NAT, o si hay una segunda interfaz en el equipo VPN, tendrás conectividad sorpresa.

Un diseño sólido suele involucrar dos planos de control:
(1) controles de red (firewall/enrutamiento en la entrada de la VPN), y
(2) controles de servicio (firewall/autenticación de la aplicación en el destino).
Si solo haces uno, tarde o temprano entenderás por qué la gente hace el otro.

Datos interesantes y un poco de historia

  • Las VPN no nacieron como herramientas de zero-trust. El pensamiento empresarial temprano asumía que “dentro del túnel” era “dentro de la oficina.” Esa suposición envejecería mal.
  • IPsec precede a muchas prácticas modernas de seguridad empresarial. Viene de una era donde los firewalls perimetrales eran la historia principal, no la segmentación interna.
  • El split tunneling siempre ha sido controvertido. Reduce la carga en la red corporativa, pero también crea endpoints de confianza mixta (Wi‑Fi doméstico + túnel corporativo).
  • Los firewalls aprendieron estado para resolver dolores reales. Los ACLs sin estado hacían “permitir TCP/443” complicado sin también permitir tráfico de respuesta; el seguimiento stateful resolvió eso.
  • “Usuario VPN = usuario de red” es un atajo histórico. Era conveniente operativamente cuando los sistemas de identidad eran más simples y el trabajo remoto era la excepción.
  • El diseño de WireGuard es inusualmente pequeño. Su minimalismo facilitó auditorías y despliegues frente a pilas VPN dispersas, y eso cambió elecciones por defecto.
  • NAT se convirtió en una muleta de seguridad. La gente trató a NAT como aislamiento; no lo es. Es traducción de direcciones, no autorización.
  • La microsegmentación no empezó en la nube. Los centros de datos la hicieron con VLANs y ACLs mucho antes de que “zero trust” fuera un sustantivo en presentaciones.

Modelo de amenazas: qué previenes (y qué no)

Estás intentando prevenir el movimiento lateral desde un cliente VPN hacia el resto de la red. Eso incluye:

  • Escaneos de puertos en rangos internos (“solo comprobando qué hay”).
  • Alcanzar superficies administrativas: SSH, RDP, consolas tipo vCenter, BMCs, gestión de NAS, impresoras (sí) y cualquier cosa con UI web.
  • Usar DNS interno para descubrir nombres que no querías publicar.
  • Acceder a servicios de metadata, registros internos o endpoints de monitorización que filtren topología y credenciales.

No estás resolviendo:

  • Credenciales comprometidas para el único servicio permitido. (Sigues necesitando autenticación fuerte allí.)
  • Malware en el endpoint que exfiltra datos a través del puerto permitido. (Necesitas DLP/monitorización y controles en la aplicación.)
  • Inspección profunda de contenido del tráfico. (Eso es otro sistema y trae sus propios costes operativos.)

Una cita para mantener la postura honesta. Como dijo Bruce Schneier: La seguridad es un proceso, no un producto. No “configuras menor privilegio” una vez.
Lo mantienes con mínimo privilegio mientras los requisitos cambian y el personal rota.

Broma corta #1: Si permites “cualquier interno” desde la VPN, no estás haciendo seguridad—estás haciendo cosplay de oficina remota.

Tres patrones que realmente funcionan

Patrón A: la pasarela VPN aplica una lista estricta de permitidos (más común)

El concentrador VPN (o el host Linux que ejecuta WireGuard/OpenVPN) actúa como punto de estrangulamiento. Los clientes VPN aterrizan en una interfaz/subred dedicada.
Una política de firewall dice: desde subred VPN → permitir a target_ip:target_port, negar todo lo demás.

Pros: modelo mental simple, control central, buen registro. Contras: si la pasarela está mal configurada o se la evita (rutas dual-homed, hairpinning),
puedes abrir accidentalmente más de lo previsto.

Patrón B: sin acceso de red general; publica un solo servicio vía proxy

En lugar de permitir que los clientes VPN lleguen al servidor objetivo directamente, expones un servicio mediante un proxy inverso (para HTTP/HTTPS) o un proxy TCP
(para no-HTTP). El cliente VPN solo puede alcanzar el proxy; el proxy es lo único que puede alcanzar al objetivo.

Pros: controles a nivel de aplicación, mejor integración de identidad, auditoría más sencilla. Contras: añades otro salto y otra cosa que mantener.

Patrón C: coloca el objetivo en una “red de servicio VPN” dedicada

Crea un segmento aislado (VLAN/VRF) donde vive el servidor objetivo, con reglas de ingreso estrictas. Los clientes VPN pueden enrutar solo hacia ese segmento,
y ese segmento no puede enrutar ampliamente hacia fuera.

Pros: fuerte reducción del radio de impacto. Contras: más ingeniería de red, y es fácil estropear la ruta de retorno y culpar a “la VPN.”

Mi opinión sesgada: Patrón A más restricciones del lado del servidor es la base. Si puedes hacer el Patrón B sin que el equipo de aplicación llore, hazlo.
El Patrón C es excelente cuando tienes infraestructura de red real y disciplina de control de cambios.

Implementación: WireGuard + firewall (recomendado)

WireGuard es popular porque es sencillo de operar. Pero WireGuard en sí no es un motor de control de acceso.
AllowedIPs es principalmente un concepto de enrutamiento/selección de pares, no una “política de seguridad que no puede ser sorteada.”
Trátalo como plomería útil y luego aplica la política con reglas de firewall donde importe.

Diseño: subred VPN dedicada y un único destino permitido

  • Interfaz VPN: wg0
  • Subred VPN: 10.50.0.0/24
  • Servidor objetivo: 10.20.30.40
  • Puerto permitido: 5432/tcp (ejemplo: Postgres)

Configuración de WireGuard (servidor)

Mantén la configuración del servidor aburrida. Aburrida es buena. Aburrida significa que tu yo futuro no tendrá que re-interpretar lo “ingenioso.”

cr0x@server:~$ sudo sed -n '1,120p' /etc/wireguard/wg0.conf
[Interface]
Address = 10.50.0.1/24
ListenPort = 51820
PrivateKey = (redacted)
# Optional: save config on runtime changes
SaveConfig = false

[Peer]
PublicKey = (redacted)
AllowedIPs = 10.50.0.10/32

Importante: El AllowedIPs del par siendo 10.50.0.10/32 significa “este par posee esa IP VPN.”
No impide mágicamente que el par intente alcanzar 10.20.30.40 una vez que el túnel está activo. Ese es trabajo del firewall.

Política de firewall con nftables (preferido en Linux moderno)

El enfoque limpio es: denegar por defecto desde VPN → a cualquier parte, luego abrir un único agujero exacto. También permitir tráfico establecido/relacionado de retorno.
Y registrar descartes con limitación de tasa para no convertir tus discos en un DoS.

cr0x@server:~$ sudo nft list ruleset
table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;
    iif "lo" accept
    ct state established,related accept
    iif "wg0" tcp dport 22 accept
    iif "eth0" tcp dport 51820 accept
    ip protocol icmp accept
    counter drop
  }

  chain forward {
    type filter hook forward priority 0; policy drop;
    ct state established,related accept

    # Allow VPN clients to reach exactly one server/port
    iif "wg0" ip saddr 10.50.0.0/24 ip daddr 10.20.30.40 tcp dport 5432 accept

    # Optional: allow VPN clients to reach only the VPN gateway DNS forwarder
    # iif "wg0" ip saddr 10.50.0.0/24 ip daddr 10.50.0.1 udp dport 53 accept
    # iif "wg0" ip saddr 10.50.0.0/24 ip daddr 10.50.0.1 tcp dport 53 accept

    # Log and drop everything else from VPN
    iif "wg0" limit rate 10/second burst 20 log prefix "VPN-DROP " flags all counter drop
    counter drop
  }

  chain output {
    type filter hook output priority 0; policy accept;
  }
}

Qué notar:

  • Política DROP en chain forward: la caja VPN no es un router “por defecto.” Es un portero con una ficha.
  • La regla de permiso está completamente calificada: interfaz, subred origen, IP destino, protocolo y puerto.
  • Registramos descartes desde wg0 con limitación de tasa. Sin límites, un becario aburrido con nmap se convierte en tu problema de retención de logs.

Enrutamiento y NAT: evita el NAT “útil” a menos que realmente lo necesites

Si tu servidor objetivo ya enruta de vuelta a la subred VPN vía la pasarela VPN, no necesitas NAT. Eso es más limpio y más auditable.
NAT puede ocultar la identidad del cliente al servidor objetivo y complicar las listas de permiso del servidor.

Si no puedes cambiar rutas en el lado del objetivo, puedes usar source NAT en la pasarela VPN para que el objetivo responda a la pasarela.
Pero trata esto como un compromiso y regístralo.

cr0x@server:~$ sudo nft list table ip nat
table ip nat {
  chain postrouting {
    type nat hook postrouting priority 100; policy accept;
    oif "eth0" ip saddr 10.50.0.0/24 ip daddr 10.20.30.40 tcp dport 5432 masquerade
  }
}

Decisión: si ves reglas de masquerade que aplican a destinos amplios (como “any”), apriétalas. NAT no es un pase de permiso.

Implementación: OpenVPN + firewall (todavía común)

OpenVPN está probado en combate y está en todas partes. El mismo principio aplica: OpenVPN empuja rutas; el firewall aplica el acceso.
OpenVPN también soporta configuración por cliente (CCD), que la gente usa como si fuera segmentación. Puede ayudar, pero no es un reemplazo de la política de firewall.

Forma de la configuración del servidor OpenVPN

cr0x@server:~$ sudo sed -n '1,160p' /etc/openvpn/server/server.conf
port 1194
proto udp
dev tun0
server 10.60.0.0 255.255.255.0
topology subnet
persist-key
persist-tun
keepalive 10 60
user nobody
group nogroup

# Don't push broad routes unless you mean it
;push "route 10.0.0.0 255.0.0.0"

# Optionally push a single host route (still not a security boundary)
push "route 10.20.30.40 255.255.255.255"

client-config-dir /etc/openvpn/ccd

Ejemplo de CCD (por cliente)

cr0x@server:~$ sudo cat /etc/openvpn/ccd/alice
ifconfig-push 10.60.0.10 255.255.255.0

Buena higiene. Aun así, no es aplicación. La aplicación es el firewall en FORWARD o la cadena equivalente de nftables.

Ejemplo iptables (legado pero aún visto)

cr0x@server:~$ sudo iptables -S FORWARD
-P FORWARD DROP
-A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i tun0 -s 10.60.0.0/24 -d 10.20.30.40/32 -p tcp -m tcp --dport 5432 -j ACCEPT
-A FORWARD -i tun0 -j LOG --log-prefix "VPN-DROP " --log-level 4
-A FORWARD -i tun0 -j DROP

Decisión: si la política no es DROP, arréglalo. Si ves -A FORWARD -i tun0 -j ACCEPT en alguna parte, esa es la regla de “barra libre.” Elíminala.

Controles en el servidor: asume que la VPN miente

Si el servidor objetivo es importante, aplica una política de firewall en él también. La pasarela VPN es un punto de control único, lo cual es genial—hasta que no lo es.
Una regla mal aplicada, una segunda instancia VPN, un cambio de emergencia o una excepción en el grupo de seguridad cloud puede omitir tu pasarela.

En el objetivo: permitir solo la subred VPN al servicio

Ejemplo: el objetivo es 10.20.30.40, el servicio es Postgres 5432/tcp. Permitir desde la subred VPN únicamente, descartar otros.

cr0x@server:~$ sudo nft list ruleset
table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;
    iif "lo" accept
    ct state established,related accept

    # Allow Postgres only from VPN subnet
    ip saddr 10.50.0.0/24 tcp dport 5432 accept

    # Allow admin SSH only from a management subnet (example)
    ip saddr 10.1.2.0/24 tcp dport 22 accept

    limit rate 5/second burst 10 log prefix "TARGET-DROP " flags all counter drop
  }

  chain forward {
    type filter hook forward priority 0; policy drop;
  }

  chain output {
    type filter hook output priority 0; policy accept;
  }
}

Si esto es una aplicación web en lugar de una base de datos, puedes ser más estricto: permitir solo la IP del proxy/pasarela, no toda la subred VPN.

Broma corta #2: La forma más rápida de aprender “defensa en profundidad” es depender de un solo firewall y luego conocer al becario con sudo.

Tareas prácticas: 12+ comprobaciones reales con comandos, salidas y decisiones

Estas tareas están escritas como si estuvieras de guardia y alguien preguntara: “¿Puede el usuario VPN alcanzar solo ese servicio?”
Cada tarea incluye: un comando, cómo se ve una salida realista, qué significa y qué decides a continuación.

Tarea 1: Confirmar que la interfaz VPN esté activa y tenga la subred esperada

cr0x@server:~$ ip -brief addr show wg0
wg0             UNKNOWN        10.50.0.1/24

Significado: la interfaz VPN existe y tiene la dirección esperada. Si falta o la subred es incorrecta, tus reglas de firewall pueden no coincidir.

Decisión: si el nombre de interfaz difiere (por ejemplo, wg-office), actualiza las reglas de firewall para que coincidan con la interfaz real.

Tarea 2: Verificar pares de WireGuard y estado de handshake

cr0x@server:~$ sudo wg show wg0
interface: wg0
  public key: (redacted)
  listening port: 51820

peer: (redacted)
  endpoint: 203.0.113.10:53422
  allowed ips: 10.50.0.10/32
  latest handshake: 1 minute, 12 seconds ago
  transfer: 18.23 MiB received, 41.77 MiB sent

Significado: el túnel está vivo. Si el handshake es “never”, no pierdas tiempo en política de firewall aún—arregla conectividad/autenticación primero.

Decisión: sin handshake → comprobar accesibilidad UDP y claves; handshake OK → pasar a enrutamiento y firewall.

Tarea 3: Asegurar que el reenvío de IP esté habilitado en la pasarela VPN (si esperas enrutamiento)

cr0x@server:~$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

Significado: el kernel reenviará paquetes entre interfaces. Si es 0, tu “lista de permitidos” es perfecta y inútil.

Decisión: establece en 1 solo si este host debe enrutar. Si usas diseño solo-proxy, puede que quieras el reenvío desactivado.

Tarea 4: Inspeccionar la política forward de nftables y los contadores

cr0x@server:~$ sudo nft list chain inet filter forward
table inet filter {
  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state established,related accept
    iif "wg0" ip saddr 10.50.0.0/24 ip daddr 10.20.30.40 tcp dport 5432 accept
    iif "wg0" limit rate 10/second burst 20 log prefix "VPN-DROP " flags all counter drop
    counter packets 1294 bytes 188290 drop
  }
}

Significado: existe un drop por defecto y tienes un permiso explícito. Los contadores te dicen si el tráfico está golpeando descartes.

Decisión: si los contadores de drop aumentan cuando un usuario prueba el servicio permitido, tu regla de permitidos está mal (IP, puerto, interfaz o protocolo incorrectos).

Tarea 5: Confirmar la ruta hacia la red objetivo en la pasarela VPN

cr0x@server:~$ ip route get 10.20.30.40
10.20.30.40 via 10.20.30.1 dev eth0 src 10.20.30.10 uid 0
    cache

Significado: la pasarela sabe cómo llegar al objetivo y qué IP de origen usará. Si enruta por la interfaz equivocada, tus expectativas de NAT/firewall se rompen.

Decisión: ruta equivocada → arregla enrutamiento antes de tocar el firewall. Los firewalls no arreglan topología.

Tarea 6: Validar la ruta de retorno (desde el objetivo de vuelta a la subred VPN)

cr0x@server:~$ ssh cr0x@10.20.30.40 'ip route get 10.50.0.10'
10.50.0.10 via 10.20.30.10 dev eth0 src 10.20.30.40 uid 0
    cache

Significado: el objetivo sabe devolver tráfico a la pasarela VPN para los clientes VPN. Si no es así, verás comportamiento “SYN enviado, sin respuesta”.

Decisión: si la ruta de retorno es incorrecta y no puedes cambiarla, considera NAT en la pasarela (pero mantenlo estrechamente acotado).

Tarea 7: Probar el único puerto permitido desde la perspectiva de un cliente VPN (usando nc)

cr0x@server:~$ nc -vz 10.20.30.40 5432
Connection to 10.20.30.40 5432 port [tcp/postgresql] succeeded!

Significado: la ruta de red y las reglas de firewall permiten el puerto. Esto no significa que la autenticación sea correcta—solo que TCP conecta.

Decisión: si falla, comprueba descartes en pasarela/objetivo. Si tiene éxito, garantiza que todo lo demás falle.

Tarea 8: Probar denegación: intentar un puerto prohibido en el mismo objetivo

cr0x@server:~$ nc -vz 10.20.30.40 22
nc: connect to 10.20.30.40 port 22 (tcp) failed: Operation timed out

Significado: el timeout suele ser “descartado por firewall.” Un “Connection refused” significaría que el firewall lo permitió pero el servicio no escucha.

Decisión: si obtienes “refused” en un puerto prohibido, estás permitiendo demasiado. Arregla el firewall de pasarela/objetivo.

Tarea 9: Probar denegación: intentar un host interno que debería ser inalcanzable

cr0x@server:~$ nc -vz 10.20.30.41 5432
nc: connect to 10.20.30.41 port 5432 (tcp) failed: Operation timed out

Significado: estás restringiendo por IP destino, no “cualquier servidor en ese puerto.” Bien.

Decisión: si conecta, tu regla de permitidos es demasiado amplia (subnet destino en lugar de host, o una regla posterior “accept all”).

Tarea 10: Revisar conntrack para flujos inesperados (detectar “conectó de alguna forma”)

cr0x@server:~$ sudo conntrack -L -p tcp 2>/dev/null | head -n 5
tcp      6 431999 ESTABLISHED src=10.50.0.10 dst=10.20.30.40 sport=49822 dport=5432 src=10.20.30.40 dst=10.50.0.10 sport=5432 dport=49822 [ASSURED] mark=0 use=1
tcp      6 119 SYN_SENT src=10.50.0.10 dst=10.20.30.40 sport=49830 dport=22 src=10.20.30.40 dst=10.50.0.10 sport=22 dport=49830 mark=0 use=1

Significado: puedes ver conexiones intentadas, incluso las fallidas. Entradas SYN_SENT que perduran pueden indicar descartes o problemas de ruta de retorno.

Decisión: si ves flujos establecidos hacia destinos/puertos que no permitiste, tu política está filtrándose—audita el orden del firewall y cualquier excepción de NAT.

Tarea 11: Ver logs de descartes en vivo (sin ahogarse)

cr0x@server:~$ sudo journalctl -k -f | grep 'VPN-DROP' | head
Dec 28 10:22:01 vpn-gw kernel: VPN-DROP IN=wg0 OUT=eth0 SRC=10.50.0.10 DST=10.20.30.40 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=51123 DF PROTO=TCP SPT=49830 DPT=22 WINDOW=64240 SYN

Significado: el firewall está bloqueando activamente. Obtienes evidencia concreta: IP origen, destino y puerto.

Decisión: si ves descartes hacia IPs internas inesperadas, has demostrado por qué el principio de menor privilegio importa. Mantén eso bloqueado.

Tarea 12: Confirmar que el servidor objetivo ve las IPs reales de los clientes (o no)

cr0x@server:~$ ssh cr0x@10.20.30.40 'sudo ss -tnp sport = :5432 | head -n 5'
State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB  0      0      10.20.30.40:5432 10.50.0.10:49822 users:(("postgres",pid=1421,fd=7))

Significado: el objetivo ve la IP del cliente VPN (10.50.0.10). Si en su lugar ve la IP de la pasarela VPN, hay NAT en juego.

Decisión: prefieres ver las IPs reales de los clientes para auditoría y controles por usuario. Si debes NATear, compensa con logs en la pasarela y autenticación fuerte en la aplicación.

Tarea 13: Verificar comportamiento de DNS (descubrimiento interno accidental es común)

cr0x@server:~$ resolvectl status | sed -n '1,40p'
Global
       Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 1.1.1.1
       DNS Servers: 1.1.1.1 8.8.8.8

Significado: este cliente no está usando DNS interno, por lo que el nombrado interno no se está filtrando por el túnel (o usándose para enumerar servicios).

Decisión: si empujas DNS interno a clientes VPN, asegúrate de no permitir también acceso a todo lo que esos nombres resuelven.

Tarea 14: Probar que las reglas del firewall persisten tras reinicios

cr0x@server:~$ sudo systemctl is-enabled nftables
enabled

Significado: el firewall volverá tras el reinicio. Si está deshabilitado, tu política estricta podría ser un milagro en tiempo de ejecución.

Decisión: habilítalo y guarda reglas en una configuración gestionada. Si lo haces a mano en producción, al menos documenta y haz commit.

Guía rápida de diagnóstico

Cuando “el usuario VPN no puede alcanzar el puerto permitido” ocurre, necesitas una forma rápida de encontrar el punto de estrangulamiento sin danzas interpretativas.
Aquí está el orden que desperdicia menos tiempo.

1) Confirma que el túnel esté arriba (no depures política sin túnel)

  • WireGuard: sudo wg show wg0 → busca handshake reciente y contadores de transferencia crecientes.
  • OpenVPN: revisa logs del servidor y estado del cliente; confirma que el cliente tiene una IP en el pool VPN esperado.

Si no hay handshake, estás en territorio de “alcance de red/llaves/autenticación”, no de segmentación.

2) Confirma que el cliente realmente intenta el destino/puerto correcto

  • Ejecuta nc -vz target port desde el cliente.
  • Comprueba si hace timeout (descartado) o rechaza (servicio inalcanzable pero permitido).

3) Busca descartes en la pasarela VPN primero

  • Inspecciona contadores y logs de nftables/iptables para el flujo intentado.
  • Si la pasarela no lo ve, el cliente podría no enrutar hacia el túnel, o estás probando desde la interfaz equivocada.

4) Valida enrutamiento y ruta de retorno

  • En la pasarela: ip route get target.
  • En el objetivo: ip route get vpn_client_ip.

El enrutamiento es la razón número uno por la que “debería funcionar” no funciona, sobre todo cuando NAT está medio configurado.

5) Finalmente, revisa el firewall local del objetivo y el binding del servicio

  • Firewall del objetivo: confirma que permite desde la subred VPN al puerto del servicio.
  • Servicio: confirma que escucha en la interfaz correcta (no solo en localhost).

Errores comunes (síntomas → causa raíz → solución)

Error 1: “Solo empujamos una ruta, así que los usuarios solo pueden llegar a un servidor”

Síntomas: Un usuario puede alcanzar otras IPs internas añadiendo rutas manualmente, o usando rutas existentes y hairpins de NAT.

Causa raíz: Confundir la configuración de enrutamiento del cliente con autorización.

Solución: Hacer cumplir la lista de permitidos en la ruta forward de la pasarela VPN (y, idealmente, en el objetivo). Denegar por defecto. Registrar descartes.

Error 2: Permitir “subred VPN → any” temporalmente, luego olvidarlo

Síntomas: Semanas después, una revisión de seguridad descubre que usuarios VPN pueden RDP a servidores aleatorios. Nadie recuerda por qué.

Causa raíz: Cambios de emergencia sin rollback o caducidad.

Solución: Poner límite temporal a excepciones. Usar gestión de configuración y revisión de código para reglas de firewall. Añadir alerta para aceptaciones amplias desde interfaces VPN.

Error 3: Interpretar mal “Connection refused” como “bloqueado”

Síntomas: Crees que el firewall está funcionando porque las pruebas fallan, pero fallan con “refused”, no con “timeout”.

Causa raíz: El firewall está permitiendo el tráfico; el servicio lo rechaza porque no está escuchando o controla el acceso.

Solución: Para rutas prohibidas, quieres timeouts/descartes (o rechazos explícitos si esa es tu política). Revisa contadores del firewall y ss -lntp en el servidor.

Error 4: NAT oculta la identidad del cliente, rompiendo auditoría y controles del servidor

Síntomas: Logs del objetivo muestran todas las conexiones desde la IP de la pasarela VPN; límites por usuario o listas de permitidos fallan.

Causa raíz: Masquerade aplicado ampliamente, a menudo como solución rápida para rutas de retorno.

Solución: Preferir enrutamiento real. Si debes NATear, acótalo a un destino/puerto y compensa con logs en la pasarela y autenticación fuerte en la app.

Error 5: Reglas de firewall en la cadena equivocada (INPUT vs FORWARD)

Síntomas: El firewall de la pasarela parece estricto, pero los clientes VPN aún alcanzan servicios internos.

Causa raíz: Las reglas se aplicaron a INPUT (tráfico al gateway) en lugar de FORWARD (tráfico a través del gateway).

Solución: Poner reglas de permitir/denegar en la ruta de reenvío. Mantener INPUT para proteger la propia pasarela.

Error 6: IPv6 elude silenciosamente tu política solo IPv4

Síntomas: Bloqueaste IPv4, pero los usuarios aún pueden llegar. Capturas muestran flujos IPv6.

Causa raíz: Clientes y redes dual-stack, con reglas de firewall escritas solo para IPv4.

Solución: Usar table inet en nftables y controlar explícitamente IPv6. O desactivar IPv6 en la interfaz VPN si es aceptable.

Error 7: DNS filtra la topología interna

Síntomas: Incluso cuando está bloqueado, los usuarios VPN pueden consultar DNS interno y enumerar nombres de servicios.

Causa raíz: Servidor DNS accesible desde la subred VPN, o DNS permitido “porque es inofensivo”.

Solución: No permitir DNS interno a menos que sea necesario. Si es necesario, permitir solo a un resolvedor controlado/split-horizon que no revele todo.

Error 8: Registrar cada paquete descartado sin limitación de tasa

Síntomas: Logs del kernel se inundan; discos se llenan; la respuesta a incidentes se convierte en “¿por qué syslog está caído?”

Causa raíz: Reglas de registro sin control combinadas con escaneo o clientes mal configurados.

Solución: Limitar tasa de logs, agregar contadores y muestrear inteligentemente. Tu registro debe sobrevivir a días malos, no causarlos.

Listas de verificación / plan paso a paso

Plan paso a paso: de “VPN abierta” a “un servidor/puerto solo”

  1. Elige el punto de aplicación. Usa la pasarela VPN como punto de estrangulamiento principal. Decide si también aplicas en el servidor objetivo (deberías).
  2. Define la tupla exacta: origen (subred VPN), IP destino, puerto destino, protocolo. Escríbelo. Si es “lo que use la app”, no has terminado.
  3. Crea una subred VPN dedicada. No la mezcles con rangos internos existentes. La segmentación comienza con límites de direcciones limpios.
  4. Configura denegar por defecto en la ruta de reenvío. En la pasarela, la cadena forward debe ser DROP por defecto.
  5. Añade una regla de permiso única. Completamente calificada. No subredes a menos que el requisito sea realmente múltiples destinos.
  6. Maneja el enrutamiento de retorno. Prefiere una ruta en el objetivo (o en el router upstream) de vuelta a la subred VPN vía la pasarela. Usa NAT solo si debes.
  7. Restringe el servidor objetivo. Permite solo la subred VPN (o la IP de pasarela/proxy) al puerto del servicio.
  8. Decide sobre DNS. Si el cliente no necesita DNS interno, no lo proveas. Si lo necesita, filtralo y acótalo.
  9. Hazlo observable. Contadores, logs con limitación de tasa y la costumbre de revisarlos tras cambios.
  10. Prueba la denegación, no solo el permiso. Intenta conectar a puertos/hosts prohibidos y asegúrate de que falle como esperas.
  11. Hazlo persistente. Servicios systemd habilitados, configs en control de versiones y cambios revisados.
  12. Ejecuta una revisión periódica de accesos. Los requisitos cambian; tu firewall no debería “deslizarse” silenciosamente con ellos.

Lista de control de control de cambios (para los adultos en la sala)

  • ¿Existe una regla explícita de lista de permitidos para el único servidor/puerto? (No una regla vaga de subred.)
  • ¿Está la política forward por defecto en DROP?
  • ¿Los logs/contadores confirman que solo ese flujo tiene éxito?
  • ¿Hay una caducidad en cualquier excepción temporal?
  • ¿Podemos probar la ruta de retorno sin NAT amplio?
  • ¿Se maneja IPv6 intencionalmente?
  • ¿El servidor objetivo también está restringido?

Tres micro-historias corporativas desde el terreno

Micro-historia 1: El incidente causado por una suposición equivocada

Una empresa mediana tenía una “VPN de proveedor” para una integración de nómina. El proveedor necesitaba acceso HTTPS a un endpoint interno.
El equipo de red empujó una ruta única a ese host y se sintió orgulloso de su moderación. Incluso lo apuntaron en un ticket de cambio.

Meses después, una auditoría interna mostró que la cuenta del proveedor podía alcanzar un servidor de archivos. Nadie lo creyó. La tabla de rutas en el portátil del proveedor parecía limpia.
Pero la pasarela VPN también hacía NAT para “hacer las cosas más fáciles”, y la política de reenvío era permisiva: permitía a la subred VPN alcanzar redes internas porque
“si no, el troubleshooting es difícil.”

El proveedor nunca intentó acceder al servidor de archivos. Ese no era el punto. El punto fue que un endpoint proveedor comprometido ahora tenía un camino pavimentado a activos internos.
La suposición fue “enrutamiento = restricción.” La realidad fue “el enrutamiento es una sugerencia; los firewalls son la aplicación.”

La solución fue aburrida: DROP por defecto en forward, una regla de permitidos única y un firewall del lado del servidor en el endpoint.
Lo difícil fue organizacional: admitir que “empujamos una ruta” era teatro de seguridad.

Micro-historia 2: La optimización que salió mal

Otro sitio quería reducir carga en su pasarela VPN. Habilitaron split tunneling y empujaron DNS interno para que los portátiles resolvieran servicios internos.
La idea: solo el tráfico de la app necesario va por el túnel; el resto se queda local. Los gráficos de ancho de banda se veían mejor. Todos aplaudieron en voz baja.

Luego vino el extraño bug: un subconjunto de usuarios accedía intermitentemente al “único servicio permitido” pero a veces sufría timeouts.
El canal de incidentes se llenó de conjeturas seguras: “WireGuard es inestable”, “la base de datos está sobrecargada”, “probablemente es MTU”.

El problema real fue mundano operativamente. Con split tunneling y DNS interno, los clientes resolvían nombres internos estando fuera de la red,
y algunos portátiles tenían solapamientos de redes locales (routers domésticos usando el mismo rango privado que la empresa). A veces el cliente enrutaba tráfico “interno” localmente,
sin entrar en la VPN. Así que el firewall de la pasarela ni siquiera estaba en el camino.

La “optimización” ahorró ancho de banda pero creó una lotería de enrutamiento. La resolución fue dejar de confiar en DNS+split tunneling para seguridad determinista.
Fijaron el objetivo por IP para acceso VPN, resolvieron los solapes de direcciones y forzaron el tráfico del servicio por el túnel. Aburrido, predecible, correcto.

Micro-historia 3: La práctica aburrida pero correcta que salvó el día

Un equipo de servicios financieros tenía la costumbre que parecía innecesaria: cada cambio de firewall incluía una prueba de “probar la denegación”.
No solo “funciona”, sino “¿falla todo lo demás?” Guardaban un pequeño script que intentaba conexiones a un puñado de puertos internos prohibidos.

Una tarde, una actualización rutinaria del SO en la pasarela VPN cambió el backend del firewall. Las reglas cargaron, pero un nombre de cadena cambió,
y su política de “permitir solo un puerto” efectivamente se convirtió en “permitir cualquier cosa establecida”, con reglas accept demasiado amplias añadidas por un script legado.

Nadie lo notó al principio, porque el servicio permitido seguía funcionando. Así es como se esconden estas fallas: el éxito no prueba la restricción.
Pero su suite de pruebas post-cambio lo detectó inmediatamente. Los intentos de conexión prohibidos no hacían timeout—se conectaban.

Revirtieron en minutos y volvieron a aplicar las reglas correctamente. Sin informe de incidente. Sin narrativa de brecha. Solo una práctica pequeña y aburrida que evitó
un problema grande y emocionante. Así es la fiabilidad cuando funciona: no pasa nada.

Preguntas frecuentes

1) ¿Puedo aplicar “un servidor/puerto solo” solo con AllowedIPs de WireGuard?

No. AllowedIPs es enrutamiento y asociación de pares. Ayuda, pero no es el límite de aplicación que quieres.
Usa reglas de firewall en la pasarela (y preferiblemente en el objetivo).

2) ¿Debería usar NAT o enrutamiento adecuado?

Prefiere enrutamiento adecuado para que el objetivo vea las IPs reales de los clientes y puedas auditar y tener listas de permitidos por servidor.
Usa NAT solo cuando no puedas arreglar rutas de retorno, y acótalo al destino/puerto único.

3) ¿Es compatible el “split tunnel” con el principio de menor privilegio?

A veces, pero es fácil equivocarse. El split tunnel aumenta la dependencia en decisiones correctas de enrutamiento del cliente y reduce tu capacidad de observar centralmente los caminos.
Para “un servidor/puerto solo”, forzar el tráfico de ese servicio por el túnel suele ser más limpio.

4) ¿Dónde debería aplicar restricciones: pasarela, objetivo o ambos?

Ambos. La aplicación en la pasarela reduce el radio de impacto y te da registro central. La aplicación en el objetivo protege contra rutas alternativas y malas configuraciones.
Defensa en profundidad, sin voz de marketing.

5) ¿Cómo restrinjo por usuario, no solo por subred VPN?

El camino más simple es IPs VPN por usuario y reglas que coincidan ip saddr 10.50.0.10 en lugar de toda la subred.
Mejor aún: poner el servicio detrás de un proxy consciente de identidad para que el acceso esté ligado a la autenticación, no solo a la IP de origen.

6) ¿Qué pasa con acceso a DNS y servidores de hora?

No los permitas automáticamente. Si tu servicio único está especificado por IP, puede que no necesites DNS interno en absoluto.
Si debes proporcionar DNS, permite solo a un resolvedor controlado y trata al DNS como metadata sensible.

7) ¿Es mejor descartar tráfico que rechazarlo?

El descarte (timeout) revela menos sobre lo que existe, pero puede ralentizar la resolución de problemas.
Rechazar puede hacer la experiencia de usuario más clara. Para segmentación VPN, normalmente descarto por defecto y registro con limitación de tasa; uso rechazos explícitos solo cuando es necesario.

8) ¿Cómo pruebo ante auditores que el acceso está restringido?

Muestra las reglas del firewall (denegar por defecto + un único permitir), muestra contadores/logs de intentos denegados y evidencia de pruebas:
conexión al puerto permitido tiene éxito, conexiones a puertos/hosts prohibidos hacen timeout. Si no puedes producir eso rápido, no lo estás controlando realmente.

9) ¿Y IPv6—debo preocuparme?

Sí, porque tus clientes ya lo usan. Si solo haces firewall en IPv4, puedes permitir caminos IPv6 accidentalmente.
Usa tablas inet en nftables o desactiva IPv6 en la interfaz VPN si encaja con tu entorno.

10) Si el servicio es HTTPS, ¿aún debo hacer “un puerto solo”?

Sí, pero considera publicar el servicio a través de un proxy inverso donde puedas aplicar identidad, mTLS, registro de peticiones
y límites de tasa. La restricción de red es necesaria; no es suficiente.

Conclusión: próximos pasos que funcionan

“Acceso VPN de oficina” no es una elección binaria entre “sin acceso” y “todo adentro”. Puedes—y debes—diseñar para un servidor y un puerto.
Así es como se ve el principio de menor privilegio cuando puedes medirlo.

  1. Elige tu punto de aplicación: implementa reenvío con denegación por defecto en la pasarela VPN.
  2. Escribe una regla de permiso precisa: interfaz + origen + IP destino + protocolo + puerto.
  3. Valida el enrutamiento de retorno: arregla rutas primero; usa NAT acotado solo si es inevitable.
  4. Añade restricciones del lado del servidor: el objetivo debe aceptar el puerto del servicio solo desde la subred VPN (o la IP de pasarela/proxy).
  5. Prueba la denegación tras cada cambio: prueba puertos/hosts prohibidos y observa contadores/logs con limitación de tasa.

Haz esto, y tu VPN dejará de ser un pasillo hacia el edificio. Será una puerta cerrada con una llave específica.
No es paranoia; es higiene profesional.

← Anterior
ZFS usando NVMe como SLOG: cuándo es perfecto y cuándo es excesivo
Siguiente →
Error de la REST API de WordPress: qué rompe REST y cómo solucionarlo

Deja un comentario