Cómo montar una VPN WireGuard en tu propio VPS
Configura WireGuard en un VPS Linux con wg0.conf, claves, reenvío IP, NAT, DNS y AllowedIPs. Incluye fallos de handshake, permisos y requisitos de virtualización.
Qué va a configurar
Una VPN de WireGuard en un servidor propio requiere unas cuarenta líneas de configuración: un par de claves, un archivo de interfaz, un parámetro de sysctl, una regla NAT y una excepción en el firewall. La instalación es sencilla, así que la mayor parte de esta guía cubre los problemas habituales: permisos de las claves, AllowedIPs, reenvío y DNS.
WireGuard es un túnel de capa 3 implementado en el kernel y forma parte de Linux desde la versión 5.6. Por eso, Ubuntu 24.04 y Debian 13 lo incluyen sin un módulo externo. No hay negociación de cifrado, autoridad certificadora ni paso de usuario y contraseña: un peer se identifica mediante una clave pública y las direcciones IP que esa clave puede usar. Los paquetes que no superan la comprobación MAC se descartan sin respuesta, por lo que el puerto no responde a los escaneos. La contrapartida es que no existe un servidor de autenticación. Para revocar el acceso, hay que eliminar el peer del servidor.
Compruebe primero la virtualización
WireGuard necesita un kernel en el que se pueda cargar un módulo y, en un VPS KVM, funciona de forma inmediata. En una virtualización basada en contenedores que comparte el kernel del host, como OpenVZ o LXC, el primer comando falla con RTNETLINK answers: Operation not supported y la alternativa es la implementación en el espacio de usuario wireguard-go. Compruébelo primero con sudo modprobe wireguard && echo ok.
Generar claves sin exponerlas
Un /etc/wireguard/server.key legible para todos equivale a no tener VPN. La línea habitual umask 077 && wg genkey | sudo tee ... no es fiable, porque sudo aplica su propio umask al archivo que crea tee. Establezca el modo explícitamente.
sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.keyGenere el par del cliente de la misma forma. wg genpsk añade una clave precompartida opcional, con una línea en cada configuración.
La interfaz del servidor: /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>
[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32chmod 600; una advertencia de inicio que indica que el archivo es accesible para todos significa que omitió ese paso. Address es la dirección del servidor dentro del túnel y contiene la máscara de toda la subred de la VPN. Elija un rango que no vaya a encontrar en redes externas. 192.168.1.0/24 entra en conflicto con la mitad de los routers domésticos detrás de los que están sus clientes, y el túnel pierde el tráfico silenciosamente frente a la ruta local.
El AllowedIPs de un par en el lado del servidor es un /32: la única dirección del túnel que pertenece a ese cliente. Si asigna la misma IP permitida a dos pares, esta pasa al par configurado en último lugar y el primero deja de recibir tráfico sin que se muestre ningún error. Deje SaveConfig sin definir; de lo contrario, wg-quick down sobrescribe este archivo con el estado activo.
Convierte el equipo en un router
Un servidor Linux descarta los paquetes que no están dirigidos a él. El reenvío y el NAT de origen están desactivados de forma predeterminada.
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
| sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forwardUn sysctl -w básico funciona hasta el siguiente reinicio y después deja de funcionar sin mostrar ningún mensaje. El NAT necesita la interfaz de egress, es decir, la NIC que llega a Internet, no wg0. No des por hecho eth0; obtén el nombre de la interfaz mediante ip route show default, ya que las imágenes actuales usan nombres como enp1s0 o ens3.
Firewall: el puerto y la ruta de reenvío
Un archivo nftables cubre el filtrado y NAT. Escriba /etc/nftables.conf; esto vacía el conjunto de reglas existente, por lo que no debe ejecutarlo en un equipo que ya gestione ufw o Docker.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
tcp dport 22 accept
udp dport 51820 accept
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "wg0" oifname "enp1s0" accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
}
}Aplíquelo con sudo systemctl enable --now nftables y mantenga abierta una segunda sesión SSH: policy drop, junto con un error tipográfico en la regla SSH, puede dejarle fuera de su propio servidor. Observe lo que la cadena de reenvío no permite, wg0 a wg0. Los equipos tienen acceso a Internet, pero no entre sí; añada iifname "wg0" oifname "wg0" accept para una VPN entre pares. La misma cadena determina a qué puede acceder un par en el propio servidor. Esto es importante cuando el equipo también funciona como equipo de desarrollo remoto que ejecuta Claude Code en tmux y no quiere exponer públicamente esa parte.
En un equipo con ufw: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" en /etc/default/ufw y una regla POSTROUTING MASQUERADE *nat al principio de /etc/ufw/before.rules.
Actívelo con systemd
sudo systemctl enable --now wg-quick@wg0
sudo wg showwg-quick crea la interfaz, añade las direcciones e instala las rutas derivadas de AllowedIPs. enable --now es la parte importante: un wg-quick up wg0 ejecutado manualmente desaparece tras el siguiente reinicio, y las actualizaciones del kernel requieren reinicios. Una unidad que no vuelve a iniciarse después de uno de esos reinicios permanece en silencio hasta que alguien intenta conectarse, por lo que un archivo de configuración adicional OnFailure= en wg-quick@wg0, dirigido a su propio servidor ntfy, es la forma más económica de recibir el aviso en el teléfono en lugar de enterarse por un usuario que no puede iniciar sesión.
La configuración del cliente y el ajuste que casi todos configuran mal
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25AllowedIPs cumple dos funciones distintas al mismo tiempo. Confundirlas es la causa de la mayoría de los problemas con WireGuard.
En las conexiones salientes, es una tabla de enrutamiento. Un paquete cuyo destino coincide con AllowedIPs de un peer se cifra y se envía a ese peer. 0.0.0.0/0, ::/0 envía todo por el túnel: es un túnel completo y el servidor actúa como ruta predeterminada. Un túnel dividido usa una lista más limitada: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 transporta el tráfico de la VPN y una red privada situada detrás del servidor, mientras que todo lo demás conserva su ruta local. Esta lista limitada permite mantener servicios completamente fuera de Internet pública. Por ejemplo, una instancia privada de Nextcloud en un VPS vinculada a la dirección del túnel o las máquinas virtuales del laboratorio de virtualización anidada que se ejecutan en el mismo equipo siguen siendo accesibles para los peers y permanecen invisibles para el resto.
En las conexiones entrantes, es una lista de control de acceso. Se descarta un paquete descifrado procedente de un peer cuya dirección de origen no está incluida en AllowedIPs de ese peer. Por eso el servidor especifica 10.8.0.2/32 para el portátil: una entrada 0.0.0.0/0 allí permitiría que ese cliente suplantara cualquier dirección del túnel.
PersistentKeepalive se usa con clientes situados detrás de NAT, donde el router mantiene abierta la asignación UDP sólo mientras circulan paquetes. Cuando la asignación caduca, el servidor ya no puede alcanzar al cliente. PersistentKeepalive = 25 mantiene abierta la asignación. Configúrelo en el cliente, no en un servidor con una dirección IP pública.
DNS y la filtración que nadie detecta
Con AllowedIPs = 0.0.0.0/0 y sin una línea DNS =, el cliente mantiene el resolvedor que aprendió de la red local, el router de la cafetería en 192.168.1.1. Esa ruta es más específica que la ruta predeterminada, por lo que las consultas DNS salen por el enlace local en texto claro mientras todo lo demás se tuneliza. El tráfico es privado; la lista de nombres no.
Hay dos opciones válidas. Apunte DNS a un resolvedor público (DNS = 9.9.9.9); las consultas viajarán por el túnel y saldrán desde el servidor, aunque ese resolvedor seguirá viéndolas. O ejecute unbound o dnsmasq enlazado a 10.8.0.1, configure DNS = 10.8.0.1 y añada udp dport 53 iifname "wg0" accept a la cadena de entrada; establezca esa línea y olvídese del resolvedor, y no se resolverá ningún nombre.
En los clientes Linux, wg-quick aplica DNS mediante resolvconf; si está ausente, obtendrá resolvconf: command not found. Instale openresolv o configure PostUp = resolvectl dns %i 10.8.0.1 en un cliente con systemd-resolved.
Añadir y eliminar peers sin interrumpir el túnel
Reiniciar la interfaz para añadir un usuario desconecta a todos los usuarios conectados. Añada el bloque [Peer] a wg0.conf y, después, vuelva a cargar el conjunto de peers sin reiniciar la interfaz.
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip muestra la configuración sin las claves exclusivas de wg-quick (Address, DNS, PostUp), y syncconf aplica las diferencias mientras las sesiones activas continúan. Sólo actualiza los peers: un Address modificado todavía requiere un down/up completo. Revoque el acceso con sudo wg set wg0 peer <public key> remove y, después, elimine el bloque del archivo; de lo contrario, volverá a aparecer en la siguiente recarga.
Modos de fallo y mensajes que verá
El handshake nunca se completa. wg show muestra el peer sin latest handshake, y los registros del cliente contienen:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)No llega ningún paquete o ninguno se acepta. Compruebe lo siguiente, en este orden: que UDP 51820 esté abierto en el firewall del VPS y en el firewall de red del proveedor, que suele ser un control independiente en la mayoría de los paneles; que la dirección y el puerto de Endpoint sean correctos; y que las claves no estén intercambiadas. La clave del bloque [Peer] del cliente debe ser la clave pública del servidor y viceversa. Pegar una clave privada o la clave pública del propio cliente produce exactamente este síntoma. sudo tcpdump -ni any udp port 51820 en el servidor muestra si llegan paquetes. El módulo del kernel no registra nada de forma predeterminada. Los mensajes de WireGuard aparecen en dmesg sólo después de habilitar la depuración dinámica (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). Con ella habilitada, una discrepancia de claves aparece como un descarte por MAC no válida.
El handshake funciona, pero no hay Internet. ping 10.8.0.1 se completa, pero ping 1.1.1.1 agota el tiempo de espera: faltan el reenvío o NAT. Compruebe que sysctl net.ipv4.ip_forward contenga 1. Después, observe los contadores mientras el cliente hace ping, con sudo nft list ruleset o sudo iptables -t nat -L POSTROUTING -n -v. Cero paquetes en la regla de masquerade indica que el nombre de la interfaz de salida es incorrecto. Un contador que aumenta sin respuestas apunta a la política de la cadena de reenvío.
Internet funciona, pero no se resuelven los nombres. ping 1.1.1.1 se completa y curl https://example.com devuelve Could not resolve host. Falta la línea DNS o especifica un resolver que no es accesible desde el interior del túnel.
Algunos sitios HTTPS se quedan bloqueados. SSH y ping funcionan, pero las páginas grandes se detienen. El problema es el MTU de la ruta: el túnel añade sobrecarga y algún enlace intermedio descarta los paquetes demasiado grandes sin devolver un mensaje ICMP. Reduzca MTU en [Interface] del cliente, pruebe 1420, después 1380 y luego 1280. Si reducir el MTU elimina las interrupciones, pero el rendimiento sigue siendo insuficiente, no siga probando valores redondos. Consulte cómo encontrar el MTU real de la ruta mediante bisección y limitar el MSS de TCP. Este procedimiento también descarta causas que no tienen relación con el túnel.
La interfaz no se inicia. Address already in use significa que otro proceso está usando UDP 51820. Cannot find device wg0 después de un up fallido suele indicar que se rechazó la configuración. Lea journalctl -u wg-quick@wg0 -n 50.
Migración desde Streisand u OpenVPN
Streisand ya no recibe mantenimiento y su repositorio está archivado. Ejecutar una VPN sobre automatización abandonada genera un problema de seguridad que empeora con el tiempo. No existe una actualización in situ y la PKI de OpenVPN no se puede convertir: WireGuard no usa certificados ni CA, y sus claves no caducan. Por tanto, cada cliente debe recibir un nuevo par de claves.
Realice la migración en paralelo. WireGuard en UDP 51820 puede coexistir con OpenVPN en 1194 en el mismo equipo. Configure wg0, migre los clientes de uno en uno y detenga después el servicio antiguo. El modelo de nombres de usuario, contraseñas y revocación de OpenVPN no se puede trasladar. Si necesita cuentas o un registro de auditoría, añada esa funcionalidad por encima de WireGuard.
Copias de seguridad, actualizaciones y los factores que limitan la escala
/etc/wireguard es el servidor. Haga una copia de seguridad (sudo tar czf wg-backup.tgz -C /etc wireguard, con permisos 600 y almacenada fuera del servidor) y podrá reconstruirlo en un VPS nuevo en minutos. Si pierde la clave privada del servidor, deberá volver a emitir la configuración de cada cliente, porque los clientes fijan la clave pública del servidor. Las actualizaciones son un apt upgrade normal, además de un reinicio cuando se actualiza el kernel, y wg-quick@wg0 vuelve a estar disponible por sí solo si lo habilitó.
El estado de cada par es pequeño y las operaciones criptográficas se ejecutan en el kernel, por lo que el límite lo determinan la CPU y el ancho de banda de su VPS, no ningún valor de esta configuración. Mídalo con iperf3 a través del túnel en lugar de confiar en una cifra publicada. Lo que se complica a escala es la operación. Cada par necesita una IP de túnel única, y editar manualmente sesenta bloques [Peer] es la forma de que aparezcan AllowedIPs duplicados: genere las configuraciones con un script. Un servidor es un único endpoint UDP y un único punto de fallo, y WireGuard no ofrece clustering: la redundancia requiere un segundo servidor con sus propias claves. La rotación de claves sigue siendo manual, así que documente quién tiene cada clave y cómo revocarla. Cuando ese control supera lo que puede gestionarse en un archivo de texto, la respuesta habitual es añadir un plano de control sobre el mismo plano de datos del kernel, y un servidor NetBird autohospedado se encarga de la asignación de direcciones, la distribución de pares y las claves de configuración que, de otro modo, gestionaría manualmente. Si ejecutar ese plano de control usted mismo supone administrar un servidor adicional, Tailscale aloja uno por usted, y su plan gratuito cubre seis usuarios con dispositivos ilimitados, suficiente para que la mayoría de las redes personales nunca tengan que pagar. A partir de ahí, el coste depende de las personas y no de las máquinas, así que lo que paga realmente una familia o un equipo pequeño depende de cuántas personas tengan credenciales de acceso, no de cuántos pares habría tenido que editar manualmente en wg0.conf. En ese modelo, la gestión de AllowedIPs del split tunnel pasa a ser anunciar sus rangos privados desde un router de subred, anunciado una vez desde un VPS y aprobado de forma centralizada, en lugar de copiarlo en cada archivo de cliente. La conveniencia de ese intercambio depende de a qué recursos pueda acceder realmente un plano de control alojado, y nunca contiene las claves que cifran su tráfico, aunque sí decide qué pares conocen la existencia de los demás.
Todo esto requiere un equipo Linux bajo su control, una IP pública, un kernel en el que pueda cargar un módulo y un firewall que administre de extremo a extremo.
FAQ
¿Por qué el handshake de WireGuard nunca se completa?
wg show al mostrar un peer sin latest handshake significa que los paquetes no llegan o no se aceptan. Compruebe UDP 51820 en el firewall del VPS y en el firewall de red independiente del proveedor. Confirme el host y el puerto de Endpoint. Después, compruebe que las claves no estén intercambiadas. El bloque [Peer] del cliente debe contener la clave pública del servidor. sudo tcpdump -ni any udp port 51820 en el servidor muestra si llegan paquetes. dmesg sólo informa de los fallos del handshake de WireGuard después de activar la depuración dinámica (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). En ese caso, una discrepancia de claves aparece como un descarte por MAC no válida.
El túnel se conecta, pero no tengo Internet. ¿Qué falta?
Que ping 10.8.0.1 funcione mientras ping 1.1.1.1 agota el tiempo de espera apunta a un problema de reenvío o NAT. Confirme que sysctl net.ipv4.ip_forward lea 1 y que esté configurado en /etc/sysctl.d/, no sólo mediante un sysctl -w que desaparezca al reiniciar. Después, compruebe que la regla de masquerade use la interfaz de salida real indicada por ip route show default, enp1s0 o ens3, y en raras ocasiones eth0.
¿Necesito la línea DNS = en la configuración del cliente?
Con un túnel completo y sin la línea DNS =, el cliente conserva el resolver que aprendió de la red local. Esas consultas salen sin cifrar por el enlace local, mientras que el resto del tráfico se tuneliza. Apunte DNS a un resolver público, o ejecute unbound/dnsmasq asociado a 10.8.0.1 y abra udp dport 53 iifname "wg0" en la cadena de entrada.
¿Qué controla realmente AllowedIPs?
Tiene dos funciones. Para el tráfico saliente, es una tabla de enrutamiento: el tráfico que coincide con AllowedIPs de un peer se cifra y se envía a ese peer. Para el tráfico entrante, es una lista de control de acceso: se descarta un paquete descifrado cuyo origen esté fuera de AllowedIPs de ese peer. Por eso el servidor especifica un /32 por cliente, mientras que el cliente puede especificar 0.0.0.0/0.
¿WireGuard funcionará en cualquier VPS?
En un VPS KVM funciona con el módulo del kernel y sin configuración adicional. En una virtualización basada en contenedores que comparte el kernel del host, como OpenVZ o LXC, modprobe wireguard falla con Operation not supported. La alternativa es la implementación en espacio de usuario wireguard-go. Ejecute sudo modprobe wireguard && echo ok antes de cualquier otra acción.