WireGuard en Kubernetes (wg-easy)
Este despliegue proporciona acceso VPN mediante WireGuard dentro de un clúster Kubernetes, utilizando la imagen wg-easy, expuesto por un Service tipo NodePort y gestionado con reglas de iptables en el host.
🔐 ¿Qué es WireGuard?
WireGuard es un protocolo de VPN moderno, rápido y seguro, que utiliza criptografía de última generación para establecer túneles cifrados entre dispositivos.
Este despliegue usa wg-easy, una interfaz web y API para administrar fácilmente instancias de WireGuard.
⚖️ Estructura del despliegue
.
├── daemonset
│ └── iptables-daemonset.yaml # Reglas de NAT
├── deployments
│ └── deployment.yaml # Despliegue de wg-easy
├── ingress
│ └── ingress.yaml # (opcional)
├── pvc
│ └── pvc.yaml # Configuración persistente
├── services
│ └── service-tcp.yaml # Service tipo clusterIP
│ └── service-udp.yaml # Service tipo LoadBalancer
├── secret.yaml # HASH de contraseña para interfaz web
├── namespace.yaml
└── kustomization.yaml
🚫 Seguridad y acceso
Contraseña
Para acceder a la interfaz web protegida de wg-easy, se define un Secret con un hash de contraseña.
Puedes generar el hash con htpasswd (instalado con apache2-utils):
apt install apache2-utils
htpasswd -nbBC 10 "" "Pozuelo12345" | cut -d ":" -f 2
Copia el resultado (sin el admin:) en el archivo secret.yaml como PASSWORD_HASH.
📁 Volúmenes
La configuración de WireGuard se guarda en /etc/wireguard dentro del contenedor. Esto se monta con un PersistentVolumeClaim para que los peers se conserven aunque se reinicie el pod.
🛂 Puertos y exposición
En el despliegue actual se utilizan dos Service separados para exponer WireGuard:
-
Service para la interfaz web (TCP)
- Tipo:
ClusterIP - Protocolo:
TCP - Puerto del contenedor:
51821 - Exposición externa: mediante
Ingress(nginx) con dominio HTTPS →https://wireguard.c2et.net/
- Tipo:
-
Service para WireGuard (UDP)
- Tipo:
LoadBalancer(MetalLB) - Protocolo:
UDP - Puerto del contenedor:
51819 - Puerto externo: asignado por MetalLB en la IP del balanceador.
- Tipo:
Esquema de acceso
- Tráfico TCP (Panel Web): entra por el
IngressController (nginx), con TLS gestionado porcert-manager. - Tráfico UDP (VPN WireGuard): entra directamente a través de la IP asignada por MetalLB, sin pasar por
Ingress.
🚀 Reenvío de tráfico y NAT
WireGuard entrega a los clientes direcciones como 192.168.254.x, pero por defecto no tendrían acceso a otras redes como:
- Red de administración:
192.168.0.0/24 - Red de pods (Flannel):
10.42.0.0/16 - Red Multus (ejemplo):
192.168.201.0/24
Para ello, se despliega un DaemonSet que aplica reglas iptables directamente en el host físico:
iptables -t nat -A POSTROUTING -s 192.168.254.0/24 -d 192.168.0.0/24 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 192.168.254.0/24 -d 10.42.0.0/16 -j MASQUERADE
Con esto, los paquetes generados por los clientes WireGuard se traducen a la IP del host, y así acceden a otras redes internas.
🛠️ Acceso a redes aisladas con Multus
Si se desea permitir acceso a una red definida por un NetworkAttachmentDefinition (NAD) con Multus, como por ejemplo:
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: br-servicios
namespace: default
spec:
config: '{
"cniVersion": "0.3.1",
"type": "bridge",
"bridge": "br-servicios",
"ipam": {
"type": "host-local",
"subnet": "192.168.201.0/24",
"rangeStart": "192.168.201.100",
"rangeEnd": "192.168.201.199"
}
}'
Entonces basta con añadir una regla más en el DaemonSet:
iptables -t nat -A POSTROUTING -s 192.168.254.0/24 -d 192.168.201.0/24 -j MASQUERADE
No es necesario conectar el contenedor de wg-easy directamente a esta red: el tráfico se enruta y NATea desde el host físico.
🔄 Reaplicar cambios
Siempre que modifiques el DaemonSet o el Deployment, puedes aplicar los cambios con:
kubectl apply -k .
✨ Resultado
Una vez desplegado y con el reenvío del router correctamente configurado, puedes acceder a la interfaz web en:
https://wireguard.c2et.net
Y configurar tus clientes WireGuard escaneando el QR generado, o descargando el archivo de configuración.
Los clientes podrán acceder a:
192.168.1.0/24(red local)10.42.0.0/16(pods del clúster)192.168.201.0/24(red multus, si se añade la regla correspondiente)