WireGuard VPN¶
El problema¶
Quieres llegar a tu Proxmox, a Grafana o al NAS de casa desde fuera sin exponer ningún panel a Internet. Las VPN clásicas (IPsec, OpenVPN) funcionan, pero arrastran configuraciones largas, negociaciones con muchos parámetros y un montón de código que auditar. Y cuando algo falla, no sabes si es la fase 1, la fase 2 o el certificado.
WireGuard reduce el problema a lo esencial: cada extremo tiene un par de claves, una lista de pares permitidos y una interfaz de red más. Vive en el kernel de Linux, el fichero de configuración cabe en una pantalla y no hay estado de sesión que depurar. A cambio, no resuelve por ti la distribución de claves, el direccionamiento ni el enrutado: eso lo decides tú, y de ahí salen casi todos los fallos.
Qué cubre y qué no
Esta página trata WireGuard «a mano» con wg y wg-quick: el bloque de construcción. Si prefieres que un plano de control gestione claves y NAT traversal, mira Tailscale o NetBird; la comparativa está en VPN overlay: comparativa.
📋 Tabla de Contenidos¶
- Cómo funciona
- Instalación
- Generación de claves
- Servidor
- Cliente
- Enrutado y NAT
- Firewall
- Arranque y operación
- Site-to-site
- Troubleshooting
- Buenas prácticas
- Referencias
Cómo funciona¶
WireGuard no distingue entre «servidor» y «cliente»: todos son pares (peers). Lo que llamamos servidor es el par con IP o puerto alcanzable, y por eso el que recibe la conexión inicial.
- Claves: cada par tiene una clave privada (que nunca sale de la máquina) y una pública (que se reparte a los demás). No hay CA ni certificados.
- Cryptokey routing:
AllowedIPshace dos cosas a la vez. Al enviar, decide a qué par va un paquete según su destino. Al recibir, descarta cualquier paquete descifrado cuyo origen no esté en la lista de ese par. - UDP y silencioso: usa un solo puerto UDP y no responde a paquetes de quien no conoce, así que un escaneo no ve nada.
- Roaming: el
Endpointde un par se actualiza solo con el último paquete autenticado que llega, de modo que un portátil que cambia de wifi a 4G sigue conectado.
AllowedIPs no es solo un filtro
Es también la tabla de rutas del túnel. Poner 0.0.0.0/0 en un cliente manda todo su tráfico por la VPN; ponerlo en dos pares del mismo extremo hace que solo uno lo reciba.
Instalación¶
En Debian y Ubuntu actuales, el soporte está en el kernel y el paquete aporta las herramientas:
sudo apt install wireguard
El paquete instala wg (inspección y ajuste en caliente) y wg-quick (levanta la interfaz leyendo un fichero). Si usas un kernel muy antiguo, el módulo puede no estar disponible; consulta la documentación oficial de tu distribución.
Generación de claves¶
Genera cada par de claves en la máquina que lo va a usar, con permisos restrictivos:
umask 077
wg genkey | tee privatekey | wg pubkey > publickey
umask 077 evita que la clave privada quede legible para otros usuarios. Para añadir una capa simétrica extra (resistencia a ataques con ordenadores cuánticos futuros, según la documentación del proyecto) puedes generar una clave precompartida por cada pareja de pares:
wg genpsk > preshared_srv_laptop
Esa clave se declara con PresharedKey en ambos extremos y es opcional.
Servidor (/etc/wireguard/wg0.conf)¶
[Interface]
Address = 10.100.0.1/24
ListenPort = 51820
PrivateKey = <CONTENIDO_DE_PRIVATEKEY_SERVIDOR>
# Peer: portátil
[Peer]
PublicKey = <PUBLICKEY_DEL_CLIENTE>
AllowedIPs = 10.100.0.2/32
# Peer: móvil
[Peer]
PublicKey = <PUBLICKEY_DEL_MOVIL>
AllowedIPs = 10.100.0.3/32
Cada cliente recibe una /32 dentro de la red del túnel (10.100.0.0/24). Esa red debe ser distinta de cualquier LAN en la que vayan a estar tus clientes: si el portátil está en una wifi 192.168.1.0/24 y tu casa también, las rutas chocan.
SaveConfig = true reescribe el fichero
La versión anterior de esta página usaba SaveConfig = true. Con él, wg-quick vuelca la configuración en memoria al fichero cuando baja la interfaz, y descarta tus comentarios y cualquier edición manual hecha mientras estaba arriba. Para un fichero que mantienes a mano o en Git, déjalo desactivado.
El fichero contiene la clave privada: permisos 600 y propietario root.
sudo chmod 600 /etc/wireguard/wg0.conf
Cliente¶
[Interface]
Address = 10.100.0.2/24
PrivateKey = <CONTENIDO_DE_PRIVATEKEY_CLIENTE>
DNS = 10.100.0.1
[Peer]
PublicKey = <PUBLICKEY_DEL_SERVIDOR>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.100.0.0/24, 192.168.10.0/24
PersistentKeepalive = 25
- Split tunnel (el de arriba): solo el tráfico hacia la red del túnel y la LAN doméstica
192.168.10.0/24va por la VPN; el resto sale directo. Es lo recomendable para acceso a servicios internos. - Full tunnel: cambia
AllowedIPspor0.0.0.0/0, ::/0y todo el tráfico pasa por el servidor. Exige NAT en el servidor (siguiente sección) y una decisión sobre el DNS. DNSsolo lo interpretawg-quick, y usaresolvconfosystemd-resolvedpara aplicarlo. Si tu sistema no tiene ninguno de los dos, la línea puede hacer fallar el arranque.PersistentKeepalive = 25manda un paquete cada 25 segundos. Es necesario si el cliente está detrás de NAT y el servidor tiene que poder hablarle sin que el cliente haya enviado nada antes. Sin él, la entrada del NAT caduca y el túnel parece caído en un sentido.
Enrutado y NAT¶
Para que los clientes alcancen la LAN detrás del servidor, el servidor debe reenviar paquetes. Actívalo de forma persistente:
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
Si la LAN no tiene la ruta de vuelta hacia 10.100.0.0/24 (porque el servidor WireGuard no es su pasarela), añade NAT para que los dispositivos de la LAN vean las conexiones como originadas en el servidor. Con iptables, en la sección [Interface] del servidor:
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
%i lo sustituye wg-quick por el nombre de la interfaz (wg0) y eth0 debe ser tu interfaz de salida real. Si ya tienes una política de reenvío en nftables, escribe ahí las reglas equivalentes en lugar de mezclar ambos mundos.
La mejor ruta es la de vuelta
El NAT funciona, pero oculta el origen real en los registros de la LAN. Si puedes añadir en tu router una ruta estática 10.100.0.0/24 hacia el servidor WireGuard, no necesitas MASQUERADE y conservas trazabilidad.
Firewall¶
El servidor solo necesita abrir un puerto: 51820/udp en el cortafuegos del host y, si hay router por delante, un reenvío del mismo puerto hacia él. Las reglas de ejemplo con nftables están en nftables y fail2ban. Conviene:
- Aceptar
udp dport 51820desde cualquier origen en la interfaz pública. - Permitir en la interfaz
wg0solo lo que quieras exponer a los clientes; que estén autenticados no significa que deban alcanzarlo todo. - Si el servidor es un router virtual, la comparativa de vRouters ayuda a decidir dónde colocar el extremo.
Arranque y operación¶
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
wg-quick@wg0 es la unit que lee /etc/wireguard/wg0.conf al arrancar. Para parar o reiniciar:
sudo wg-quick down wg0
sudo systemctl restart wg-quick@wg0
Para ver el estado en vivo:
sudo wg show
La salida lista la interfaz con su clave pública y puerto, y debajo cada par con su endpoint, los allowed ips, el tiempo desde el último handshake y los bytes transferidos. Un par que nunca ha hecho handshake no aparece con esa línea: es la señal más directa de que el tráfico no está llegando o las claves no coinciden.
Cambios sin cortar el túnel¶
Para añadir un par en caliente, sin reiniciar la interfaz:
sudo wg set wg0 peer <PUBLICKEY_NUEVO> allowed-ips 10.100.0.4/32
Eso no se guarda en el fichero (salvo con SaveConfig), así que añádelo también a wg0.conf. Si editas el fichero y quieres aplicarlo sin tumbar las conexiones existentes, wg-quick strip produce la configuración sin las directivas propias de wg-quick y wg syncconf la aplica:
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'
syncconf solo aplica lo que entiende wg (clave privada, puerto, pares y sus AllowedIPs, Endpoint, etc.); no toca Address, rutas ni PostUp. Si cambias esos, hay que bajar y subir la interfaz.
Revocar un acceso¶
Eliminar el bloque [Peer] y aplicar el cambio basta: sin su clave pública en la lista, el par no puede hablar con el servidor. No hay listas de revocación porque no hay certificados. Rota las claves de un dispositivo perdido generando un par nuevo y sustituyéndolo.
Site-to-site¶
Para unir dos sedes (casa y un VPS, o dos laboratorios), ambos lados son servidor y cliente a la vez. Cada lado declara en AllowedIPs la LAN remota:
# Sede A (LAN 192.168.10.0/24, túnel 10.100.0.1)
[Interface]
Address = 10.100.0.1/30
ListenPort = 51820
PrivateKey = <CLAVE_PRIVADA_A>
[Peer]
PublicKey = <CLAVE_PUBLICA_B>
Endpoint = sedeb.example.com:51820
AllowedIPs = 10.100.0.2/32, 192.168.20.0/24
PersistentKeepalive = 25
La sede B es el espejo, con 192.168.10.0/24 en su AllowedIPs. Los dispositivos de cada LAN necesitan que su pasarela sepa que la red remota está detrás del extremo WireGuard (ruta estática o que el extremo sea la pasarela). Para topologías con más de dos sedes, malla completa a mano escala mal; valora un plano de control como los de Tailscale o NetBird.
Troubleshooting¶
| Síntoma | Causa probable | Arreglo |
|---|---|---|
wg show no muestra línea de latest handshake |
El UDP no llega (puerto cerrado, NAT sin reenvío) o las claves no coinciden | Comprueba el reenvío de 51820/udp; verifica que la clave pública declarada en cada lado es la del otro |
| Handshake correcto pero no hay tráfico | AllowedIPs no incluye la IP origen o destino |
Revisa AllowedIPs en ambos extremos; recuerda que filtra al recibir |
| El cliente llega al servidor pero no a la LAN | Falta ip_forward o la LAN no conoce la ruta de vuelta |
Activa el reenvío; añade ruta estática o NAT |
| El túnel funciona de cliente a servidor pero no al revés | Cliente tras NAT sin keepalive | Añade PersistentKeepalive = 25 en el cliente |
| Conexiones colgadas con páginas grandes o SSH que se congela | MTU: el encapsulado reduce el tamaño útil | Baja MTU en [Interface]; si no lo fijas, wg-quick lo calcula a partir de la interfaz de la ruta hacia el Endpoint (o de la ruta por defecto) |
wg-quick up falla con error de DNS o resolvconf |
Línea DNS sin resolvconf ni systemd-resolved |
Instala uno de los dos o quita DNS y configura el resolvedor a mano |
Tras down, los cambios y comentarios de wg0.conf se pierden |
SaveConfig = true |
Desactívalo y gestiona el fichero a mano o con Git |
Dos clientes con el mismo AllowedIPs y solo uno funciona |
Cada IP solo puede pertenecer a un par | Asigna una /32 única por cliente |
| Funciona en casa pero no en una red corporativa | La red bloquea UDP saliente no estándar | Prueba un puerto más común como 443/udp |
Para ver por qué un paquete no entra, tcpdump -ni eth0 udp port 51820 en el servidor confirma si llegan datagramas; el contenido va cifrado, pero basta para saber si el problema es de red o de claves.
Buenas prácticas¶
- Un par de claves por dispositivo, nunca compartidas; así revocas uno sin tocar el resto.
AllowedIPsmínimos: la/32del cliente en el servidor y solo las redes necesarias en el cliente.- Claves privadas con permisos
600, fuera de repositorios y copias sin cifrar. Si versionaswg0.conf, usa un gestor de secretos y plantillas, no la clave en claro. - Reserva un rango de túnel propio (
10.100.0.0/24aquí) que no solape con ninguna LAN conocida. - Documenta la tabla de IPs asignadas a cada par; es la única «base de usuarios» que existe.
- Expón solo el puerto UDP y controla qué alcanza el tráfico entrante desde
wg0con el cortafuegos. - Monitoriza el último handshake de cada par (
wg show wg0 latest-handshakes) para detectar pares caídos. - Para flotas grandes o NAT traversal complejo, valora una capa de control encima antes de multiplicar ficheros a mano.