WireGuard VPN: Configura tu propio servidor sin abrir agujeros innecesarios

¿Te fue útil?

Quieres una VPN porque tu portátil navega por Wi‑Fi de cafeterías, redes de hoteles y ese “FREE_AIRPORT_WIFI” que nunca sabes qué es. También quieres autohospedar porque has leído suficientes informes de brechas como para saber que “confía en nosotros” no es un modelo de seguridad.

La trampa: la gente trata “configurar una VPN” como “abrir un montón de puertos y cruzar los dedos”. Así es como se consigue un servidor accesible, sí—pero también por atacantes, escáneres y por tu yo de las 3 a.m. preguntándose por qué el enrutamiento está roto. Vamos a hacerlo de la forma aburrida y correcta: un puerto UDP expuesto (o ninguno si usas una ruta solo saliente), un cortafuegos estricto, enrutamiento explícito y una rutina de diagnóstico que no dependa de sensaciones.

Postura de seguridad: “un puerto, una responsabilidad”

WireGuard no es una suite de demonios y plugins. Es un protocolo pequeño y una implementación pequeña. Trátalo como un aparato de propósito único:
expón un puerto UDP (si debes), acepta paquetes solo en ese puerto, desde cualquier lugar, y deja que el apretón de manos criptográfico de WireGuard haga el trabajo pesado.

Tu cortafuegos no está para “hacer la VPN segura”. Tu cortafuegos está para evitar que, por accidente, expongas todo lo demás mientras intentas ser ingenioso. No seas ingenioso.

Cómo suelen verse los “agujeros innecesarios”

  • Abrir SSH al mundo “solo por ahora” y luego olvidarlo durante un año.
  • Redirigir puertos al azar porque un post dijo que había que hacerlo, aunque WireGuard solo necesita un puerto UDP.
  • Permitir reenvío desde la interfaz VPN a tu LAN sin política, porque “está cifrado”.
  • Instalar una interfaz web que se ejecuta como root porque “simplifica la gestión”.

El cifrado no es permiso. Es transporte. El permiso es enrutamiento y políticas.

Una cita, porque sigue siendo cierta

“La esperanza no es una estrategia.” — James Cameron

No es una frase de operaciones, pero funciona: no esperes que tu cortafuegos esté bien; verifícalo.

Hechos y contexto de WireGuard (lo que la gente recuerda mal)

Son intencionalmente breves. La idea es calibrar tu modelo mental para que dejes de resolver problemas en la capa equivocada.

  1. WireGuard es solo UDP por diseño. Si persigues “reintentos TCP”, estás depurando lo equivocado.
  2. Usa un apretón de manos basado en Noise (NoiseIK). Esa es una razón por la que es rápido y relativamente simple comparado con pilas VPN más antiguas.
  3. Entró en el kernel de Linux en 5.6 (2020). Antes corría fuera del árbol; ahora es ciudadano de primera clase en distribuciones modernas.
  4. “Peers” no son “usuarios”. Un peer es un par de claves y una política de enrutamiento; la identidad vive en las claves, no en nombres de usuario.
  5. AllowedIPs es tanto enrutamiento como control de acceso. Determina qué rutas se instalan y qué tráfico se acepta para un peer.
  6. La traversía NAT no es magia; son keepalives. PersistentKeepalive es básicamente que llamas educadamente cada N segundos para que firewalls con estado no te olviden.
  7. WireGuard no tiene renegociación como IPsec. Las claves rotan, pero el modelo es intencionalmente minimalista.
  8. Evita la proliferación de algoritmos. Las elecciones criptográficas son deliberadamente limitadas; no puedes “elegir lo que quieras”, y eso es una característica, no un fallo.

Broma #1: Una VPN que “soporta todos los cifrados” es como un restaurante con un menú de 40 páginas—nadie está verificando la frescura.

Elige tu topología: hub en VPS, hub en casa o “sin puertos entrantes”

La forma más limpia de evitar agujeros innecesarios es decidir qué intentas conectar. Hay tres patrones comunes y no son intercambiables.

Topología A: VPS como hub (recomendado para la mayoría)

Alquilas un VPS pequeño, expones un puerto UDP (por ejemplo, 51820/udp) y conectas clientes a él. Si quieres acceso a dispositivos domésticos, ejecutas también un peer WireGuard en casa que se conecta al VPS y anuncia tus rutas domésticas.

Por qué es bueno:

  • No necesitas exponer tu IP doméstica ni abrir agujeros en un router de ISP inestable.
  • Tienes un endpoint público estable y un cortafuegos predecible.
  • Puedes mantener SSH restringido a una red de gestión o a un bastión.

Topología B: Servidor en casa como hub (funciona, pero dependes del router)

Rediriges un puerto UDP desde tu router doméstico al servidor WireGuard. Está bien cuando está bien. También está a un update de firmware de distancia de “¿por qué se reinició el router y perdió mis reglas?”

Haz esto solo si entiendes el comportamiento del cortafuegos con estado de tu router doméstico y puedes tolerar cortes ocasionales.

Topología C: “Sin puertos entrantes”: solo salidas usando un rendezvous

Si tu entorno prohíbe puertos entrantes (redes corporativas, CGNAT, ISP hostiles), aún puedes construir un sistema funcional. El enfoque habitual es:

  • Un VPS público ejecuta el servidor WireGuard (un puerto UDP abierto en el VPS).
  • Todo lo demás (gateway doméstico, portátiles) se conecta saliendo a ese VPS.

Observa lo que sucede: no eliminaste la exposición; la moviste a una caja pública controlada y mantuviste tu lado doméstico cerrado a entrantes. Esa suele ser la compensación correcta.

Decisiones de endurecimiento que realmente importan

1) Pon el endpoint de WireGuard en un host mínimo

No co-albergues tu endpoint VPN con tu clúster Kubernetes de juguete, tu galería de fotos familiar y una app Node experimental. Cada servicio adicional multiplica tu radio de impacto. Si necesitas una navaja suiza, vale. Pero no te sorprendas cuando te cortes.

2) Restringe SSH como si lo dijeras en serio

SSH suele ser el verdadero “agujero innecesario”. Si este servidor es accesible en Internet público, por defecto:

  • Solo claves SSH, sin autenticación por contraseña.
  • Filtra SSH por rango IP de confianza (oficina, casa o tu subred VPN).
  • Considera una interfaz de gestión separada o un bastión si lo manejas para un equipo.

3) Política de cortafuegos: permite WireGuard, permite establecido, descarta el resto

El conjunto de reglas básico es aburrido y suficiente:

  • Entrante: permitir UDP 51820 (o tu puerto elegido) al host WireGuard.
  • Entrante: permitir SSH solo desde una fuente restringida.
  • Reenvío: permitir solo lo que intentes desde wg0 hacia otras interfaces.

La interfaz VPN no es un pase libre a tu LAN. Es un segmento de red. Trátalo como tal.

4) La política de enrutamiento es tu control de acceso

WireGuard no hace “permisos de usuario” como un concentrador VPN empresarial. Tus perillas de control son:

  • AllowedIPs por peer (qué rutas puede enviar y recibir un peer).
  • Reglas de cortafuegos en wg0 (qué tráfico está permitido una vez que llega).
  • Decisiones de reenvío IP / NAT (qué va a enrutar el servidor hacia adelante).

5) MTU: el asesino silencioso

WireGuard es rápido. También está dispuesto a hundir tu rendimiento si encapsulas dentro de PPPoE, VLANs u otros túneles y no ajustas la MTU. En caso de duda, empieza con 1420 en wg0 y mide.

6) Registro: lo suficiente para diagnosticar, no tanto como para filtrar

WireGuard es intencionalmente silencioso. Eso es bueno. Pero aún necesitas observabilidad a nivel de sistema:

  • Contadores de interfaz y rutas.
  • Contadores de paquetes del cortafuegos.
  • Logs del kernel por paquetes descartados (con cuidado).

Listas de verificación / plan paso a paso

Lista de verificación: antes de tocar el servidor

  • Decide tu subred VPN (ejemplo: 10.6.0.0/24). No reutilices una subred LAN.
  • Decide túnel dividido vs túnel completo por cliente. Por defecto, túnel dividido salvo que necesites “todo el tráfico por la VPN”.
  • Elige tu puerto de endpoint (51820/udp por defecto está bien). La seguridad por oscuridad no es un plan, pero reducir ruido sí ayuda.
  • Anota qué subredes privadas quieres alcanzables (ejemplo: 192.168.50.0/24 en casa).
  • Decide si el servidor debe NATear el tráfico de clientes a Internet (túnel completo) o solo enrutar a redes internas.

Paso a paso: hub en VPS con Ubuntu y nftables

Este flujo asume:

  • Interfaz pública del servidor: eth0
  • Interfaz WireGuard: wg0
  • Subred VPN: 10.6.0.0/24
  • IP VPN del servidor: 10.6.0.1
  • IP VPN del cliente: 10.6.0.2

Lista de verificación: en qué deberías terminar

  • Solo UDP 51820 expuesto entrante (más SSH fuertemente restringido si hace falta).
  • wg0 activo al inicio, configuración propiedad de root, permisos cerrados.
  • Reenvío IP habilitado solo si realmente enrutas entre interfaces.
  • Reglas de cortafuegos que permitan explícitamente el tráfico deseado, y nada más.
  • Configs de cliente que coincidan con AllowedIPs, endpoint y elección de DNS.

Tareas prácticas: comandos, salidas y lo que decides

Esta sección es deliberadamente práctica: comando, salida realista y luego la decisión que tomas. Si te saltas la parte de “decisión”, solo estás tipeando hechizos.

Tarea 1: confirmar soporte de WireGuard y datos básicos del SO

cr0x@server:~$ uname -r
6.5.0-41-generic

Qué significa: Kernel moderno; soporte in-kernel de WireGuard disponible.
Decisión: Usa wireguard-tools y el módulo del kernel; evita envoltorios en espacio de usuario a menos que tengas una plataforma rara.

Tarea 2: instalar las herramientas de WireGuard

cr0x@server:~$ sudo apt-get update
Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease
Reading package lists... Done
cr0x@server:~$ sudo apt-get install -y wireguard wireguard-tools nftables
Reading package lists... Done
Setting up wireguard-tools (1.0.20210914-1ubuntu2) ...
Setting up wireguard (1.0.20210914-1ubuntu2) ...
Setting up nftables (1.0.2-1ubuntu1) ...

Qué significa: Las herramientas están presentes. Decisión: Procede con la interfaz gestionada por systemd y reglas nftables. Si estás en una distro que viene con ufw por defecto, decide ahora si lo mantendrás o lo desactivarás; mezclar cortafuegos es un pasatiempo, no una carrera.

Tarea 3: generar claves del servidor con permisos correctos

cr0x@server:~$ umask 077
cr0x@server:~$ sudo mkdir -p /etc/wireguard
cr0x@server:~$ sudo bash -c 'wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub'
cr0x@server:~$ sudo ls -l /etc/wireguard
total 8
-rw------- 1 root root 45 Oct 12 10:20 server.key
-rw------- 1 root root 45 Oct 12 10:20 server.pub

Qué significa: Las claves existen y no son legibles por cualquier usuario.
Decisión: Si los permisos son más amplios que 600, arréglalos; no continúes con manejo descuidado de claves.

Tarea 4: escribir la configuración de la interfaz del servidor

cr0x@server:~$ sudo bash -c 'cat > /etc/wireguard/wg0.conf <<EOF
[Interface]
Address = 10.6.0.1/24
ListenPort = 51820
PrivateKey = '"$(sudo cat /etc/wireguard/server.key)"'
SaveConfig = false

# If you will route to other networks, enable forwarding and add firewall rules (below).
EOF'
cr0x@server:~$ sudo chmod 600 /etc/wireguard/wg0.conf

Qué significa: El servidor está configurado con una dirección interna y un puerto UDP.
Decisión: Mantén SaveConfig=false a menos que quieras que los cambios en tiempo de ejecución se escriban automáticamente. Autoescribir configs es cómodo hasta que deja de serlo.

Tarea 5: levantar la interfaz y verificar que existe

cr0x@server:~$ sudo systemctl enable --now wg-quick@wg0
Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service → /lib/systemd/system/wg-quick@.service.
cr0x@server:~$ ip a show wg0
6: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.6.0.1/24 scope global wg0
       valid_lft forever preferred_lft forever

Qué significa: La interfaz está arriba con MTU 1420 (por defecto de wg-quick).
Decisión: Si tu entorno necesita una MTU distinta (común con PPPoE), fíjala explícitamente ahora en lugar de perseguir fantasmas después.

Tarea 6: confirmar que el servidor escucha en el puerto UDP elegido

cr0x@server:~$ sudo ss -lunp | grep 51820
UNCONN 0      0           0.0.0.0:51820       0.0.0.0:*    users:(("wireguard",pid=1123,fd=6))

Qué significa: Un socket UDP está abierto y vinculado.
Decisión: Si no está escuchando, detente e inspecciona journalctl por fallos de wg-quick antes de tocar reglas de cortafuegos.

Tarea 7: configurar nftables para permitir solo lo que necesitas

cr0x@server:~$ sudo bash -c 'cat > /etc/nftables.conf <<EOF
flush ruleset

table inet filter {
  chain input {
    type filter hook input priority 0;
    policy drop;

    ct state established,related accept
    iif "lo" accept

    # Allow WireGuard
    udp dport 51820 accept

    # Allow SSH only from a trusted source (example: your office IP)
    ip saddr 203.0.113.10 tcp dport 22 accept

    # Optional: allow ICMP for debugging (rate-limit in real environments)
    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept

    counter reject with icmpx type port-unreachable
  }

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

    ct state established,related accept
    # Allow VPN clients to reach the server itself is handled in input;
    # forward is for routed traffic to other interfaces.

    # Example: allow VPN clients to reach a private LAN behind this server
    # iif "wg0" oif "eth0" ip daddr 192.168.50.0/24 accept
    # iif "eth0" oif "wg0" ip saddr 192.168.50.0/24 accept
  }

  chain output {
    type filter hook output priority 0;
    policy accept;
  }
}
EOF'
cr0x@server:~$ sudo systemctl enable --now nftables
cr0x@server:~$ sudo nft list ruleset | sed -n '1,60p'
table inet filter {
	chain input {
		type filter hook input priority filter; policy drop;
		ct state established,related accept
		iif "lo" accept
		udp dport 51820 accept
		ip saddr 203.0.113.10 tcp dport 22 accept
		ip protocol icmp accept
		ip6 nexthdr icmpv6 accept
		counter reject with icmpx type port-unreachable
	}

Qué significa: Drop por defecto, permitidos explícitos. Eso es lo que quieres en un host expuesto a Internet.
Decisión: Si no puedes restringir SSH por IP de origen, considera seriamente mover SSH detrás de la VPN (es decir: nada de SSH público).

Tarea 8: verificar que no expusiste otros servicios por accidente

cr0x@server:~$ sudo ss -lntup
Netid State  Recv-Q Send-Q Local Address:Port   Peer Address:Port Process
udp   UNCONN 0      0      0.0.0.0:51820       0.0.0.0:*     users:(("wireguard",pid=1123,fd=6))
tcp   LISTEN 0      4096   0.0.0.0:22          0.0.0.0:*     users:(("sshd",pid=904,fd=3))

Qué significa: Solo SSH y WireGuard están escuchando. Bien.
Decisión: Si ves oyentes sorpresa (dashboards web, exporters, agentes aleatorios), quítalos o protégelos con cortafuegos. “Pero está vinculado a 0.0.0.0” no es un rasgo de personalidad.

Tarea 9: generar un par de claves cliente y añadir un peer

cr0x@server:~$ wg genkey | tee /tmp/client1.key | wg pubkey > /tmp/client1.pub
cr0x@server:~$ sudo wg set wg0 peer $(cat /tmp/client1.pub) allowed-ips 10.6.0.2/32
cr0x@server:~$ sudo wg show wg0
interface: wg0
  public key: 3rYlF8wF1aV9rS8dK0aJ7xw5w2Vn9pYqHc0Rr3X0u2M=
  private key: (hidden)
  listening port: 51820

peer: 7bTt2yqg3fJ0zq5fQm2v0QwqYk9mM9t8Gq2dZ1gk3jA=
  allowed ips: 10.6.0.2/32

Qué significa: El servidor ahora conoce una clave de peer y qué IP VPN le pertenece.
Decisión: Mantén AllowedIPs estrictos: /32 por cliente a menos que estés haciendo enrutamiento sitio-a-sitio deliberadamente. AllowedIPs amplios causan filtraciones accidentales de enrutamiento y conflictos entre peers.

Tarea 10: crear la configuración del cliente (por defecto túnel dividido)

cr0x@server:~$ SERVER_PUB=$(sudo cat /etc/wireguard/server.pub); echo "$SERVER_PUB"
3rYlF8wF1aV9rS8dK0aJ7xw5w2Vn9pYqHc0Rr3X0u2M=
cr0x@server:~$ cat > /tmp/client1.conf <<EOF
[Interface]
PrivateKey = $(cat /tmp/client1.key)
Address = 10.6.0.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = $(sudo cat /etc/wireguard/server.pub)
Endpoint = 198.51.100.25:51820
AllowedIPs = 10.6.0.0/24
PersistentKeepalive = 25
EOF

Qué significa: Túnel dividido: el cliente solo enruta tráfico de la subred VPN dentro del túnel.
Decisión: Empieza con túnel dividido. Activa túnel completo solo cuando puedas articular por qué lo necesitas (y hayas probado el comportamiento de DNS).

Tarea 11: comprobar apretón de manos y contadores de tráfico en el servidor

cr0x@server:~$ sudo wg show
interface: wg0
  public key: 3rYlF8wF1aV9rS8dK0aJ7xw5w2Vn9pYqHc0Rr3X0u2M=
  private key: (hidden)
  listening port: 51820

peer: 7bTt2yqg3fJ0zq5fQm2v0QwqYk9mM9t8Gq2dZ1gk3jA=
  allowed ips: 10.6.0.2/32
  latest handshake: 24 seconds ago
  transfer: 18.21 KiB received, 22.77 KiB sent

Qué significa: El apretón de manos tuvo éxito y el tráfico fluye.
Decisión: Si “latest handshake” está vacío o es antiguo, depura la alcanzabilidad (cortafuegos, endpoint, NAT) antes de tocar rutas.

Tarea 12: verificar la tabla de enrutamiento y políticas en el servidor

cr0x@server:~$ ip route show
default via 203.0.113.1 dev eth0 proto dhcp src 198.51.100.25 metric 100
10.6.0.0/24 dev wg0 proto kernel scope link src 10.6.0.1
203.0.113.0/24 dev eth0 proto kernel scope link src 198.51.100.25

Qué significa: El servidor sabe que la subred VPN está en wg0.
Decisión: Si falta la ruta a 10.6.0.0/24, wg0 no está configurado correctamente; no procedas a “arreglos” de NAT/enrutamiento hasta que la interfaz base esté sana.

Tarea 13: habilitar reenvío IP solo si enrutas más allá del servidor

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

Qué significa: El servidor no enruta paquetes entre interfaces.
Decisión: Déjalo apagado a menos que lo necesites. Si haces túnel completo o sitio-a-sitio, actívalo deliberadamente y añade reglas de cortafuegos que coincidan.

cr0x@server:~$ sudo sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
cr0x@server:~$ sudo bash -c 'printf "net.ipv4.ip_forward=1\n" > /etc/sysctl.d/99-wireguard-forward.conf'

Tarea 14: añadir NAT para clientes en túnel completo (solo cuando lo necesites)

Si quieres que los clientes usen el VPS como salida a Internet, debes NATear el tráfico de wg0 a eth0. Sin ello, tendrás “conectado pero sin Internet”.

cr0x@server:~$ sudo nft add table ip nat
cr0x@server:~$ sudo nft 'add chain ip nat postrouting { type nat hook postrouting priority 100 ; }'
cr0x@server:~$ sudo nft add rule ip nat postrouting oif "eth0" ip saddr 10.6.0.0/24 masquerade
cr0x@server:~$ sudo nft list table ip nat
table ip nat {
	chain postrouting {
		type nat hook postrouting priority srcnat; policy accept;
		oif "eth0" ip saddr 10.6.0.0/24 masquerade
	}
}

Qué significa: El tráfico del cliente saldrá con la IP pública del servidor.
Decisión: Si no necesitas túnel completo, no hagas esto. NAT oculta errores como “funciona”, hasta que necesitas claridad sitio-a-sitio.

Tarea 15: validar contadores del cortafuegos durante una prueba

cr0x@server:~$ sudo nft list chain inet filter input
chain input {
	type filter hook input priority filter; policy drop;
	ct state established,related accept
	iif "lo" accept
	udp dport 51820 accept
	ip saddr 203.0.113.10 tcp dport 22 accept
	ip protocol icmp accept
	ip6 nexthdr icmpv6 accept
	counter packets 12 bytes 672 reject with icmpx type port-unreachable
}

Qué significa: El contador de reject incrementa cuando ruido aleatorio de Internet te golpea.
Decisión: Si contadores de aceptaciones inesperadas están subiendo (p. ej., SSH), aprieta las reglas. Si los contadores UDP 51820 se quedan en cero mientras los clientes se conectan, en realidad no estás alcanzando el host—revisa grupos de seguridad upstream.

Tarea 16: captura de paquetes para “el apretón de manos nunca ocurre”

cr0x@server:~$ sudo tcpdump -n -i eth0 udp port 51820 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:44:10.112233 IP 203.0.113.77.49822 > 198.51.100.25.51820: UDP, length 148
12:44:10.212244 IP 203.0.113.77.49822 > 198.51.100.25.51820: UDP, length 148
5 packets captured

Qué significa: Los paquetes llegan al servidor. Si wg aún no muestra apretón de manos, el problema probablemente sean las claves, la configuración del peer o AllowedIPs.
Decisión: Si tcpdump no muestra nada, deja de editar configs de WireGuard y arregla la alcanzabilidad (cortafuegos del proveedor/grupo de seguridad, NAT, reenvío de puertos, IP equivocada).

Enrutamiento y NAT: túnel dividido vs túnel completo sin sabotearte

Túnel dividido: riesgo mínimo, sorpresas mínimas

En túnel dividido, el cliente enruta solo subredes específicas a la VPN. AllowedIPs típicos en el cliente:

  • 10.6.0.0/24 para alcanzar otros peers VPN
  • 192.168.50.0/24 para alcanzar tu LAN doméstica vía un peer sitio-a-sitio

Pros:

  • Menos probabilidad de romper el acceso a Internet del cliente.
  • Menos uso de ancho de banda en el servidor.
  • Menos dramas de “¿por qué Netflix aparece en otro país?”.

Contras: no estás ocultando todo tu tráfico de la red local. Si necesitas eso (Wi‑Fi hostil), usa túnel completo temporalmente.

Túnel completo: potente, rompe cosas más rápido

Túnel completo significa que el cliente enruta 0.0.0.0/0 (y ::/0 para IPv6) por la VPN. Eso implica:

  • NAT o enrutamiento apropiado en el servidor.
  • DNS gestionado con cuidado para evitar filtraciones o caídas.
  • Los problemas de MTU se vuelven más visibles porque todo usa el túnel.

La línea de configuración del cliente es la diferencia entre “herramienta útil” y “dependencia de toda la red”:

  • Dividido: AllowedIPs = 10.6.0.0/24, 192.168.50.0/24
  • Completo: AllowedIPs = 0.0.0.0/0, ::/0

Sitio-a-sitio vía VPS: la forma limpia de alcanzar casa sin abrir puertos

Ejecuta WireGuard en una pequeña caja en casa (router, mini PC, NAS si hace falta) como peer que se conecta saliendo al VPS.
En el VPS, añades un peer con AllowedIPs igual a tu subred LAN doméstica. En el peer doméstico, añades una ruta y reglas de cortafuegos para reenviar entre wg0 y lan0.

Dos guardarraíles importantes:

  • Solo un peer debe “poseer” una ruta dada en AllowedIPs. Si dos peers reclaman 192.168.50.0/24, el tráfico irá al que se añadió último (y te arrepentirás).
  • En el lado doméstico, el reenvío en el cortafuegos no es opcional. Estás creando un router. Compórtate como tal.

Guion de diagnóstico rápido

Cuando WireGuard “no funciona”, la gente se descontrola: cambian puertos, reinstalan paquetes, reinician y luego declaran victoria por la razón equivocada. Aquí está la secuencia que encuentra el cuello de botella rápido.

Primero: ¿pueden los paquetes alcanzar el puerto UDP del servidor?

  • En el servidor: ss -lunp | grep 51820 (¿está escuchando?)
  • En el servidor durante un intento de conexión: tcpdump -n -i eth0 udp port 51820 (¿llegan paquetes?)
  • En entornos cloud: confirma que el cortafuegos del proveedor/grupo de seguridad permite UDP 51820.

Si los paquetes no llegan, nada más importa. Arregla la ruta de red.

Segundo: ¿WireGuard forma un apretón de manos?

  • wg show → comprueba “latest handshake”
  • Si no hay apretón de manos: IP/puerto de endpoint equivocado, claves equivocadas o cortafuegos del servidor que descarta UDP.

Tercero: ¿puedes pasar tráfico dentro del túnel?

  • Haz ping a la IP wg del servidor desde el cliente.
  • Servidor: observa que los contadores de transfer en wg show aumenten.
  • Si hay apretón de manos pero falla el tráfico, sospecha de un desajuste de AllowedIPs, cortafuegos en wg0 o MTU.

Cuarto: enrutamiento/NAT y DNS (la categoría “conectado pero inútil”)

  • Túnel dividido: asegura que existan rutas en el cliente para las subredes internas esperadas.
  • Túnel completo: asegura que el servidor tenga masquerade NAT y forwarding activado.
  • DNS: asegura que tu cliente apunte a un resolutor alcanzable por la modalidad de enrutamiento elegida.

Quinto: cuellos de botella de rendimiento

  • Revisa MTU y síntomas de fragmentación primero (HTTPS lento, bloqueos, algunos sitios fallan).
  • Revisa saturación de CPU en el servidor (VPS barato + alto throughput puede ser cuello de botella).
  • Revisa path MTU y comportamiento de carrier-grade NAT si estás en redes móviles.

Broma #2: Depurar VPN es como plomería—el 99% del tiempo, el problema está donde no miraste porque parecía “obvio”.

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

1) Síntoma: “El apretón de manos nunca ocurre”

Causa raíz: Puerto UDP bloqueado upstream (cortafuegos cloud, ISP, reenvío de puerto equivocado), o endpoint IP/puerto equivocado en la configuración del cliente.

Solución: Verifica con ss que el servidor está escuchando, luego con tcpdump que llegan UDP entrantes. Si no llegan paquetes, arregla reglas upstream antes de tocar configs de WireGuard.

2) Síntoma: “El apretón de manos ocurre, pero no puedo hacer ping a 10.6.0.1”

Causa raíz: El cortafuegos del servidor descarta tráfico en wg0 (la cadena input no lo permite), o la Address/AllowedIPs del cliente está mal.

Solución: Permite input en wg0 o permite ICMP para depuración; confirma que la Address del cliente sea 10.6.0.2/32 y que el AllowedIPs del peer en el servidor incluya 10.6.0.2/32.

3) Síntoma: “Conectado, pero sin Internet en túnel completo”

Causa raíz: Falta de reenvío IP y/o NAT masquerade en el servidor.

Solución: Habilita net.ipv4.ip_forward=1 y añade masquerade nftables desde la subred wg hacia la interfaz de salida.

4) Síntoma: “Algunos sitios cargan, otros se cuelgan (especialmente HTTPS)”

Causa raíz: Desajuste de MTU causando fragmentación/blackholing.

Solución: Reduce la MTU de wg0 (prueba 1420 → 1380 → 1360). Si usas PPPoE o túneles dobles, probablemente necesites más pequeña.

5) Síntoma: “Un cliente deja fuera de línea a otro”

Causa raíz: Dos peers configurados con AllowedIPs superpuestos (p. ej., ambos reclaman 10.6.0.2/32 o la misma subred LAN).

Solución: Haz que los AllowedIPs de los peers sean únicos. Usa /32 por cliente. Para subredes de sitio, asegura que solo un peer anuncie cada subred.

6) Síntoma: “La VPN funciona desde Wi‑Fi doméstico pero no desde móvil”

Causa raíz: NAT del operador móvil / cortafuegos con estado que caduca los mapeos UDP; no hay keepalive configurado.

Solución: Configura PersistentKeepalive = 25 en el cliente. No lo pongas en el servidor para clientes en movimiento; el cliente está detrás del NAT.

7) Síntoma: “La VPN está activa, pero no alcanzo mi LAN doméstica vía el VPS”

Causa raíz: El peer doméstico no reenvía entre su LAN y wg0, o faltan rutas de retorno en dispositivos LAN.

Solución: Habilita reenvío y añade reglas de cortafuegos en el gateway doméstico. Considera NAT en el gateway doméstico por simplicidad si no puedes añadir rutas de retorno.

8) Síntoma: “Filtraciones DNS o resolución dividida y confusa”

Causa raíz: El cliente usa DNS local a pesar de que nombres internos deberían resolverse por la VPN, o apuntaste DNS a un resolutor interno no alcanzable en túnel dividido.

Solución: En túnel dividido, o bien mantén DNS público o enruta hacia tu DNS interno e incluye su subred en AllowedIPs. En túnel completo, configura DNS explícitamente y verifica que sea alcanzable a través del túnel.

Tres micro-historias del mundo corporativo

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

Una empresa mediana quería una VPN rápida para ingenieros on-call. El plan fue “simple”: desplegar WireGuard en una VM cloud, permitir que los ingenieros se conectaran y luego enrutar a subredes de producción. El ingeniero que lo implementó asumió que el grupo de seguridad cloud ya permitía UDP porque “permitimos tráfico web”.

El lunes por la noche, un on-call intentó conectarse durante un incidente. El cliente mostraba “conectado” en la GUI, porque la interfaz se levantó localmente. Pero el servidor nunca vio un apretón de manos. La gente persiguió claves, configs y MTU durante una hora, porque esos son los ajustes que puedes tocar sin pedir nada a nadie.

El verdadero problema: UDP 51820 estaba bloqueado en la capa de firewall del proveedor. El servidor escuchaba. El firewall OS estaba correcto. Pero los paquetes nunca llegaban. El equipo había tratado al “firewall del servidor” como el único firewall.

La solución fue trivial una vez identificada: añadir una regla UDP entrante en el borde cloud. La lección fue cara: valida la alcanzabilidad primero y documenta dónde vive realmente la política. Las redes cloud son en capas, y cada capa es una oportunidad para equivocarse con confianza.

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

Otra organización usó WireGuard como hub para desarrolladores. Alguien notó que agregar más peers y rutas hacía las configs difíciles de manejar. Su “optimización” fue ampliar AllowedIPs en todas partes: en vez de /32 por cliente, usaron 10.6.0.0/24 para cada peer en el servidor, pensando que reduciría el trabajo.

Funcionó hasta que no. El portátil de un desarrollador se conectó y secuestró silenciosamente tráfico destinado a otros peers. No por malicia—simplemente porque WireGuard creía que ese peer podía enviar tráfico legítimo para toda la subred. El enrutamiento se volvió no determinista según el orden de inserción de peers y el estado de la interfaz.

Los síntomas fueron maravillosamente caóticos: dos ingenieros podían conectarse bien, el tercero se conectaba pero no alcanzaba servicios internos, y a veces el problema “se arreglaba” tras reiniciar wg0. Esos son los mejores outages porque enseñan humildad y blasfemias al mismo tiempo.

El rollback fue volver a AllowedIPs estrictos: /32 por road-warrior y solo subredes específicas para peers de sitio. La gestión se volvió un poco más tediosa. La red dejó de estar embrujada. Es un buen intercambio.

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

Un equipo de servicios financieros usó WireGuard para un pequeño conjunto de agentes de automatización en múltiples VPCs. Su configuración era poco glamorosa: una VM diminuta como hub, un puerto UDP abierto, SSH solo desde una subred de gestión dedicada y reglas nftables con políticas de forward explícitas. También mantuvieron un runbook: “alcanzabilidad → apretón de manos → rutas → DNS”.

Durante un incidente del proveedor, la pérdida de paquetes subió y las latencias se volvieron extrañas. Los agentes de automatización empezaron a fallar en los checks de salud. El equipo no entró en pánico ni re-deplegó. Sacaron su runbook y lo ejecutaron como si pagaran renta por él.

Rápidamente demostraron: los paquetes UDP estaban llegando, los apretones de manos eran recientes y los contadores de transferencia aumentaban. Eso eliminó la mayoría de las causas locales. Luego comprobaron errores de interfaz y MTU: limpio. En ese punto escalaron al proveedor con evidencia en lugar de sentimientos.

El proveedor resolvió el problema más tarde, pero el punto clave es que el equipo evitó heridas autoinfligidas. La práctica aburrida no fue “tener WireGuard”. Fue tener un flujo de diagnóstico repetible, más una política de cortafuegos que no cambiara bajo estrés.

Preguntas frecuentes

1) ¿Necesito cambiar el puerto por defecto de WireGuard por seguridad?

No por seguridad. La seguridad de WireGuard viene de las claves, no de números de puerto. Cambiar el puerto puede reducir ruido de escaneos aleatorios. Si lo cambias, cámbialo en todas partes y documenta el cambio.

2) ¿Puedo ejecutar WireGuard y mantener SSH cerrado a Internet?

Sí, y a menudo es la decisión correcta. Levanta WireGuard primero vía consola/out-of-band, luego restringe SSH a la subred VPN (o a un bastión). Si no puedes garantizar que no te cerrarás el acceso, mantén una vía de gestión restringida.

3) ¿Qué es lo mínimo que debo exponer a Internet?

Un puerto UDP al host WireGuard (por ejemplo 51820/udp). Eso es todo. Todo lo demás debe estar bloqueado o restringido a fuentes de confianza.

4) ¿Cómo evito que un cliente VPN alcance toda mi LAN?

Dos capas: mantén AllowedIPs del cliente limitados (no le enrutes la LAN), y aplica reglas de reenvío en el servidor (descarta tráfico wg0 → lan salvo que esté explícitamente permitido).

5) ¿Debería usar NAT o enrutamiento para sitio-a-sitio?

Prefiere enrutamiento cuando controlas ambos extremos y puedes añadir rutas de retorno; es más limpio y fácil de depurar. Usa NAT cuando no controlas los dispositivos LAN o no puedes añadir rutas; es pragmático pero oculta la realidad de la red.

6) ¿Por qué “conectado” no significa “funciona”?

Muchos clientes muestran “conectado” cuando la interfaz está arriba localmente, incluso sin apretón de manos. Confía en wg show y en los contadores de paquetes, no en insignias de la UI.

7) ¿Necesito PersistentKeepalive?

Para clientes en movimiento y detrás de NAT (móvil, Wi‑Fi de hotel), sí—a menudo. Ponlo en el cliente a algo como 25 segundos. Para un servidor con IP pública, normalmente no.

8) ¿WireGuard es seguro para producción?

Sí, con un modelo de despliegue sano: servicios expuestos mínimos, AllowedIPs estrictos por peer, cortafuegos explícito y un plan para rotación de claves y offboarding. La mayoría de “incidentes VPN” son fallos de enrutamiento y políticas, no fallos criptográficos de WireGuard.

9) ¿Y IPv6?

Si tu entorno usa IPv6, trátalo como de primera clase: asigna direcciones IPv6 en wg0, incluye ::/0 solo si realmente quieres túnel completo IPv6, y escribe reglas de cortafuegos IPv6. Ignorar IPv6 es una forma común de crear “agujeros innecesarios” que no monitoreaste.

10) ¿Cómo rotar claves sin downtime?

Añade una nueva clave de peer (o actualiza un peer) manteniendo la vieja brevemente, luego elimina la vieja cuando los clientes confirmen. En la práctica: prepara cambios, verifica apretón de manos, y luego poda. No hagas rotaciones tipo “día D” salvo que te gusten llamadas sorpresa.

Conclusión: próximos pasos que puedes hacer hoy

Si te llevas una idea de esto: expón un puerto UDP y sé explícito respecto a todo lo demás. WireGuard es lo bastante simple como para mantener todo el sistema en tu cabeza—hasta que empiezas a añadir “atajos útiles”.

Pasos prácticos siguientes:

  1. Elige una topología (VPS hub suele ser la menos dolorosa) y anota las subredes que vas a enrutar.
  2. Implementa un cortafuegos drop por defecto con permisos explícitos para UDP 51820 y SSH restringido.
  3. Configura peers con AllowedIPs estrictos (/32 por cliente) y prueba apretón de manos y contadores con wg show.
  4. Si necesitas túnel completo, habilita reenvío y NAT deliberadamente, luego valida el comportamiento del DNS.
  5. Imprime (o pega) el Guion de diagnóstico rápido en tu runbook. Tu yo futuro te lo agradecerá en forma de menos incidencias.
← Anterior
ZFS SLOG: Cuándo un dispositivo de registro ayuda, cuándo es inútil, cuándo es peligroso
Siguiente →
Docker Build lento: Caché de BuildKit que realmente lo acelera

Deja un comentario