WireGuard es un protocolo VPN y su implementación de referencia, integrado directamente en el kernel Linux desde la versión 5.6. A diferencia de OpenVPN, que funciona en espacio de usuario y negocia conexiones a través de TLS con cientos de parámetros configurables, WireGuard opera en el kernel y tiene una superficie criptográfica fija: Curve25519 para el intercambio de claves, ChaCha20-Poly1305 para el cifrado autenticado, y BLAKE2s para los hashes. No hay negociación de algoritmos, no hay modos legacy, no hay opciones que puedas elegir mal. Eso es un diseño deliberado.
El modelo conceptual es minimalista: cada nodo tiene un par de claves (pública/privada), y defines explícitamente qué peers pueden comunicarse contigo listando su clave pública y los rangos de IP (AllowedIPs) que les pertenecen. WireGuard actúa como una interfaz de red (wg0) que encapsula tráfico UDP. Cuando el kernel ve un paquete saliendo hacia una IP dentro del rango de AllowedIPs de un peer, lo cifra y lo envía al endpoint de ese peer. Cuando llega un paquete cifrado de una clave conocida, lo descifra y lo inyecta en la interfaz. Es básicamente un túnel IP con autenticación por clave pública, sin estado de sesión explícito ni handshake visible para el usuario.
Lo usas cuando necesitas conectar dos o más máquinas de forma cifrada —acceso a una red interna desde fuera, enlazar servidores en distintos proveedores cloud, o VPN personal desde tu portátil— y quieres algo que sea auditable (el código fuente del módulo del kernel tiene ~4000 líneas), rápido, y que no requiera PKI completa ni certificados. Si lo que necesitas es un despliegue multi-usuario con gestión centralizada, descubrimiento automático de peers y sin tocar configuración manual, eso es exactamente lo que resuelve Tailscale, que usa WireGuard como capa de transporte pero añade su propio plano de control. La diferencia es que Tailscale gestiona las claves y los endpoints por ti; WireGuard puro es infraestructura, tú gestionas todo.
Si configuras mal los AllowedIPs el tráfico simplemente no fluye —no hay error explícito, el paquete se descarta silenciosamente. Si confundes las claves pública y privada en la configuración el túnel no levanta. Si el puerto UDP está bloqueado por un firewall sin regla explícita, la conexión queda en silencio también. El debugging en WireGuard es deliberadamente poco verboso: o funciona o no, y el diagnóstico es manual.
# ── INSTALACIÓN ──────────────────────────────────────────────────────────
# En Debian Bookworm el módulo ya está en el kernel; solo necesitamos
# las herramientas de espacio de usuario.
apt install wireguard
# ── GENERACIÓN DE CLAVES (se hace en cada nodo) ──────────────────────────
# wg genkey produce una clave privada en base64; la derivamos para obtener
# la pública. Guardamos con permisos estrictos desde el principio.
(umask 077; wg genkey > /etc/wireguard/server_private.key)
wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key
# Igual en el cliente (puedes hacerlo aquí para tenerlo a mano):
(umask 077; wg genkey > /etc/wireguard/client_private.key)
wg pubkey < /etc/wireguard/client_private.key > /etc/wireguard/client_public.key
# ── VERIFICACIÓN DE CLAVES GENERADAS ─────────────────────────────────────
cat /etc/wireguard/server_public.key
cat /etc/wireguard/client_public.key
# ── CONFIGURACIÓN DEL SERVIDOR (198.51.100.1) ────────────────────────────
# El servidor es solo el nodo que tiene un endpoint estático conocido.
# En WireGuard no existe realmente distinción servidor/cliente a nivel
# de protocolo, pero sí a nivel de quién tiene IP pública fija.
cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
# IP privada del servidor dentro del túnel
Address = 10.10.0.1/24
# Puerto UDP en el que escucha (debe ser alcanzable desde el cliente)
ListenPort = 51820
# La clave privada del servidor — nunca compartas este archivo
PrivateKey = <contenido de /etc/wireguard/server_private.key>
# Activamos forwarding y masquerading para que los clientes puedan
# salir a internet a través del servidor. ens3 es la interfaz externa;
# ajusta al nombre real de tu interfaz (ip link para verlo).
PostUp = sysctl -w net.ipv4.ip_forward=1; \
nft add table ip nat; \
nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }; \
nft add rule ip nat postrouting oifname "ens3" masquerade
PostDown = nft delete table ip nat
[Peer]
# Clave pública del cliente — identifica criptográficamente al peer
PublicKey = <contenido de /etc/wireguard/client_public.key>
# Solo los paquetes con origen en esta IP son aceptados desde este peer.
# Para múltiples clientes usa una IP distinta por peer dentro del /24.
AllowedIPs = 10.10.0.2/32
EOF
# Permisos correctos — wg-quick avisa si son demasiado permisivos
chmod 600 /etc/wireguard/wg0.conf
# ── CONFIGURACIÓN DEL CLIENTE ─────────────────────────────────────────────
# Este bloque lo ejecutas en la máquina cliente, no en el servidor.
# Lo mostramos aquí para tener el ejemplo completo en un solo lugar.
cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.10.0.2/24
PrivateKey = <contenido de /etc/wireguard/client_private.key>
# DNS opcional — útil si quieres resolver nombres internos por el túnel
DNS = 1.1.1.1
[Peer]
# Clave pública del servidor
PublicKey = <contenido de /etc/wireguard/server_public.key>
# Endpoint estático del servidor: IP pública y puerto
Endpoint = 198.51.100.1:51820
# 0.0.0.0/0 redirige TODO el tráfico por el túnel (full-tunnel).
# Para split-tunnel pon solo los rangos que quieres alcanzar,
# p.ej. 10.10.0.0/24 para acceder solo a la red interna.
AllowedIPs = 0.0.0.0/0, ::/0
# Mantiene el túnel vivo a través de NAT enviando un paquete keepalive
# cada 25 segundos. Solo necesario en el lado que está detrás de NAT.
PersistentKeepalive = 25
EOF
chmod 600 /etc/wireguard/wg0.conf
# ── LEVANTAR Y VERIFICAR ──────────────────────────────────────────────────
# wg-quick lee wg0.conf, crea la interfaz, aplica las claves y las rutas.
wg-quick up wg0
# Estado del túnel: muestra peers, bytes transferidos, último handshake
wg show
# Comprobación básica de conectividad a través del túnel
ping -c 3 10.10.0.1
# ── PERSISTENCIA CON SYSTEMD ──────────────────────────────────────────────
# La unidad wg-quick@.service ya existe tras instalar wireguard-tools.
# Esto levanta wg0 en el arranque sin necesidad de configuración adicional.
systemctl enable --now wg-quick@wg0
# Verificar que el servicio está corriendo
systemctl status wg-quick@wg0
Qué está pasando en cada decisión
La generación de claves con umask 077 no es cosmética. Si generas la clave privada sin restringir los permisos, otros procesos corriendo como usuarios distintos podrían leerla. WireGuard no tiene revocación de claves ni CRL: si alguien obtiene tu clave privada, puede impersonar ese nodo indefinidamente hasta que cambies manualmente todas las claves y actualices la configuración en todos los peers. Por eso la restricción de permisos en el momento de creación es importante, no algo que arreglas después.
AllowedIPs es el mecanismo de autorización, no solo de enrutamiento. Cuando el servidor recibe un paquete descifrado con la clave del cliente, verifica que la IP de origen del paquete interior esté dentro del rango AllowedIPs configurado para ese peer. Si no está, lo descarta. Esto significa que AllowedIPs = 10.10.0.2/32 en el servidor evita que ese cliente pueda enviar tráfico haciéndose pasar por cualquier otra IP del túnel. En el cliente, AllowedIPs controla qué tráfico se redirige al túnel —es tanto una tabla de enrutamiento como una lista de control de acceso, según desde qué lado se mire.
El bloque PostUp usa nft (nftables) en lugar de iptables, que es la herramienta correcta en Debian Bookworm. La tabla nat se crea y destruye dinámicamente con la interfaz. Si tu sistema ya tiene una tabla nat preexistente en nftables, el comando add table fallará; en ese caso añade las reglas directamente a la tabla existente. Fíjate en que ens3 es el nombre de la interfaz externa: si tu servidor usa eth0 o cualquier otro nombre, tienes que cambiarlo, o todo el tráfico de los clientes llegará al túnel pero no saldrá.
PersistentKeepalive = 25 solo tiene sentido en el cliente cuando está detrás de NAT. Los routers NAT expiran las entradas UDP ociosas típicamente entre 30 y 300 segundos. Sin keepalive, después de un período de inactividad el mapeo NAT desaparece y el servidor ya no puede alcanzar al cliente aunque el cliente intente enviar tráfico. El valor 25 es estándar y funciona con la mayoría de equipos NAT.
wg show es el único comando de diagnóstico que necesitas habitualmente. La línea latest handshake es la clave: si no aparece o tiene más de dos minutos, el túnel no está establecido. Si aparece con una marca de tiempo reciente y ves transfer con bytes en ambas direcciones, el túnel funciona. La ausencia de handshake apunta a problema de firewall o de clave incorrecta; bytes transmitidos pero sin respuesta apunta a problema de enrutamiento en destino.
systemctl enable --now wg-quick@wg0 usa la unidad template que wireguard-tools instala. La parte después de @ es el nombre de la interfaz, que corresponde al archivo /etc/wireguard/wg0.conf. Si tienes múltiples túneles (wg0, wg1…), habilitas cada uno por separado con su propia unidad. No hay que crear archivos .service manualmente.