Hasta ahora has cerrado puertas. En el capítulo 1 blindaste SSH con llaves Ed25519 y un puerto personalizado. En el capítulo 2 montaste nftables con política drop por defecto. En los capítulos 5 y 6 pusiste Traefik como puerta de entrada con middlewares de seguridad. En los capítulos 9 a 12 añadiste autenticación con Authelia, Authentik y Pocket ID. Tu servidor es una fortaleza. Pero hay un problema.
Todos esos servicios que has configurado con tanto cariño están expuestos a internet. Sí, protegidos con autenticación, HTTPS y firewalls. Pero siguen siendo accesibles desde cualquier rincón del mundo. Cualquier persona con una conexión puede intentar entrar. Cada puerto abierto es una superficie de ataque. Cada servicio expuesto es un posible vector de entrada. ¿Y si pudieras hacer que ciertos servicios ni siquiera fueran visibles desde internet? ¿Y si pudieras acceder a tu red local desde cualquier lugar como si estuvieras en casa, sin exponer nada al mundo exterior? Eso es exactamente lo que hace una VPN.
¿Por qué una VPN para self-hosted?
Una VPN (Virtual Private Network) crea un túnel cifrado entre tu dispositivo y tu servidor. Todo el tráfico que pasa por ese túnel viaja cifrado y autenticado. Desde fuera parece ruido aleatorio. Desde dentro parece que estás en tu red local.
¿Por qué necesitas una VPN si ya tienes autenticación con Authelia o Pocket ID?
Buena pregunta. Vamos a ver las diferencias.
Abrir puertos vs. VPN
Cuando abres un puerto en tu firewall y expones un servicio a internet, ese servicio es visible para todo el mundo. Puede escanearte un bot en cuanto tu IP aparezca en Shodan. Puede probar vulnerabilidades el mismo día en que se publica un CVE. Puede sufrir un ataque de denegación de servicio aunque tengas rate limiting.
Pones toda tu confianza en la capa de autenticación. Si falla, el atacante tiene acceso directo a tu servicio.
Con una VPN, el servicio ni siquiera está expuesto. El firewall no tiene abierto el puerto 443 para ese servicio. Solo tiene abierto el puerto de WireGuard (un único puerto UDP). Para acceder a tus servicios, primero tienes que conectarte a la VPN. Sin conexión VPN, no hay servicio al que acceder.
La diferencia es sutil pero enorme. No es que el servicio esté protegido. Es que el servicio no existe fuera de tu red privada.
¿Qué servicios deberías poner tras la VPN?
La regla general es sencilla. Si un servicio es solo para ti y tu familia, ponlo tras la VPN. Si un servicio necesita ser accesible desde cualquier lugar (un blog público, una API que consumes desde fuera, un Nextcloud que compartes con gente que no va a instalar una VPN), entonces sí, lo expones con autenticación.
Ejemplos típicos de servicios que van tras la VPN:
- Paneles de administración (Portainer, Cockpit, phpMyAdmin).
- Herramientas de monitorización (Grafana, Prometheus, Netdata).
- Bases de datos (no expongas PostgreSQL o MySQL a internet nunca).
- Servicios de descarga (Sonarr, Radarr, Transmission).
- Acceso SSH a tu servidor desde fuera (a través de la VPN, no directamente).
Ejemplos de servicios que pueden ir expuestos con autenticación:
- Nextcloud (si necesitas compartir archivos con externos).
- Bitwarden/Vaultwarden (necesitas acceder desde cualquier dispositivo).
- Servicios públicos (tu blog, tu sitio web).
La combinación ideal: servicios críticos o de administración solo por VPN. Servicios de usuario con autenticación fuerte y expuestos.
WireGuard frente a OpenVPN
No eres el primero en necesitar una VPN. OpenVPN lleva veinte años siendo la solución estándar. Pero WireGuard llegó en 2016 y cambió las reglas del juego.
Rendimiento
WireGuard corre en el kernel de Linux. No hay cambio de contexto entre espacio de usuario y kernel. No hay copias de memoria innecesarias. El cifrado es ChaCha20-Poly1305, diseñado para ser rápido incluso en CPUs sin aceleración AES.
OpenVPN usa OpenSSL y TLS. Cada paquete pasa por una pila de protocolos compleja. El rendimiento es aceptable, pero WireGuard es entre 2 y 4 veces más rápido en la mayoría de pruebas. En CPUs ARM de bajo consumo (Raspberry Pi, ODROID), la diferencia es abismal.
Simplicidad
OpenVPN tiene cientos de opciones de configuración. Modos de autenticación, compresión, protocolos de túnel, opciones TLS, scripts de conexión, gestión de rutas. Es poderoso, pero abrumador.
WireGuard tiene menos de 4.000 líneas de código. Su configuración es un archivo de 10 líneas. Una clave privada, una clave pública, un puerto, una dirección IP. Ya está.
No hay demonio que configurar. No hay certificados que gestionar. No hay CA que firmar. No hay modos de operación que elegir.
Menor superficie de ataque
Menos código significa menos bugs. Menos bugs significa menos vulnerabilidades. WireGuard ha sido auditado formalmente por equipos de seguridad independientes. Su superficie de ataque es mínima: solo acepta paquetes UDP en un puerto, y solo responde si están firmados con la clave privada correcta.
OpenVPN, con su pila TLS, su gestión de certificados y sus cientos de opciones, tiene una superficie de ataque mucho mayor. Ha tenido vulnerabilidades en el pasado. Las tendrá en el futuro.
¿Cuándo elegir OpenVPN?
WireGuard es mejor en casi todo. Pero hay escenarios donde OpenVPN sigue siendo necesario:
- Si necesitas que los clientes se conecten a través de proxies HTTP.
- Si necesitas autenticación con certificados de cliente en lugar de claves pre-compartidas.
- Si tu firewall corporativo bloquea UDP y solo permite TCP.
- Si necesitas integración con Active Directory o RADIUS.
Para el 99% de los homelabs, WireGuard es la opción correcta.
Instalación de WireGuard en Ubuntu 24.04
WireGuard viene incluido en el kernel de Linux desde la versión 5.6. Ubuntu 24.04 usa kernel 6.x. No necesitas compilar módulos ni instalar paquetes de kernel adicionales.
Solo necesitas las herramientas de usuario para generar claves y gestionar la interfaz.
sudo apt update
sudo apt install wireguard-tools -y
El paquete wireguard-tools instala:
wg: utilidad para configurar y mostrar interfaces WireGuard.wg-quick: script que simplifica el levantamiento y caída de interfaces.systemd-resolved: integración con resolvconf para DNS automático.
Comprueba que el módulo del kernel está disponible:
sudo modprobe wireguard
lsmod | grep wireguard
Si ves wireguard en la salida, todo está listo. Si no, no pasa nada. En Ubuntu 24.04 el módulo se carga automáticamente al usar wg-quick.
Generación de claves
WireGuard usa criptografía de curva elíptica (Curve25519). Cada peer (servidor o cliente) tiene un par de claves: una privada y una pública.
Generas las claves del servidor:
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
El umask 077 es importante. Las claves privadas de WireGuard deben ser legibles solo por root. Sin este umask, el archivo se crea con permisos que cualquier usuario podría leer.
Generas las claves para cada cliente de la misma forma. En el servidor generas el par del cliente, o bien el cliente genera su propio par y te pasa la clave pública. La segunda opción es más segura porque la clave privada nunca sale del dispositivo del cliente.
# En el dispositivo cliente
umask 077
wg genkey | tee cliente.key | wg pubkey > cliente.pub
La clave pública del cliente la necesitas en el servidor. La clave privada del servidor la necesitas en el servidor. Y cada cliente necesita la clave pública del servidor y su propia clave privada.
Nunca, bajo ninguna circunstancia, compartas una clave privada.
Configuración del servidor
La configuración de WireGuard es un archivo INI. En el servidor, creas /etc/wireguard/wg0.conf:
[Interface]
Address = 10.10.10.1/24
ListenPort = 51820
PrivateKey = (contenido de server.key)
# Regla de iptables para NAT (la sustituiremos por nftables después)
# PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Portátil de Lorenzo
PublicKey = (clave pública del portátil)
AllowedIPs = 10.10.10.2/32
[Peer]
# Móvil de Lorenzo
PublicKey = (clave pública del móvil)
AllowedIPs = 10.10.10.3/32
[Peer]
# Portátil de Ana
PublicKey = (clave pública del portátil de Ana)
AllowedIPs = 10.10.10.4/32
Vamos a analizar cada campo.
[Interface]
Address: la dirección IP que tendrá el servidor dentro de la red VPN. Usas el rango 10.10.10.0/24. Es un rango privado que probablemente no colisiona con tu red local. Si tu red local ya usa 10.10.10.0/24, cambia a 10.11.10.0/24 o 172.16.0.0/24.
ListenPort: el puerto UDP donde WireGuard escucha conexiones. El estándar es 51820, pero puedes usar cualquier puerto. Si tu ISP bloquea ciertos puertos, usa uno diferente. El 443 suele funcionar siempre aunque los ISPs bloqueen otros puertos no estándar.
PrivateKey: la clave privada del servidor. Debe ser solo el contenido del archivo, sin espacios ni saltos de línea extra.
[Peer]
Cada cliente es un [Peer]. No necesitas configurar [Peer] para el servidor en el archivo del servidor. El servidor es el [Interface] y los demás son peers.
PublicKey: la clave pública del cliente.
AllowedIPs: aquí viene la parte más confusa de WireGuard. Tiene dos significados según el contexto.
En el servidor, AllowedIPs determina:
- Qué direcciones IP se permiten desde ese peer. Si el peer intenta usar una IP diferente, WireGuard descarta el paquete.
- Las rutas que se añaden automáticamente al levantar la interfaz. Con
10.10.10.2/32, el servidor sabe que para llegar a10.10.10.2debe usar ese peer.
Siempre usas /32 porque cada peer tiene una única IP dentro de la VPN.
Levantar la interfaz
sudo wg-quick up wg0
Este comando:
- Crea la interfaz virtual
wg0. - Le asigna la dirección IP
10.10.10.1/24. - Configura la clave privada y el puerto de escucha.
- Añade los peers con sus claves públicas y rutas.
Comprueba que la interfaz está levantada:
sudo wg show
Deberías ver algo como:
interface: wg0
public key: (tu clave pública)
private key: (hidden)
listening port: 51820
peer: (clave pública del portátil)
endpoint: (none)
allowed ips: 10.10.10.2/32
peer: (clave pública del móvil)
endpoint: (none)
allowed ips: 10.10.10.3/32
Los endpoints aparecen como (none) porque los clientes aún no se han conectado. WireGuard es peer-to-peer: no necesita que el cliente esté conectado para tenerlo configurado.
Configuración del cliente
Cada cliente necesita su propio archivo de configuración. La estructura es similar, pero con diferencias importantes.
Cliente portátil (Linux)
En tu portátil con Linux, creas /etc/wireguard/wg0.conf (o en cualquier ruta, pero /etc/wireguard/ es el estándar):
[Interface]
Address = 10.10.10.2/24
PrivateKey = (clave privada del portátil)
DNS = 10.10.10.1
[Peer]
PublicKey = (clave pública del servidor)
Endpoint = tu-servidor.com:51820
AllowedIPs = 10.10.10.0/24, 192.168.1.0/24
PersistentKeepalive = 25
Fíjate en las diferencias con el servidor.
Address: la IP que tendrá este cliente dentro de la VPN. Debe coincidir con la AllowedIPs que configuraste en el servidor para este peer.
DNS: opcional. Si pones la IP del servidor WireGuard y el servidor tiene un DNS resolver, las consultas DNS de la máquina cliente irán por la VPN. Esto es útil si tu servidor es también tu DNS local (Pi-hole, AdGuard Home, Unbound).
Endpoint: la dirección pública de tu servidor y el puerto donde WireGuard escucha. Puede ser un dominio (si tienes DDNS) o una IP pública.
AllowedIPs: aquí está la clave del split tunneling. En el cliente, AllowedIPs determina qué tráfico va por la VPN.
10.10.10.0/24: el tráfico hacia la red VPN va por la VPN.192.168.1.0/24: el tráfico hacia tu red local va por la VPN.
Si pones 0.0.0.0/0, todo el tráfico de internet va por la VPN (full tunnel). Esto es útil para proteger tu conexión en redes WiFi públicas, pero añade latencia a todo tu tráfico.
PersistentKeepalive: cada 25 segundos, el cliente envía un paquete keepalive al servidor. Esto mantiene abierto el túnel incluso cuando estás detrás de NAT o un firewall que cierra conexiones inactivas. Sin esto, si tu cliente está detrás de un NAT (la mayoría de los hogares), el servidor no puede iniciar la conexión hacia el cliente y el túnel se cae cuando cambia tu IP local.
Cliente smartphone (iOS / Android)
La aplicación oficial de WireGuard está en la App Store y en Google Play. Es gratuita, open source y no tiene publicidad.
Para configurar el móvil, tienes dos opciones.
Opción A: archivo de configuración por QR
En el servidor, generas un archivo de configuración para el móvil:
[Interface]
Address = 10.10.10.3/24
PrivateKey = (clave privada del móvil)
DNS = 10.10.10.1
[Peer]
PublicKey = (clave pública del servidor)
Endpoint = tu-servidor.com:51820
AllowedIPs = 10.10.10.0/24, 192.168.1.0/24
PersistentKeepalive = 25
Conviertes ese archivo en un código QR con qrencode:
sudo apt install qrencode -y
qrencode -t ansiutf8 < movil.conf
Escaneas el QR desde la app de WireGuard en el móvil y la configuración se importa automáticamente. No necesitas escribir nada en el teléfono.
Opción B: manual desde la app
Abres la app, pulsas el botón «+» y seleccionas «Crear desde cero». Rellenas los mismos campos que en el archivo de configuración: dirección, DNS, clave privada, y los datos del peer (clave pública del servidor, endpoint, allowed IPs, keepalive).
La opción del QR es más cómoda y menos propensa a errores.
Cliente Windows / macOS
En Windows y macOS, la app oficial de WireGuard también está disponible. Descárgala desde wireguard.com/install.
El proceso es el mismo que en el móvil: importas un archivo .conf o lo creas manualmente. La app se encarga de gestionar la interfaz virtual.
Enrutamiento y split tunneling
Una de las decisiones más importantes al configurar WireGuard es decidir qué tráfico va por la VPN.
Full tunnel
Todo el tráfico de internet de tu dispositivo va por la VPN. Usas la IP de tu servidor para todo. Esto es útil cuando:
- Estás en una red WiFi pública no segura (aeropuerto, cafetería, hotel).
- Quieres que tu IP pública sea siempre la de tu servidor.
- Quieres evitar bloqueos geográficos o censura de tu ISP.
Configuración en el cliente:
AllowedIPs = 0.0.0.0/0, ::/0
Ventajas: máxima privacidad y seguridad en redes no confiables.
Inconvenientes: mayor latencia para todo tu tráfico. Consumo de ancho de banda de tu servidor. Si tu servidor tiene un límite de datos, lo alcanzas rápido.
Split tunneling (recomendado)
Solo el tráfico hacia tu red local va por la VPN. El resto (YouTube, Google, Netflix, lo que sea) va por tu conexión normal.
Configuración en el cliente:
AllowedIPs = 10.10.10.0/24, 192.168.1.0/24
Esta es la configuración que recomiendo para el día a día.
- Accedes a tus servicios en
192.168.1.x(tus servidores domésticos) como si estuvieras en casa. - Accedes a los servicios que están dentro de la VPN (
10.10.10.x) directamente. - El resto de tu tráfico no sufre latencia adicional.
Puedes añadir más rutas según necesites:
AllowedIPs = 10.10.10.0/24, 192.168.1.0/24, 172.16.0.0/12
Acceso a servicios Docker
Si tus servicios Docker usan redes separadas (por ejemplo, 172.16.0.0/12 para Traefik y los contenedores), necesitas añadir esas rutas a AllowedIPs para poder acceder a los contenedores por su IP Docker.
Pero ojo. Normalmente no necesitas acceder a contenedores por su IP Docker. Accedes a través del dominio que has configurado en Traefik. Y si Traefik está en tu red local (192.168.1.x), con la ruta a tu red local es suficiente.
La única excepción es si quieres acceder directamente a servicios que no tienen ruta en Traefik. En ese caso, necesitas la ruta a la red Docker correspondiente.
Integración con nftables
En el capítulo 2 configuraste nftables con política drop por defecto. Ahora necesitas añadir reglas para que WireGuard funcione.
Abrir el puerto de WireGuard
WireGuard usa UDP. Necesitas permitir tráfico UDP entrante al puerto que elegiste (51820 o el que hayas puesto).
Añades esta regla a tu cadena input de nftables, antes de la política drop:
udp dport 51820 accept
Si usas el script completo del capítulo 2, lo añades después de la regla de SSH y antes de los servicios web:
# WireGuard (VPN)
udp dport 51820 accept
Permitir tráfico forward de la interfaz wg0
WireGuard necesita reenviar paquetes entre la interfaz wg0 y tu interfaz de red principal (eth0, enp1s0, etc.). Necesitas reglas en la cadena forward de nftables.
chain forward {
type filter hook forward priority 0; policy drop;
# Permitir tráfico desde WireGuard hacia la red local
iif "wg0" oif "eth0" accept
# Permitir respuestas de la red local hacia WireGuard
iif "eth0" oif "wg0" ct state related,established accept
}
La primera regla permite el tráfico que viene de la VPN hacia tu red local. Cuando estás fuera y accedes a 192.168.1.100 (tu servidor), el paquete entra por wg0 y sale por eth0 hacia la red local.
La segunda regla permite las respuestas. Tu servidor local responde al paquete, la respuesta entra por eth0 y debe salir por wg0 hacia el cliente VPN. Solo permites respuestas a conexiones establecidas.
NAT (masquerading)
Tus clientes VPN tienen direcciones IP del rango 10.10.10.0/24. Cuando un cliente VPN accede a un servicio en tu red local (192.168.1.100), el servicio ve la petición desde 10.10.10.2. Si el servicio espera tráfico de la red local, puede no saber cómo responder a una IP que no conoce.
Necesitas NAT para que el tráfico de la VPN parezca venir de la IP del servidor en tu red local.
En nftables, añades una tabla nat y una regla de masquerading:
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
# Trafico desde VPN hacia la red local
ip saddr 10.10.10.0/24 oif "eth0" masquerade
}
}
Esta regla modifica la IP de origen de los paquetes que vienen de la VPN (10.10.10.0/24) y salen por eth0 hacia tu red local. El destino ve la IP del servidor, no la IP del cliente VPN.
Si también quieres que los clientes VPN tengan salida a internet a través de tu servidor, puedes añadir la misma regla para cualquier interfaz de salida. Pero si usas split tunneling, los clientes ya tienen su propia conexión a internet y no necesitan esto.
Script completo de nftables con WireGuard
Integrando todo, tu archivo /etc/nftables.conf queda así:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Loopback
iif "lo" accept
# Conexiones establecidas
ct state related,established accept
# SSH
tcp dport 8822 accept
# WireGuard
udp dport 51820 accept
# Servicios web (opcional, si expones algo)
# tcp dport {http, https} accept
# ICMP controlado
icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded, parameter-problem } accept
icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, parameter-problem, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
# Rate limiting
tcp flags syn meter flood { ip saddr limit rate 10/second burst 20 } log prefix "NF-RATE-LIMIT: " accept
# Logging de descartes
log prefix "NF-DROP: " limit rate 5/minute
}
chain forward {
type filter hook forward priority 0; policy drop;
# Tráfico de WireGuard a red local
iif "wg0" oif "eth0" accept
# Respuestas de red local a WireGuard
iif "eth0" oif "wg0" ct state related,established accept
}
chain output {
type filter hook output priority 0; policy accept;
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.10.10.0/24 oif "eth0" masquerade
}
}
Ajusta eth0 por el nombre de tu interfaz de red principal. Puedes averiguarlo con ip link show o ip route show default.
Carga las reglas:
sudo nft -f /etc/nftables.conf
Activar IP forwarding
El kernel de Linux no reenvía paquetes entre interfaces por defecto. Necesitas activarlo para que WireGuard funcione:
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf
Si usas IPv6, añade también:
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.d/99-wireguard.conf
Automatización: systemd y wg-quick
wg-quick genera automáticamente un servicio systemd para cada interfaz WireGuard. Cuando creas /etc/wireguard/wg0.conf, el servicio se llama wg-quick@wg0.
Habilitar auto-arranque
sudo systemctl enable wg-quick@wg0
Con esto, WireGuard arranca automáticamente al iniciar el servidor. No necesitas hacer nada más.
Iniciar, detener, reiniciar
sudo systemctl start wg-quick@wg0
sudo systemctl stop wg-quick@wg0
sudo systemctl restart wg-quick@wg0
Ver estado
sudo systemctl status wg-quick@wg0
Y para ver la configuración activa de WireGuard:
sudo wg show
Dependencias de red
Si tu servidor usa NetworkManager, wg-quick espera a que la red esté lista antes de levantar la interfaz. No deberías tener problemas. Pero si usas systemd-networkd, asegúrate de que la dependencia está configurada correctamente.
Puedes comprobarlo con:
sudo systemctl cat wg-quick@wg0
Verás algo como:
[Unit]
Description=WireGuard via wg-quick(8) for %I
After=network-online.target
Wants=network-online.target
...
Si no ves esas líneas, no pasa nada. wg-quick las incluye por defecto.
Añadir y eliminar peers
Añadir un nuevo cliente es sencillo.
- Generas un par de claves para el nuevo cliente.
- Añades un
[Peer]en el archivowg0.confdel servidor. - Creas el archivo de configuración para el cliente.
- Reinicias WireGuard en el servidor.
# En el servidor
sudo nano /etc/wireguard/wg0.conf
# Añades:
# [Peer]
# PublicKey = (clave pública del nuevo cliente)
# AllowedIPs = 10.10.10.5/32
sudo systemctl restart wg-quick@wg0
Para eliminar un peer, simplemente borras su bloque [Peer] y reinicias.
Si quieres hacerlo sin reiniciar (en caliente), puedes usar wg directamente:
# Añadir peer sin reiniciar
sudo wg set wg0 peer (clave-pública) allowed-ips 10.10.10.5/32
# Eliminar peer sin reiniciar
sudo wg set wg0 peer (clave-pública) remove
Pero cuidado. Los cambios con wg set no se persisten en el archivo de configuración. Si reinicias el servicio o el servidor, los cambios se pierden. Siempre actualiza también el archivo wg0.conf.
WireGuard en Docker (wg-easy)
Si prefieres no instalar WireGuard directamente en el servidor, o si tu servidor ya está tan lleno de servicios que no quieres añadir más configuración a nivel de sistema, puedes ejecutar WireGuard en Docker.
wg-easy es la opción más popular. Es un contenedor que incluye WireGuard y una interfaz web sencilla para gestionar peers.
¿Por qué usar wg-easy?
- Interfaz web para añadir y eliminar peers.
- Generación automática de archivos de configuración y códigos QR.
- Script de instalación con wizard guiado.
- No necesitas tocar archivos de configuración manualmente.
¿Por qué no usar wg-easy?
- Un contenedor más que mantener.
- La interfaz web es otro servicio que proteger.
- Si el contenedor se cae, pierdes la VPN.
- Menos control fino sobre la configuración.
- Dependencia de una imagen de terceros.
Mi recomendación: si tienes pocos peers (menos de 5) y te sientes cómodo editando archivos de configuración, usa WireGuard nativo. Si tienes muchos peers o quieres dárselo a usuarios no técnicos, wg-easy te facilita la vida.
Instalación de wg-easy
# docker-compose.yml
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:latest
container_name: wg-easy
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
volumes:
- ./data:/etc/wireguard
environment:
- WG_HOST=vpn.tudominio.com
- PASSWORD=tu-contraseña
- WG_PORT=51820
- WG_DEFAULT_ADDRESS=10.10.10.x
- WG_DEFAULT_DNS=10.10.10.1
- WG_ALLOWED_IPS=10.10.10.0/24, 192.168.1.0/24
- WG_PERSISTENT_KEEPALIVE=25
ports:
- "51820:51820/udp"
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.wg-easy.rule=Host(`vpn.tudominio.com`)"
- "traefik.http.routers.wg-easy.entrypoints=websecure"
- "traefik.http.routers.wg-easy.tls=true"
- "traefik.http.routers.wg-easy.tls.certresolver=letsencrypt"
- "traefik.http.services.wg-easy.loadbalancer.server.port=51821"
Variables clave:
- WG_HOST: el dominio o IP pública de tu servidor. Se incluye en la configuración de los clientes.
- PASSWORD: contraseña para acceder al panel web. No es súper segura, pero como el panel solo es accesible desde tu red local o tras autenticación, es suficiente.
- WG_PORT: el puerto UDP externo.
- WG_DEFAULT_ADDRESS: el rango de direcciones para asignar a los peers. La
xse reemplaza por un número secuencial. - WG_ALLOWED_IPS: las rutas que se configuran en los clientes. Aquí pones tu red local y el rango de la VPN.
- WG_PERSISTENT_KEEPALIVE: keepalive para clientes detrás de NAT.
El panel web se sirve en el puerto 51821. Lo expones con Traefik para tener HTTPS.
Accedes a https://vpn.tudominio.com, introduces la contraseña, y ves una tabla con los peers. Puedes añadir, eliminar y descargar configuraciones. Cada peer nuevo genera automáticamente un archivo .conf y un código QR.
Limitaciones de wg-easy
- El panel web no tiene autenticación con OIDC. Solo una contraseña compartida.
- No puedes personalizar la configuración por peer más allá de lo básico.
- La imagen se actualiza con frecuencia. Revisa las release notes antes de actualizar.
- Si usas nftables, necesitas las mismas reglas de forward y NAT que con WireGuard nativo.
Verificación
Has configurado WireGuard. Ahora toca comprobar que todo funciona.
Probar conexión básica
Desde el cliente (tu portátil, por ejemplo), activas WireGuard:
sudo wg-quick up wg0
Si todo va bien, verás algo como:
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.10.10.2/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] resolvconf -a tun.wg0 -m 0 -x
[#] ip -4 route add 10.10.10.0/24 dev wg0
[#] ip -4 route add 192.168.1.0/24 dev wg0
Haces ping a la IP del servidor dentro de la VPN:
ping -c 3 10.10.10.1
Deberías recibir respuesta. Si no, revisa:
- Que el firewall permite UDP en el puerto 51820.
- Que las claves públicas coinciden entre servidor y cliente.
- Que el endpoint del cliente apunta a la IP o dominio correcto de tu servidor.
- Que el servidor tiene IP forwarding activado.
Comprobar que el tráfico pasa por WireGuard
Desde el cliente, verificas que tienes conectividad con tu red local:
ping -c 3 192.168.1.100
Si tu servidor tiene la IP 192.168.1.100 en tu red local, este ping debería funcionar igual que si estuvieras en casa.
Ahora la prueba definitiva: compruebas que el tráfico hacia internet NO pasa por la VPN (si usas split tunneling).
curl ifconfig.me
Esta página te dice tu IP pública. Debería ser la IP de tu conexión actual (la del WiFi donde estés), no la IP de tu servidor. Si ves la IP de tu servidor, tienes configurado AllowedIPs = 0.0.0.0/0 en el cliente, o has añadido la ruta por defecto sin querer.
Test de velocidad
WireGuard es rápido, pero conviene comprobarlo. Desde el cliente, transfieres un archivo grande al servidor dentro de la VPN:
# En el servidor, creas un archivo de prueba
dd if=/dev/urandom of=/tmp/test.bin bs=1M count=100
# Desde el cliente
scp -P 8822 usuario@10.10.10.1:/tmp/test.bin /dev/null
Mide el tiempo que tarda. En una conexión normal, 100 MB deberían transferirse en pocos segundos. Si ves velocidades muy bajas, puede ser por:
- MTU incorrecto. WireGuard usa MTU 1420 por defecto. Si tu conexión tiene problemas con ese MTU, baja a 1280.
- CPU limitada. WireGuard necesita CPU para cifrar. En servidores muy pequeños (512 MB RAM, 1 vCPU), el cifrado puede ser el cuello de botella.
- Latencia alta. Si tu conexión a internet tiene mucha latencia, el TCP sobre WireGuard se resiente. No es problema de WireGuard, es física.
También puedes usar iperf3 para una medición más precisa:
# En el servidor
iperf3 -s
# Desde el cliente, conectando a la IP VPN del servidor
iperf3 -c 10.10.10.1
Verificar peers conectados
En el servidor:
sudo wg show
Fíjate en la columna endpoint. Cuando un cliente está conectado, ves su IP pública y puerto. Cuando no está conectado, ves (none).
interface: wg0
...
peer: (clave pública del portátil)
endpoint: 83.45.12.34:51820
allowed ips: 10.10.10.2/32
latest handshake: 1 minute, 23 seconds ago
transfer: 1.24 MiB received, 3.45 MiB sent
El campo latest handshake te dice cuándo fue el último intercambio de claves con ese peer. Si ves valores de horas o días, ese peer lleva tiempo sin conectarse o se ha desconectado.
El campo transfer te muestra el tráfico intercambiado. Es útil para saber cuánto están usando la VPN tus clientes.
Comprobar los logs
WireGuard no genera logs por defecto. Pero puedes ver los mensajes del kernel relacionados:
sudo dmesg | grep wireguard
Si hay errores de handshake, MTU o enrutamiento, aparecen aquí.
También puedes ver los logs de systemd del servicio:
sudo journalctl -u wg-quick@wg0
Probar desde el móvil
Desconecta el WiFi de tu móvil. Activa los datos móviles. Abre la app de WireGuard y pulsa el botón de conexión.
Cuando veas el icono de la llave (iOS) o el mensaje «Conectado» (Android), abre un navegador y accede a http://192.168.1.100:8080 (o la IP y puerto de cualquier servicio de tu red local).
Deberías ver el servicio como si estuvieras en casa. Si ves la página, la VPN funciona desde tu móvil con datos móviles.
Resumen
WireGuard no es solo una VPN más. Es un cambio de paradigma en cómo te conectas a tu red local desde fuera.
Con WireGuard:
- Cierras la exposición de servicios de administración a internet.
- Accedes a tu red local desde cualquier lugar con un solo clic.
- No necesitas recordar IPs, puertos ni configuraciones complejas.
- Tu tráfico viaja cifrado con criptografía moderna y eficiente.
- El rendimiento es tan bueno que ni notas que estás usando una VPN.
En este capítulo has visto:
- Por qué una VPN es mejor que abrir puertos para servicios de administración.
- Las diferencias entre WireGuard y OpenVPN: rendimiento, simplicidad y seguridad.
- La instalación de WireGuard en Ubuntu 24.04 con un solo paquete.
- La generación de claves y la configuración del servidor con wg0.conf y peers.
- La configuración de clientes en portátil, smartphone, Windows y macOS.
- El enrutamiento y split tunneling: solo tu tráfico local por la VPN.
- La integración completa con nftables: reglas de firewall, forward y NAT.
- La automatización con systemd y wg-quick para que WireGuard arranque solo.
- La opción de usar wg-easy en Docker si prefieres una interfaz gráfica.
- La verificación de todo: ping, test de velocidad, peers conectados, logs.
Tu servidor ya no es una fortaleza con las puertas abiertas. Ahora es una fortaleza a la que solo tú tienes la llave. Y esa llave es tu conexión WireGuard.
En el próximo capítulo vamos a dar un paso más. WireGuard te da acceso a tu red local, pero ¿y si quieres conectar múltiples servidores entre sí? Ahí entran las mallas VPN con herramientas como Netbird, Tailscale o Headscale. Pero eso es otra historia.
Más información,
- Documentación oficial de WireGuard — https://www.wireguard.com/
- Documentación de wg-quick —
man wg-quick - Documentación de wg-easy — https://github.com/wg-easy/wg-easy
- Instalación de WireGuard en Ubuntu — https://ubuntu.com/server/docs/network-wireguard
- WireGuard en Arch Wiki — https://wiki.archlinux.org/title/WireGuard