INFRAESTRUCTURA & SEGURIDAD // HOMELAB DEFENSE

Topología WireGuard & Aislamiento de Homelab

Diseño de arquitectura de red privada en VPS: cómo orquesto decenas de contenedores, sincronización de datos y paneles de gestión sin abrir un solo puerto administrativo al Internet público.

Zero Exposed Ports WireGuard Noise IK Kernel Overlay (wg0) CrowdSec IPS ~14 min lectura
Davito @davito_03 // David
Octubre 2026 Nivel: Intermedio - Avanzado Lab: VPS Netfilter Hardening
Tabla de Contenidos // Índice de Arquitectura

01. La Paradoja del Homelab en VPS

Tener un Servidor Privado Virtual (VPS) operando de forma ininterrumpida es la piedra angular de cualquier infraestructura de self-hosting moderna: ancho de banda simétrico no estrangulado por ISPs domésticos, disponibilidad 24/7 y una dirección IP fija o gestionada. Sin embargo, en el mismo instante en que se despliega el primer contenedor de monitoreo, un gestor de descargas o una base de datos, surge una disyuntiva de seguridad de primer orden:

La Trampa de la Exposición Oportunista
¿Cómo interactuar con decenas de interfaces web administrativas desde una estación de trabajo, portátil o teléfono móvil sin exponer puertos de gestión a escaneos masivos de Shodan, Censys y botnets automatizadas?

La solución intuitiva (y peligrosa) adoptada por administradores con poca experiencia consiste en publicar puertos arbitrarios en el firewall: 3001 para Uptime Kuma, 8091 para Beszel, 8384 para Syncthing, 9000 para Portainer o 8080 para microservicios internos. Esta aproximación convierte al servidor en un blanco trivial:

  • Indexación y Catalogación en minutos: Motores de búsqueda de infraestructura (Shodan, Censys, Project Sonar) barren el espacio IPv4 completo diariamente. Sus spiders catalogan títulos HTML, versiones exactas de bibliotecas HTTP y certificados TLS autofirmados.
  • Superficie de Ataque Polimórfica: Cada contenedor web incorpora su propio runtime (Node.js/Express, Python/FastAPI, Go HTTP, Rust/Actix). Un desbordamiento de búfer, inyección SQL o escape de contenedor en cualquiera de ellos compromete el servidor anfitrión.
  • Saturación de Logs por Ataques de Diccionario: Bots de fuerza bruta prueban credenciales por defecto (admin/admin, root/root) sin descanso, degradando el ancho de banda y llenando gigabytes de logs con ruido inútil.

Para erradicar este vector sin sacrificar accesibilidad, la arquitectura de mi infraestructura se rige por un dogma irrompible: Zero Exposed Management Ports. Todo el tráfico de control se encapsula en una red de malla virtual (overlay mesh) gobernada por el protocolo criptográfico WireGuard.

02. WireGuard & Handshake Criptográfico Noise IK

A diferencia de suites heredadas como OpenVPN o IPsec (que arrastran más de 100.000 líneas de código en espacio de usuario, complejas jerarquías PKI con certificados X.509 y lentas transiciones de contexto), WireGuard fue concebido desde sus cimientos para operar en el espacio del kernel de Linux (wireguard.ko), ocupando menos de 4.000 líneas de código auditables.

El motor de WireGuard implementa el marco criptográfico Noise Protocol Framework, específicamente el patrón Noise_IKpsk2:

Primitiva Algoritmo Empleado Función en el Túnel
Intercambio de Claves Curve25519 (ECDH) Diffie-Hellman elíptico de 256 bits para derivar secretos de sesión en 1 solo Round-Trip (1-RTT).
Cifrado Simétrico ChaCha20 Cifrado de flujo de alta velocidad insensible a ataques de temporización por cache-timing.
Autenticación (MAC) Poly1305 Código de autenticación de mensajes de un solo uso para verificar integridad estricta.
Función Hash & KDF BLAKE2s Derivación de claves de transporte y cálculo de tokens de mitigación anti-DoS.
Protección Anti-Replay Sliding Window (64-bit) Contador de paquetes con ventana deslizante para neutralizar ataques de repetición.

El Principio de "Silencio de Red" (Stealth Protocol)

Uno de los rasgos más formidables de WireGuard es su comportamiento ante sondas externas. Cuando un escáner como Nmap envía paquetes UDP arbitrarios al puerto del VPS:

  1. El kernel calcula la firma criptográfica MAC1 utilizando la clave pública registrada en la interfaz wg0.
  2. Si el paquete carece de una firma válida o la clave del peer no está en el listado de AllowedIPs, el kernel simplemente destruye el paquete en silencio.
  3. No se emite ninguna respuesta ICMP Port Unreachable ni paquetes TCP RST.
Invisible para Shodan
Desde la perspectiva de cualquier escáner de puertos en Internet, el puerto UDP de WireGuard se comporta exactamente igual que una dirección IP que no existe o un host apagado. No hay fugas de información ni banners legibles.

03. Topología Visual Hub-and-Spoke & Flujo de Paquetes

La arquitectura de red utiliza un esquema Hub-and-Spoke segmentado. El nodo VPS actúa como concentrador central (Hub), mientras que las estaciones de trabajo, dispositivos móviles y pantallas táctiles operan como nodos satélite (Spokes).

Nivel 1: Nodos Satélite Autenticados (Spokes)
PC Principal
10.66.66.2/32
Estación de desarrollo Linux / Windows. Acceso SSH y dashboards.
Curve25519 Peer
Móvil Personal
10.66.66.3/32
Teléfono inteligente con WireGuard Always-On / On-Demand.
Curve25519 Peer
Portátil Remoto
10.66.66.4/32
Dispositivo móvil para emergencias de administración fuera de LAN.
Curve25519 Peer
Tablet MT6580
10.66.66.5/32
Kiosk Dashboard recuperado con Android 8.1 y ZRAM tuning.
WireGuard Go
Túnel Cifrado UDP (Noise IK Handshake · 1-RTT Criptográfico)
Nivel 2: Gateway Central (Hub wg0 · Linux Kernel)
Interfaz wg0 (10.66.66.1/24) · Cryptokey Routing Table
Filtro Netfilter: Enrutamiento directo sin NAT innecesario entre clientes y microservicios locales
Las tablas del kernel asocian cada IP de origen 10.66.66.X estrictamente a su clave pública registrada. Si hay suplantación (spoofing), el paquete se descarta de raíz.
Aislamiento en Loopback y Sockets Exclusivos de Interfaz
Nivel 3: Contenedores & Servicios Blindados (Zero-Exposure)
Beszel Hub
127.0.0.1:8091
Telemetría de CPU, RAM, disco y temperaturas.
Uptime Kuma
127.0.0.1:3001
Monitoreo interno de salud de microservicios.
Syncthing BEP
10.66.66.1:22000
Replicación directa TLS 1.3 sin discovery relays.
SearXNG Proxy
10.66.66.1:8444
Metabuscador privado con TLS interno.

04. Docker & la Trampa del 0.0.0.0 (Netfilter / iptables bypass)

Este es uno de los fallos de configuración más extendidos en la comunidad de self-hosting. Muchos usuarios configuran reglas en ufw asumiendo que su servidor está protegido con una política por defecto de denegación:

LA FALSA SENSACIÓN DE SEGURIDAD (UFW)
# El usuario ejecuta:
$ sudo ufw default deny incoming
$ sudo ufw allow 22/tcp
$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp
$ sudo ufw enable

# ...pero luego en su docker-compose.yml escribe:
ports:
  - "3001:3001"  # <--- ¡BRECHA CRÍTICA! Bindea en 0.0.0.0
¿Por qué UFW no detiene a Docker?
Por defecto, el daemon de Docker manipula directamente las tablas PREROUTING y DOCKER de iptables. Las reglas de Docker se evalúan antes de que el paquete alcance la cadena INPUT gestionada por UFW. Como consecuencia, el puerto 3001 queda expuesto en la IP pública del VPS, anulando por completo la directiva deny incoming de UFW.

La Solución: Hardened Binding Explícito

Para impedir que Docker inyecte reglas públicas en PREROUTING, forzamos siempre la dirección IP de enlace (binding IP) en cada servicio:

  • Binding a Loopback (127.0.0.1): El puerto solo es alcanzable por procesos del host (como Nginx en modo reverse proxy local).
  • Binding a WireGuard (10.66.66.1): El puerto únicamente procesa paquetes que ya han sido autenticados y desencriptados por la interfaz virtual del kernel.
docker-compose.yml // Hardened Port Mapping
# Configuración segura: ningun servicio escucha en 0.0.0.0
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    restart: unless-stopped
    ports:
      # Solo accesible mediante el proxy local Nginx
      - "127.0.0.1:3001:3001"
    volumes:
      - ./kuma-data:/app/data

  searxng-proxy:
    image: caddy:latest
    restart: unless-stopped
    ports:
      # Solo accesible para clientes autenticados en la VPN wg0
      - "10.66.66.1:8444:8444"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile

05. El Doble Nginx: Pasarelas Públicas vs. Pasarelas VPN

En este modelo de defensa en profundidad, el servidor web Nginx opera como un enrutador inteligente segmentado en dos zonas estrictamente estancas:

Zona A: El Reino Público (Edge & CDN Proxificado)

Servicios orientados al exterior como davito.es, davito.dev o status.davito.dev. Cuentan con:

  • Proxy DNS inverso con mitigación DDoS y cifrado Cloudflare Full (Strict).
  • Cabeceras HSTS con max-age=63072000; includeSubDomains; preload.
  • Content Security Policy (CSP) que bloquea la inyección de recursos externos no autorizados.
  • Compresión Brotli y Gzip para maximizar tiempos de respuesta.

Zona B: El Reino Privado (VPN Server Blocks en 10.66.66.1)

Herramientas como Stirling PDF, el panel de telemetría de hardware, IT-Tools o interfaces de administración. No poseen subdominio público en Cloudflare ni registro DNS público alguno. Sus bloques de servidor escuchan de manera exclusiva en la IP del túnel:

/etc/nginx/conf.d/vpn-services.conf
# Servidor Nginx accesible ÚNICAMENTE dentro de la VPN
server {
    # Escucha estricta en el socket de la interfaz virtual wg0
    listen 10.66.66.1:8181 ssl;
    server_name 10.66.66.1;

    ssl_certificate     /etc/ssl/internal/fullchain.pem;
    ssl_certificate_key /etc/ssl/internal/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    location / {
        # Proxy pass al contenedor local en loopback
        proxy_pass http://127.0.0.1:8181;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;

        # Soporte para WebSockets (dashboards interactivos)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Si un paquete TCP intenta conectar al puerto 8181 desde la IP pública del servidor, el socket del kernel responde con un RST o es descartado de inmediato sin permitir que Nginx procese ni un solo byte de la petición HTTP.

06. Mitigación Activa con CrowdSec IPS & nftables a Wirespeed

Incluso con los puertos de gestión totalmente blindados, los puertos web públicos (80 y 443) deben permanecer abiertos para servir las aplicaciones de producción. Un firewall estático es insuficiente frente a escaneos persistentes de vulnerabilidades (LFI, SQLi, rastreo de archivos .env o wp-login.php).

Para resolver esto, opera como centinela un agente de CrowdSec como Sistema de Prevención de Intrusiones (IPS):

  • Ingesta de Telemetría Multi-Fuente: CrowdSec parsea en streaming los registros de /var/log/nginx/access.log, los logs de autenticación de SSH (/var/log/auth.log) y las salidas de contenedores Docker.
  • Escenarios de Detección Heurística: Reglas como crowdsecurity/http-crawl-non_statics, crowdsecurity/http-sensitive-files y crowdsecurity/ssh-bf detectan ataques distribuidos en milisegundos.
  • Remediación mediante nftables bouncer: Cuando se emite un baneo, el bouncer inserta la IP atacante en un conjunto dinámico del kernel (nft set).
Rendimiento a Wirespeed
Al operar directamente dentro de las estructuras de conjuntos (sets) de nftables a nivel de kernel, los paquetes procedentes de IPs maliciosas son descartados (DROP) con una complejidad algorítmica de O(1) antes de consumir ciclos de CPU en el espacio de usuario.

07. Caso Práctico: Obsidian Vault & Syncthing BEP TLS 1.3

El banco de pruebas más crítico de esta topología es mi sistema de gestión de conocimiento personal y documentación técnica (Bóveda de Obsidian: La Cúpula). Este entorno alberga miles de archivos Markdown, esquemas de bases de datos, diagramas y notas de investigación sincronizados en tiempo real entre mi estación de trabajo, mi teléfono móvil y el VPS.

El protocolo subyacente para la sincronización es el Block Exchange Protocol (BEP) de Syncthing, configurado bajo directivas de máximo aislamiento:

Syncthing Peer Config // Direct WireGuard Peering
<!-- Ajustes estrictos de sincronización privada en Syncthing -->
<options>
    <globalAnnounceEnabled>false</globalAnnounceEnabled>
    <localAnnounceEnabled>false</localAnnounceEnabled>
    <relaysEnabled>false</relaysEnabled>
    <natEnabled>false</natEnabled>
</options>

<device id="VPS-NODE-ID" name="VPS-Core">
    <!-- Dirección estática forzada dentro del túnel wg0 -->
    <address>tcp://10.66.66.1:22000</address>
</device>

Ventajas directas de este diseño:

  1. Cero filtraciones a servidores de descubrimiento: Al desactivar Global Discovery y Relaying, el identificador único del dispositivo nunca se anuncia en servidores relay públicos de terceros en Internet.
  2. Transferencias en Bloques Cifrados: BEP divide cada archivo en bloques de 128 KiB a 16 MiB calculando hashes SHA-256 de integridad. La transmisión viaja doblemente encapsulada (TLS 1.3 nativo de Syncthing dentro del túnel ChaCha20-Poly1305 de WireGuard).
  3. Sincronización Inmediata en Movilidad: Al editar una nota en el móvil mediante la app de Obsidian, el túnel WireGuard transmite los deltas al VPS en cuestión de 50 milisegundos con confirmación instantánea.

08. MTU Clamping (1420 Bytes) & Keepalive Tuning

Uno de los problemas más elusivos en el despliegue de VPNs móviles es la degradación de rendimiento provocada por la fragmentación de paquetes TCP/IP. Cuando un paquete supera la Unidad Máxima de Transferencia (MTU) de la red física subyacente, los routers intermedios se ven obligados a dividir el paquete en fragmentos, multiplicando la latencia o descartando paquetes si el bit Don't Fragment (DF) está activado.

Cálculo del Overhead de Encapsulamiento

En una conexión Ethernet estándar, el MTU base es de 1500 bytes. WireGuard añade las siguientes cabeceras a cada paquete encapsulado:

  • Cabecera IPv4: 20 bytes (o 40 bytes en IPv6).
  • Cabecera UDP: 8 bytes.
  • Cabecera WireGuard: 32 bytes (Tipo de mensaje: 4B, Índice de receptor: 4B, Contador anti-replay: 8B, Autenticación Poly1305 MAC: 16B).
  • Total de Overhead: 60 bytes en IPv4 / 80 bytes en IPv6.
Fórmula MTU WireGuard
MTU_Túnel = MTU_Físico (1500) - Overhead_IPv6 (80) = 1420 bytes

Al fijar MTU = 1420 en la configuración de la interfaz virtual, se garantiza compatibilidad total con redes móviles 4G/5G (que habitualmente manejan MTUs de 1420 a 1440 por encapsulamientos GTP) y conexiones de fibra con PPPoE (MTU 1492), evitando fragmentación y retardos de reensamblado en el kernel.

Persistencia de Estado en CGNAT: PersistentKeepalive

Las operadoras de telefonía móvil sitúan a los smartphones tras cortafuegos CGNAT con tiempos de expiración de estado NAT sumamente agresivos (en ocasiones eliminando asociaciones UDP tras 30 segundos de inactividad). Para solventar esto y mantener el túnel siempre receptivo a eventos Push entrantes sin agotar la batería:

wg0-client.conf // Mobile Keepalive Optimization
[Interface]
PrivateKey = a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v=
Address = 10.66.66.3/32
DNS = 10.66.66.1
MTU = 1420

[Peer]
PublicKey = z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3h2g1f0e9d=
Endpoint = vpn.tu-dominio.com:51820
AllowedIPs = 10.66.66.0/24
# Envía un pulso mínimo cifrado cada 25 segundos para conservar el mapeo en CGNAT
PersistentKeepalive = 25

09. Protocolo de Auditoría: Verificación en Terminal

Para garantizar que ninguna actualización inadvertida de contenedores o reinicio del sistema haya desalineado los sockets, ejecuto un protocolo de validación rutinario mediante comandos nativos:

audit-network-ports.sh // Detección de Fugas en Sockets
# 1. Comprobar todos los sockets TCP/UDP escuchando en interfaces públicas
# Salida esperada: ÚNICAMENTE SSH (puerto administrativo), HTTP (80) y HTTPS (443)
$ sudo ss -tulpn | grep LISTEN | grep -v '127.0.0.1\|10.66.66.1\|::1'

# 2. Inspeccionar el estado de la malla y últimos handshakes de peers
$ sudo wg show wg0
# peer: PC-Principal
#   endpoint: 198.51.100.42:58210
#   latest handshake: 14 seconds ago
#   transfer: 2.14 GiB received, 8.42 GiB sent

# 3. Comprobar decisiones activas de baneo en el motor CrowdSec
$ sudo cscli decisions list
# +------+----------+-------------------+----------------------+--------+
# |  ID  | Source   | Scope:Value       | Reason               | Action |
# +------+----------+-------------------+----------------------+--------+
# | 1402 | crowdsec | Ip:203.0.113.88   | http-sensitive-files | ban    |
# +------+----------+-------------------+----------------------+--------+

10. Conclusiones y Reglas de Oro Zero-Trust

La seguridad integral en un entorno de homelab no proviene de una herramienta mágica aislada, sino de la defensa en profundidad (Defense in Depth) implementada a través de capas complementarias:

  1. Principio de Mínima Exposición: Si un servicio no necesita ser consumido por el público general, no tiene justificación técnica para escuchar en 0.0.0.0.
  2. Cripto-Enrutamiento Determinista: WireGuard resuelve enrutamiento y autenticación de un plumazo mediante pares de claves estáticas, eliminando la complejidad y vulnerabilidad de las contraseñas débiles.
  3. Desacoplamiento Estricto en Docker: Forzar siempre 127.0.0.1:PUERTO:PUERTO o 10.66.66.1:PUERTO:PUERTO para neutralizar el bypass automático de iptables.
  4. Mitigación Activa en el Kernel: Con CrowdSec y nftables, los atacantes oportunistas son neutralizados a nivel de tarjeta de red sin consumir recursos de CPU en el servidor.
Resultado Operativo
Un homelab con más de 15 microservicios activos, sincronización instantánea de notas y monitorización en tiempo real, operando con la tranquilidad absoluta de que el 95% de la infraestructura es matemáticamente invisible para el resto del planeta.
Anterior: Root Tablet MT6580 (eMMC & Forense) Siguiente: Davusic (Tauri 2 & Rust)