SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor

Cómo funciona WireGuard: enrutamiento con claves

Entienda por qué AllowedIPs cumple dos funciones: tabla de rutas y control de acceso. Revise el handshake Noise, la rotación de claves y wg0.conf.

Cómo funciona WireGuard, en una idea

WireGuard vincula cada paquete a una clave pública. Este mecanismo se denomina enrutamiento mediante claves criptográficas y constituye todo el diseño: la línea AllowedIPs situada junto a un peer es la tabla de enrutamiento para los paquetes que salen de su máquina y la lista de control de acceso para los paquetes que llegan desde ese peer. Un ajuste cumple dos funciones. Interprete AllowedIPs de esa forma y todos los archivos de configuración de WireGuard serán fáciles de leer.

No existe una tabla de sesiones indexada por dirección IP ni una base de datos de usuarios. Un peer es una clave pública más el conjunto de direcciones que esa clave puede utilizar. El handshake y los temporizadores mantienen válida esa asociación aunque cambie la red subyacente. Si quiere disponer primero de un túnel funcional y estudiar después la teoría, créelo con una VPN WireGuard autohospedada en su propio VPS y vuelva aquí cuando una línea de configuración le resulte inesperada.

AllowedIPs es una tabla de enrutamiento y una lista de acceso

Empiece por la dirección de salida. El kernel enruta un paquete al dispositivo wg0 de la forma habitual, mediante la tabla de enrutamiento principal. Después, WireGuard compara la dirección de destino de ese paquete con una tabla que contiene todos los prefijos permitidos de cada peer, empezando por el prefijo más específico. La coincidencia identifica un peer, que a su vez identifica una clave pública, una clave de sesión y un endpoint UDP. El paquete se cifra para ese peer y se envía allí.

Si ningún AllowedIPs de los peers incluye el destino, no se envía nada porque no existe ninguna clave con la que enviarlo.

ping: sendmsg: Required key not available

Ese error sólo puede significar una cosa: la dirección que intentó alcanzar no aparece en ningún peer. Otro error, ping: sendmsg: Destination address required, indica que sí coincidió un peer, pero WireGuard no tiene ningún endpoint para él porque no se configuró ninguno y todavía no se ha aprendido ninguno.

Ahora, la dirección de entrada. Un paquete UDP llega al puerto de escucha. WireGuard busca la sesión mediante el índice del receptor incluido en la cabecera, comprueba el contador con una ventana deslizante contra repeticiones y después descifra y autentica la carga útil. Sólo entonces lee el paquete interno, y la dirección de origen de ese paquete debe estar dentro de AllowedIPs del peer emisor. Si no lo está, el paquete se descarta. Con la depuración dinámica habilitada, el kernel imprime el motivo en una línea como esta:

wg0: Packet has unallowed src IP (10.8.0.9) from peer 2 (203.0.113.10:51820)

Por eso un peer en el lado del servidor recibe un /32. Un peer configurado con AllowedIPs = 10.8.0.2/32 puede enviar paquetes desde 10.8.0.2 y desde ninguna otra dirección. Si escribe 0.0.0.0/0 en su lugar, ese único cliente podrá inyectar paquetes que indiquen cualquier dirección de origen dentro del túnel, incluida la de otro cliente.

Los prefijos solapados se resuelven por especificidad, porque la búsqueda utiliza la coincidencia del prefijo más largo. Los prefijos idénticos en dos peers se comportan de otra manera: la entrada pasa al peer que se configuró en último lugar, y el primer peer deja de recibir ese tráfico sin que se muestre ningún error. wg show wg0 allowed-ips imprime la tabla que está realmente en el kernel. Esa es la que cuenta cuando el archivo del disco y el estado en ejecución han dejado de coincidir.

Lectura de un archivo de configuración teniendo en cuenta el enrutamiento con claves criptográficas

En el servidor:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <server private key>

[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32

En el cliente:

[Interface]
Address = 10.8.0.2/32
PrivateKey = <laptop private key>

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

La misma palabra clave tiene un efecto opuesto en cada lado. En el cliente indica «enviar todos los destinos a este peer». En el servidor indica «aceptar sólo esta dirección de este peer». La asimetría está en los valores, no en ningún rol.

La mitad de estas claves no pertenece al protocolo. Address, DNS, MTU, PostUp y SaveConfig pertenecen a wg-quick, el script de shell que activa la interfaz. El kernel nunca las ve. wg-quick strip wg0 muestra la configuración reducida que carga realmente la herramienta wg. Es la forma más rápida de ver esa separación.

Qué hace realmente el handshake

El handshake de WireGuard es Noise_IKpsk2, del Noise Protocol Framework. La parte IK es la relevante para un sysadmin: la clave pública estática del responder ya es conocida por el initiator, porque es la PublicKey de tu bloque [Peer], y el initiator envía su propia clave pública estática dentro del primer mensaje, cifrada. Por tanto, no hay intercambio de certificados ni un intercambio de identidad de ida y vuelta. Un observador pasivo no puede saber qué clave está realizando la llamada a menos que tenga la clave privada del responder.

El coste es un único viaje de ida y vuelta. El mensaje de iniciación tiene 148 bytes, la respuesta tiene 92 bytes y los datos empiezan a fluir inmediatamente después. Cada extremo genera un nuevo par de claves efímeras Curve25519 para cada handshake, y las claves de sesión se obtienen de una cadena de resultados Diffie-Hellman que combina las claves estáticas y efímeras. Después se descartan las claves privadas efímeras, lo que proporciona secreto perfecto hacia adelante: alguien que registre tu tráfico hoy y robe la clave privada del servidor el próximo año seguirá sin poder leer lo que registró.

La iniciación de un handshake contiene una marca de tiempo TAI64N, y cada peer recuerda la marca de tiempo más reciente que ha visto del otro. Por tanto, una iniciación reproducida se rechaza. Los paquetes de datos contienen un contador de 64 bits que se usa como nonce, y el receptor mantiene una ventana deslizante de los contadores vistos recientemente. Así, las reproducciones y los reordenamientos importantes se gestionan sin mantener un estado de conexión al estilo de TCP.

Las claves de sesión no duran mucho, y los temporizadores están compilados en el programa en lugar de ser configurables.

ChartWireGuard protocol timers, in seconds
The data behind this chart
[
  {
    "label": "REKEY_TIMEOUT",
    "seconds": 5,
    "notes": "resend a handshake initiation that got no answer"
  },
  {
    "label": "KEEPALIVE_TIMEOUT",
    "seconds": 10,
    "notes": "send a keepalive after receiving data and sending none back"
  },
  {
    "label": "REKEY_ATTEMPT_TIME",
    "seconds": 90,
    "notes": "give up on the handshake and report the peer as down"
  },
  {
    "label": "REKEY_AFTER_TIME",
    "seconds": 120,
    "notes": "sender begins a fresh handshake for a new session key"
  },
  {
    "label": "REJECT_AFTER_TIME",
    "seconds": 180,
    "notes": "the old session key is refused and traffic stops"
  }
]

Son constantes de la especificación del protocolo, no mediciones. El 5 de estas constantes controla todo el ciclo de vida de la sesión. Después de 120 segundos de uso, el emisor inicia un nuevo handshake. Después de 180 segundos, la clave antigua se rechaza directamente y el tráfico se detiene hasta que termina un nuevo handshake. Una iniciación que no recibe respuesta se reenvía cada 5 segundos y se abandona después de 90 segundos. Por eso wg show muestra latest handshake como una antigüedad relativa, y por eso un túnel activo y saludable mantiene esa antigüedad baja. Si la antigüedad aumenta mientras envías tráfico activamente, significa que los handshakes están fallando, no que el túnel esté inactivo.

Por qué un peer no tiene el rol de cliente ni de servidor

Ambos extremos ejecutan el mismo código y usan el mismo formato de configuración. No existe un modo de servidor. La asimetría que se percibe procede de Endpoint, y Endpoint es opcional.

Un peer con un endpoint configurado puede iniciar un handshake. Un peer sin endpoint espera y después obtiene la dirección y el puerto del otro extremo a partir del primer paquete que se autentica correctamente. Ese endpoint aprendido se almacena y se actualiza cada vez que llega un paquete válido desde una dirección nueva. Así funciona el roaming: un portátil que cambia de wifi a una red móvil mantiene el mismo túnel porque una sesión se identifica mediante la clave y el índice, no mediante la dirección IP. No se reconecta nada porque nunca hubo una conexión en el sentido de TCP.

El mismo mecanismo produce un dato importante: el peer con la dirección pública siempre conserva la última IP pública conocida del otro extremo, y wg show la muestra.

Primitivas fijas, sin negociación

WireGuard no tiene una lista de suites criptográficas. Usa ChaCha20-Poly1305 para el cifrado autenticado, Curve25519 para el acuerdo de claves, BLAKE2s para el hashing y HKDF para la derivación de claves. Todas las implementaciones utilizan esas primitivas, por lo que no hay una fase de negociación que analizar ni una vía de degradación a una opción más débil. El compromiso es real: si una de esas primitivas se rompe, la solución es una nueva versión del protocolo completo y una actualización en ambos extremos, no un cambio de configuración. Esta decisión elimina gran parte del código y de los modos de fallo que implica un túnel basado en TLS. En eso se resume principalmente la comparación de WireGuard frente a OpenVPN.

Por qué el puerto no responde a un escáner

Cada mensaje de enlace contiene un campo llamado mac1. Es un código de autenticación de mensajes (MAC) que se calcula sobre el mensaje con una clave derivada de la clave pública estática del responder. Un emisor que no conoce esa clave pública no puede producir un mac1 válido, y el receptor descarta ese paquete sin responder. No envía ningún error, restablecimiento ni mensaje ICMP.

El resultado visible es un escaneo UDP que no recibe ninguna respuesta.

sudo nmap -sU -p 51820 vpn.example.com

nmap informa open|filtered, que es la misma respuesta que proporciona para un puerto cuyos paquetes descarta silenciosamente un firewall. El puerto se comporta igual tanto si WireGuard está escuchando como si no lo está, al menos para quien no posee ya su clave pública.

Un segundo campo, mac2, gestiona la presión por denegación de servicio. Cuando el receptor está bajo carga, responde a una iniciación válida con una respuesta de cookie de 64 bytes vinculada a la dirección de origen del emisor, y no realiza operaciones costosas de clave pública hasta que el emisor devuelve esa cookie. Así demuestra que la dirección de origen es real antes de consumir CPU y sólo se activa bajo carga.

Por qué 0.0.0.0/0 convierte a un peer en la ruta predeterminada

Como AllowedIPs es la tabla de enrutamiento, AllowedIPs = 0.0.0.0/0, ::/0 reclama todos los destinos para ese peer. Esa es toda la configuración del túnel completo.

El enrutamiento que hace que funcione es más interesante que la línea en sí. Una ruta predeterminada simple a través de wg0 entraría en un bucle, porque el paquete UDP cifrado que transporta el tráfico también tiene que salir de la máquina y coincidiría con su propia ruta predeterminada. wg-quick evita esto mediante el enrutamiento basado en políticas. Marca con un fwmark los paquetes salientes propios de WireGuard, coloca la ruta predeterminada del túnel en una tabla de enrutamiento independiente y añade reglas para que sólo el tráfico sin marca llegue a ella. Ejecute ip rule show para ver el resultado:

32764:	from all lookup main suppress_prefixlength 0
32765:	not from all fwmark 0xca6c lookup 51820
32766:	from all lookup main

0xca6c es 51820 en hexadecimal, y 51820 también es el número de la tabla. La regla suppress_prefixlength 0 hace que la tabla principal omita su propia ruta predeterminada. Así, las rutas específicas, como la de la subred local, siguen teniendo prioridad, mientras que el resto del tráfico pasa a la tabla del túnel. Un túnel dividido no necesita nada de esto: una lista más limitada, como AllowedIPs = 10.8.0.0/24, 10.20.0.0/16, se convierte en rutas normales de la tabla principal.

Un túnel completo no resuelve por sí solo la resolución de nombres, porque el resolver que el cliente obtuvo de la red local normalmente se mantiene y su ruta suele ser más específica. Es una tarea independiente, explicada en DNS que se filtra fuera de un túnel WireGuard.

Para qué sirve realmente PersistentKeepalive

WireGuard no envía nada cuando no hay tráfico. No hay heartbeat, actualización de sesión ni ningún otro paquete en la red. Este silencio ayuda a ahorrar batería y también al caso del escáner descrito arriba, pero rompe una configuración concreta.

Un peer detrás de NAT (traducción de direcciones de red) o de un firewall con estado sólo es accesible desde el exterior mientras exista un mapeo en ese dispositivo. Ese mapeo se creó mediante un paquete saliente. Los tiempos de vida habituales de los mapeos UDP empiezan en unos 30 segundos. Cuando el mapeo caduca, el middlebox descarta los paquetes procedentes del lado público y el túnel parece inactivo hasta que el peer detrás de NAT envía algo. PersistentKeepalive = 25 envía un paquete autenticado vacío cada 25 segundos. Este intervalo es inferior al tiempo de vida habitual más corto, por lo que el mapeo permanece abierto.

Configure esta opción en el peer detrás de NAT. Un servidor con una dirección pública y un puerto UDP abierto no la necesita. Configurarla allí sólo añade tráfico. No la confunda con el keepalive automático. Este se activa 10 segundos después de que un peer reciba datos y no tenga nada propio que enviar en respuesta. Siempre está habilitado y no se puede configurar.

El encaminamiento de la LAN a través del túnel no es una función de WireGuard

Supongamos que el peer B está en la red doméstica 192.168.50.0/24 y que el peer A debe acceder a ella. Dos sistemas independientes deben estar configurados correctamente, y sólo uno de ellos es WireGuard.

La parte de WireGuard: añada 192.168.50.0/24 a AllowedIPs de B en A. Esto hace que A encamine el prefijo hacia B y que acepte de B paquetes con esas direcciones de origen. Sin esta configuración, el encaminamiento mediante claves criptográficas no tiene una clave para el destino ni permiso para el origen.

La parte del kernel: en B, net.ipv4.ip_forward debe ser 1, o el kernel descarta todos los paquetes descifrados que no estén dirigidos al propio B. La cadena de reenvío del firewall de B debe permitir el tráfico. Los hosts de la LAN necesitan una ruta de retorno hacia 10.8.0.0/24, o B debe aplicar NAT de origen para que las respuestas regresen a través de B.

La función de WireGuard termina cuando entrega el paquete descifrado al kernel. Todo lo que sucede después corresponde al encaminamiento y filtrado normales de Linux. Por eso este fallo aparece en los contadores de nft list ruleset o en ip -s link show wg0, y no en wg show. Si prefiere administrar los peers mediante una interfaz web, ejecutar wg-easy en Docker genera las entradas de los peers, aunque las reglas de reenvío siguen correspondiendo al host.

Por qué WireGuard está en el kernel

wg0 es un controlador de dispositivo de red. Los paquetes llegan a través de la pila de enrutamiento normal, se cifran en el contexto de softirq y salen mediante un socket UDP sin pasar nunca al espacio de usuario. De ahí procede el rendimiento, y por eso el módulo se mantiene en unas cuatro mil líneas de código, un tamaño suficientemente reducido para revisarlo e incorporarlo al kernel principal de Linux 5.6 en marzo de 2020. Ubuntu 24.04 y Debian 13 lo incluyen, por lo que sólo falta el paquete wireguard-tools.

El hecho de ser una interfaz normal tiene consecuencias prácticas. tcpdump -ni wg0 muestra los paquetes internos en texto claro, mientras que tcpdump -ni eth0 udp port 51820 muestra los paquetes externos cifrados. Compararlos permite saber de inmediato qué dirección está fallando. netfilter y el control de tráfico tratan wg0 como cualquier otro enlace. Cuando el módulo del kernel no está disponible, por ejemplo en una virtualización de contenedores que comparte el kernel del host, wireguard-go implementa el mismo protocolo en el espacio de usuario sobre un dispositivo TUN, con un coste real de rendimiento porque cada paquete cruza dos veces el límite del kernel.

Lo que WireGuard no protege

El modelo de amenazas es deliberadamente limitado, y un protocolo tan discreto puede generar expectativas equivocadas. Hay que expresarlo con claridad.

  • No oculta que usa WireGuard. Los mensajes de handshake tienen tamaños fijos, el primer byte indica el tipo de mensaje y el transporte es UDP. La inspección profunda de paquetes lo reconoce con facilidad, y una red que bloquee las VPN puede bloquearlo. La ofuscación se omitió de forma deliberada.
  • No oculta el volumen ni los tiempos. Las cargas útiles sólo se rellenan hasta un límite de 16 bytes, por lo que un observador sigue viendo cuándo envía datos y, de forma aproximada, cuánto envía.
  • Conserva el último endpoint conocido. El peer con la dirección pública almacena la IP pública actual del otro extremo, y wg show la muestra. Junto con una dirección del túnel fija en la configuración, esto constituye un identificador estable que sigue al usuario entre redes. En su propio VPS esto no supone un problema. También explica por qué los servicios comerciales añaden una capa sobre el protocolo.
  • Autentica una clave, no una persona. El peer es quien tenga el archivo de clave privada. Mantenga /etc/wireguard con el modo 700 y los archivos de clave con el modo 600.
  • No existe una lista de revocación ni una fecha de caducidad. El acceso termina cuando elimina la entrada del peer de todos los servidores que la contienen, y las claves estáticas siguen siendo válidas hasta que las elimine.

Nada de esto hace que WireGuard sea débil. Lo hace pequeño, y ese es el objetivo: autentica y cifra, y deja la gestión de identidades y la asignación de direcciones en manos de lo que se construya por encima. Una capa de coordinación como la descrita en WireGuard comparado con Tailscale existe precisamente para cubrir esa carencia, utilizando el mismo plano de datos que acaba de leer.

FAQ

¿Qué es el enrutamiento mediante claves criptográficas en WireGuard?

El enrutamiento mediante claves criptográficas es la regla que vincula cada paquete con una clave pública. Cada entrada de peer contiene una lista de prefijos en AllowedIPs. Para el tráfico saliente, WireGuard selecciona el peer comparando el destino del paquete con la lista de cada peer, empezando por el prefijo más específico. Por tanto, la lista funciona como una tabla de enrutamiento. Para el tráfico entrante, una vez que el paquete se descifra y autentica, su dirección de origen interna debe estar dentro de la lista de ese mismo peer. De lo contrario, el paquete se descarta. Por tanto, la lista también funciona como una lista de control de acceso. WireGuard no tiene una configuración de enrutamiento independiente ni un firewall interno independiente, porque una sola lista cumple ambas funciones.

¿Necesito configurar PersistentKeepalive en ambos peers?

No. Configúrelo en el lado que está detrás de NAT (traducción de direcciones de red) o de un firewall con estado, que normalmente es el cliente. WireGuard no envía nada mientras está inactivo. Por eso, la asignación que permite al otro extremo llegar a ese peer caduca, a menudo en menos de un minuto, y el túnel parece inactivo en una dirección. PersistentKeepalive = 25 envía un paquete autenticado vacío cada 25 segundos y mantiene abierta la asignación. Un peer con una dirección pública y un puerto UDP abierto no lo necesita.

¿Por qué el ping a través del túnel muestra "Required key not available"?

Porque la dirección de destino no está dentro de ningún AllowedIPs. El enrutamiento mediante claves criptográficas no encontró una clave con la que cifrar el paquete y el kernel rechazó su envío. Ejecute wg show wg0 allowed-ips y compare esa salida con la dirección a la que está haciendo ping. El error similar Destination address required indica un problema diferente: se encontró un peer coincidente, pero WireGuard no tiene ningún endpoint para él porque no se configuró ninguno y todavía no ha llegado ningún paquete autenticado desde ese peer.

¿Puede un firewall detectar y bloquear WireGuard?

Sí. WireGuard autentica y cifra el tráfico, y no intenta ocultar que es WireGuard. Los mensajes de handshake tienen un tamaño fijo de 148 y 92 bytes. El primer byte de cada mensaje identifica su tipo. Además, el transporte utiliza UDP. Por tanto, la inspección profunda de paquetes identifica el protocolo sin dificultad. Las redes que bloquean UDP o identifican protocolos mediante huellas digitales lo detendrán. Ocultar el túnel requiere encapsularlo en otra cosa. Es una herramienta independiente, no una opción de WireGuard.