# Decision: Capa de red y conectividad (punto 4) ## Contexto El PBS actual tiene el puerto 8007 abierto a internet para recibir backups. A 100+ servidores, cada servicio de backup deberia ir por VPN privada. Ademas, ya existe PowerDNS y Traefik en uso. La capa de red organiza como se comunican los servidores entre si y con el exterior de forma segura. Infraestructura fisica: servidores con 2 interfaces de red (IP publica + vRack OVH 25Gbps). Todo el direccionamiento interno sera IPv6 (ULA). 3 subcapas: - 4.1 VPN y acceso seguro entre servidores - 4.2 DNS y service discovery - 4.3 Proxy inverso y balanceo --- ## 4.1 VPN y acceso seguro entre servidores ### El problema - PBS:8007 expuesto a internet para recibir backups de PVE remotos - Trafico de backup viaja por internet sin cifrado adicional (solo TLS de PBS) - vRack de OVH conecta servidores pero sin cifrado ni autenticacion (L2 privada, no segura) - Sin VPN, cada servicio nuevo expuesto requiere abrir puertos en firewall publico - La superficie de ataque crece con cada puerto abierto - Modelo actual de gateways: 1 VM gateway por cliente (NAT + WG), no escala ### Opciones evaluadas | Herramienta | Modelo | Recursos | Complejidad | Estado | |-------------|--------|----------|-------------|--------| | **WireGuard** | Punto a punto, hub-spoke | Kernel module, ~0 overhead | Baja (config manual, Ansible) | **ELEGIDA** | | **Tailscale** | Mesh VPN, SaaS | Agente ligero | Muy baja | Descartada (SaaS, dependencia externa) | | **Headscale** | Tailscale self-hosted | ~200MB RAM | Baja-media | Posible evolucion futura si escala | | **Nebula** | Overlay con certificados | Agente ligero | Media | Descartada (menor comunidad) | | **Netbird** | Mesh con SSO | ~300MB RAM | Media | Descartada (muy joven) | | **BGP/OSPF entre hubs** | Routing dinamico | FRRouting | Alta | Descartado (overengineering a esta escala) | ### DECISION 4.1 (confirmada) #### WireGuard + wgdashboard + IPv6 ULA Todo el direccionamiento WireGuard en IPv6 ULA (fd00::/8). No ruteable a internet, privado por diseño. #### Tres roles de WireGuard **Rol 1: PBS como servidor WG (backups)** Cada PBS fisico es un servidor WireGuard cuyos unicos peers son los PVE que le envian backups. ``` PBS-1 (wg server) PBS-2 (wg server) ├── peer: PVE-A ├── peer: PVE-D ├── peer: PVE-B └── peer: PVE-E └── peer: PVE-C ``` - PVE apunta su PBS storage a [fdab:1234:bkp::1]:8007 (IP WG del PBS) - PBS:8007 deja de escuchar en IP publica - Los peers PVE NO se ven entre ellos (ip forwarding desactivado, AllowedIPs /128 por peer) - Trafico por vRack (25Gbps) cuando estan en el mismo DC **Rol 2: WG Hub (acceso clientes + tecnicos + VMs)** Hub centralizado donde se conectan: - Usuarios remotos (tecnicos, clientes) - Mikrotiks de oficinas de clientes - VMs de clientes que necesitan red privada ``` WG Hub (par HA con keepalived) ├── peer: tecnico David ├── peer: tecnico Juan ├── peer: cliente ACME usuario remoto ├── peer: cliente ACME mikrotik oficina ├── peer: VM-web ACME (en PVE-1) ├── peer: VM-db ACME (en PVE-2) ├── peer: cliente BETA usuario remoto ├── peer: VM-app BETA (en PVE-1) └── ... ``` - ip forwarding ACTIVADO (los peers deben verse entre ellos) - Aislamiento por cliente con nftables: peers de ACME solo ven peers de ACME - VMs en PVEs distintos se ven via WG Hub (cross-PVE transparente) - Panel futuro para que el cliente defina granularidad peer-a-peer - Reemplaza el modelo de 1 gateway VM por cliente: gateways compartidos con reglas granulares **Rol 3: Mesh de gestion entre fisicos (sobre vRack)** WireGuard sobre vRack para cifrar y autenticar el trafico de gestion entre servidores fisicos. ``` Fisico 1 (vRack) ◄── WG tunnel cifrado ──► Fisico 2 (vRack) fdab:1234:mgmt::1 fdab:1234:mgmt::2 ``` - Servicios internos (Grafana, Netbox, Wazuh, Semaphore) escuchan en IP WG mesh - Ansible, exporters, logs viajan por WG mesh - Hub-spoke: todos los fisicos conectan a un hub de gestion (o al par HA) #### Direccionamiento IPv6 ULA ``` fdab:1234:bkp::/48 → Red de backups (PBS ↔ PVE) fdab:1234:bkp:1::/64 PBS-1 y sus PVEs fdab:1234:bkp:2::/64 PBS-2 y sus PVEs fdab:1234:vpn::/48 → Red de clientes/tecnicos (WG Hub) fdab:1234:vpn:acme::/64 Cliente ACME (VMs + usuarios) fdab:1234:vpn:beta::/64 Cliente BETA fdab:1234:mgmt::/48 → Red de gestion (mesh entre fisicos) fdab:1234:mgmt::1 Hub/Fisico 1 fdab:1234:mgmt::2 Fisico 2 ``` #### Arquitectura del WG Hub Debian minimal dedicado exclusivamente a WireGuard: ``` ┌──────────────────────────────────┐ │ Debian minimal │ │ │ │ Kernel: WireGuard module │ │ Host: nftables (aislamiento │ │ por cliente) │ │ Host: /etc/wireguard/*.conf │ │ │ │ Docker: wgdashboard │ │ └─ UI web (gestion de peers) │ │ └─ API REST (→ ICSManager) │ │ └─ Escribe /etc/wireguard/ │ │ └─ Recarga WG (wg syncconf) │ │ │ │ Nada mas. Sin PBS, sin VMs, │ │ sin otros servicios. │ └──────────────────────────────────┘ ``` #### Alta disponibilidad: par HA con keepalived ``` ┌─────────────┐ ┌─────────────┐ │ WG Hub A │ │ WG Hub B │ │ (MASTER) │ │ (BACKUP) │ │ fdab::1 │ │ fdab::2 │ └──────┬──────┘ └──────┬──────┘ │ │ └───── VIP ─────────┘ fdab:1234:vpn::1 (IP flotante) ``` - Misma config WG en ambos (mismas claves, mismos peers) - Keepalived gestiona VIP sobre vRack - Failover en <2 segundos - Los peers no se enteran: su endpoint es la VIP, no cambia - Para endpoint publico (usuarios remotos): VIP publica o DNS con TTL bajo #### Aislamiento entre clientes (nftables en el hub) ```bash # nftables en WG Hub # Cliente ACME: sus peers se ven entre ellos nft add rule inet filter forward \ iifname "wg-hub" ip6 saddr fdab:1234:vpn:acme::/64 \ ip6 daddr fdab:1234:vpn:acme::/64 accept # Cliente BETA: sus peers se ven entre ellos nft add rule inet filter forward \ iifname "wg-hub" ip6 saddr fdab:1234:vpn:beta::/64 \ ip6 daddr fdab:1234:vpn:beta::/64 accept # DEFAULT: nadie ve a nadie de otro cliente nft add rule inet filter forward iifname "wg-hub" drop ``` Cada cliente tiene su /64. Las reglas son por subnet, no por peer individual (mas simple). Panel futuro para granularidad peer-a-peer dentro del /64. #### Cambio de modelo: gateways compartidos | Modelo actual | Modelo nuevo | |---|---| | 1 VM gateway por cliente | 2 VM WG Hub (par HA) compartidos | | 50 clientes = 50 VMs gateway | 50 clientes = 2 VMs hub | | NAT por cliente | Sin NAT (peers directos IPv6) | | Cada GW consume RAM/CPU | Recursos compartidos | | Aislamiento por VM | Aislamiento por nftables | | Sin HA (si el GW cae, el cliente cae) | HA con keepalived (<2 seg failover) | Mitigacion de riesgo: tests automatizados de aislamiento (playbook que verifica que cada cliente solo alcanza sus recursos). #### Exposicion resultante del servidor fisico ``` eth0 (IP publica): - Firewall: solo WG UDP (1 puerto) + SSH bastion - PBS:8007 CERRADO desde internet - PVE:8006 CERRADO desde internet - Todo lo demas: DROP eth1 (vRack 25Gbps): - WG mesh cifrado (gestion + backups) - Servicios internos solo escuchan en IP WG wg-hub (overlay IPv6): - Clientes y tecnicos acceden a paneles por aqui - :8007, :8006, Grafana, etc. solo via WG ``` #### Fase de arranque Fase inicial con solo tecnicos internos para pulir el modelo: 1. Montar par HA de WG Hub (2 VMs Debian minimal) 2. Peers: solo tecnicos del equipo 3. Validar: acceso a paneles, grabacion bastion, aislamiento nftables 4. Cuando funcione estable: abrir a clientes y VMs progresivamente #### Recursos | Componente | CPU | RAM | Disco | |-----------|-----|-----|-------| | WG Hub A (master) | 1 vCPU | 512 MB | 10 GB | | WG Hub B (backup) | 1 vCPU | 512 MB | 10 GB | | wgdashboard (Docker en Hub A) | incluido | ~200 MB | incluido | | WG en cada PBS (backup role) | kernel module | ~0 | ~0 | | WG en cada fisico (mesh role) | kernel module | ~0 | ~0 | #### Escalabilidad futura - Si un par HA no es suficiente (>200 peers): asignar clientes a hubs distintos (Hub par 1: clientes A-M, Hub par 2: clientes N-Z), con tunel WG entre hubs para cross-routing - Si se necesita mesh automatico: evaluar Headscale (compatible con clientes WireGuard existentes) - Si se necesita routing dinamico entre DCs: FRRouting con OSPF sobre tuneles WG entre hubs - Nada de esto es necesario para arrancar --- ## 4.2 DNS y service discovery ### Las 4 problematicas 1. **Autoritativo de clientes**: zonas de dominios de clientes (primario o secundario) 2. **Resolver para clientes**: navegacion de VMs y usuarios WG, con bloqueo configurable 3. **Naming automatico**: toda IP asignada tiene AAAA + PTR operativos desde el momento 0 4. **Diagnostico self-service**: pagina donde el cliente verifica su conectividad antes de abrir ticket ### Opciones evaluadas para resolver | Herramienta | Multi-tenant | Panel cliente | Bloqueo config. | API | Estado | |-------------|-------------|---------------|-----------------|-----|--------| | **Technitium DNS** | Si (por cliente/zona) | Panel web multi-usuario nativo | Si (listas por cliente) | REST completa | **ELEGIDA como resolver** | | **AdGuard Home** | Parcial (por IP) | Solo admin | Si (listas por IP) | REST | Descartada (no multi-tenant nativo) | | **Pi-hole** | No | No | Global | Limitada | Descartada | | **Blocky** | Si (grupos) | No tiene UI | Si (por grupo) | Si | Descartada (sin panel) | | **Unbound** | No | No | No | No | Descartada (solo resuelve) | ### DECISION 4.2 (confirmada) #### Componente 1: PowerDNS Authoritative (ya existente) Se mantiene y amplia. No se reemplaza. | Funcion | Estado | |---------|--------| | Zonas autoritativas de clientes (primario/secundario) | Ya funciona, mantener | | Zona vpn6.com.es (nombres WG peers) | Crear, automatizar | | Zonas PTR IPv6 (reverse DNS) | Crear, automatizar | | API REST para creacion automatica de registros | Activar/usar | Alimentado por: - **wgdashboard**: al crear peer WG → API PowerDNS → AAAA + PTR en vpn6.com.es - **Netbox (netbox-dns plugin)**: VMs sincronizadas desde PVE → registros DNS automaticos - **Manual**: zonas de clientes como hasta ahora, evolucionando dominio a dominio #### Componente 2: Technitium DNS (resolver con bloqueo) **Solo como resolver**. No toca las zonas autoritativas (eso es PowerDNS). Technitium resuelve consultas DNS de: - VMs de clientes - Usuarios WG (tecnicos, clientes remotos) - Mikrotiks de oficinas conectados por WG Funcionalidades clave: - **Bloqueo multi-tenant**: cada cliente (identificado por rango IP WG) tiene su perfil de bloqueo - **Panel web nativo multi-usuario**: cada cliente puede entrar y ver sus queries, ajustar bloqueo - **Categorias de bloqueo**: malware, phishing, ads, adult, custom por cliente - **Dashboard de trafico DNS**: top dominios, queries bloqueadas, estadisticas - **API REST completa**: automatizable desde ICSManager/n8n - **DNSSEC validation**: verifica firmas DNS - **DoH/DoT upstream**: consultas cifradas a upstream (Cloudflare, Google) ``` VM cliente ──────►┌──────────────────────────┐ │ Technitium DNS │ WG usuario ──────►│ (resolver + bloqueo) │───► Upstream DoH │ │ (Cloudflare/Google) Mikrotik ────────►│ Panel por cliente: │ │ - ACME: malware+ads ✅ │ │ - BETA: solo malware ✅ │ │ - GAMMA: todo ✅ │ │ │ │ Zonas internas: │ │ → reenvio a PowerDNS │ │ para *.vpn6.com.es │ │ y zonas de clientes │ └──────────────────────────┘ ``` Technitium reenvia a PowerDNS las consultas de zonas internas: - `*.vpn6.com.es` → PowerDNS (nombres WG) - `*.cliente.com` → PowerDNS (si somos autoritativo) - Todo lo demas → upstream DoH (navegacion) Recursos: 1-2 vCPU, 512MB-1GB RAM, contenedor Docker o VM ligera. #### Componente 3: Naming automatico (toda IP tiene nombre) Esquema de nombres sobre `vpn6.com.es` (dominio propio): ``` Tecnicos internos: laptop.david.vpn6.com.es → fdab:1234:vpn::42 movil.david.vpn6.com.es → fdab:1234:vpn::43 Clientes (subdominio por cliente): oficina.acme.vpn6.com.es → fdab:1234:vpn:acme::1 (mikrotik) portatil.juan.acme.vpn6.com.es → fdab:1234:vpn:acme::10 (usuario) vm-web.acme.vpn6.com.es → fdab:1234:vpn:acme::100 (VM) Infraestructura: pbs-1.mgmt.vpn6.com.es → fdab:1234:mgmt::10 pve-2.mgmt.vpn6.com.es → fdab:1234:mgmt::20 grafana.mgmt.vpn6.com.es → fdab:1234:mgmt::30 Con dominio del cliente (si lo pide): vpn.acme-empresa.com → CNAME a acme.vpn6.com.es ``` Flujos de creacion automatica: ``` wgdashboard crea peer → nombre definido en el formulario → API PowerDNS: crea AAAA + PTR → El peer ya tiene rDNS + forward desde el momento 0 PVE crea VM → netbox-proxmox sincroniza VM a Netbox → netbox-dns genera registro en PowerDNS → La VM ya tiene nombre DNS automatico Ansible despliega servidor nuevo → Playbook incluye task: registrar en PowerDNS via API → Servidor tiene nombre desde el primer boot ``` #### Componente 4: Pagina de diagnostico self-service Panel web donde el cliente entra (SSO Authentik) y ve su estado: ``` ┌─────────────────────────────────────────┐ │ Diagnostico de conectividad │ │ Cliente: ACME │ │ │ │ ✅ Tu IP: fdab:1234:vpn:acme::10 │ │ ✅ WireGuard: conectado (handshake 3s) │ │ ✅ DNS: resolviendo correctamente │ │ ❌ Certificado PBS: caduca en 3 dias │ │ ✅ PBS:8007: accesible (120ms) │ │ ✅ Latencia WG: 12ms │ │ ⚠️ Tunel IPsec oficina: no detectado │ │ │ │ [Copiar informe] [Abrir ticket] │ └─────────────────────────────────────────┘ ``` Implementacion: app web ligera (Flask/FastAPI, un contenedor Docker) que: 1. Detecta IP origen → identifica cliente (via Netbox API o tabla local) 2. Ejecuta checks en tiempo real contra los servicios de ese cliente 3. Genera informe copiable para pegar en ticket 4. Login via SSO Authentik **Prioridad**: mes 3+, desarrollo propio. No es critico para arrancar. #### Integracion PVE → DNS via Netbox ``` PVE API ──netbox-proxmox──► Netbox │ │ VM con IP asignada ▼ netbox-dns plugin │ │ Sincroniza zona en PowerDNS ▼ PowerDNS API │ ▼ AAAA: vm-web.acme.vpn6.com.es PTR: fdab:...::100 → vm-web.acme.vpn6.com.es ``` No hay hookscripts fragiles en PVE. Netbox es el intermediario fiable. Si la VM existe en Netbox con IP, tiene DNS. #### Arquitectura DNS completa ``` ┌───────────────────────────────────────────────────────┐ │ │ │ AUTORITATIVO RESOLVER │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ PowerDNS Auth │ │ Technitium DNS │ │ │ │ (ya existente) │◄─reenvio──│ (nuevo) │ │ │ │ │ zonas │ │ │ │ │ Zonas clientes │ internas │ Resolver + │ │ │ │ vpn6.com.es │ │ bloqueo multi- │ │ │ │ PTR IPv6 │ │ tenant │ │ │ │ │ │ │ │ │ │ ← Netbox API │ │ Panel por │ │ │ │ ← wgdashboard │ │ cliente nativo │ │ │ │ ← Ansible │ │ │ │ │ └──────────────────┘ │ Upstream: DoH │ │ │ └──────────────────┘ │ │ │ │ NAMING AUTOMATICO DIAGNOSTICO │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ Netbox (SSOT) │ │ Panel web ligero│ │ │ │ + netbox-dns │ │ (Flask/FastAPI) │ │ │ │ + netbox-proxmox│ │ SSO Authentik │ │ │ │ → PowerDNS API │ │ Checks por │ │ │ │ │ │ cliente │ │ │ │ wgdashboard │ │ Mes 3+ │ │ │ │ → PowerDNS API │ └──────────────────┘ │ │ └──────────────────┘ │ │ │ └───────────────────────────────────────────────────────┘ ``` #### Recursos DNS | Componente | CPU | RAM | Disco | Estado | |-----------|-----|-----|-------|--------| | PowerDNS Authoritative | existente | existente | existente | Ampliar zonas | | Technitium DNS (resolver) | 1 vCPU | 512 MB - 1 GB | 10 GB | Nuevo | | netbox-dns plugin | 0 (dentro de Netbox) | 0 | 0 | Configurar | | Panel diagnostico | incluido en VM servicios | ~100 MB | incluido | Mes 3+ | #### Prioridades despliegue DNS | Prioridad | Que | Esfuerzo | |-----------|-----|----------| | **Semana 1** | Technitium DNS como resolver basico (sin bloqueo aun) | 2h | | **Semana 1** | Crear zona vpn6.com.es en PowerDNS | 1h | | **Semana 2** | Automatizar AAAA+PTR desde wgdashboard → PowerDNS API | 4h | | **Semana 2** | Configurar Technitium: reenvio zonas internas a PowerDNS | 1h | | **Mes 1** | Configurar perfiles de bloqueo por cliente en Technitium | 2h | | **Mes 1** | netbox-proxmox + netbox-dns: auto-registro VMs | 1 dia | | **Mes 2** | Abrir panel Technitium a clientes (con SSO Authentik) | 2h | | **Mes 3+** | Panel de diagnostico self-service | 3-5 dias desarrollo | --- ## 4.3 Proxy inverso y publicacion de servicios ### El problema - Tendencia a eliminar IPv4: los clientes deben poder publicar servicios sin necesitar IP publica propia - Cada cliente que quiere exponer un servicio web necesita SSL, reverse proxy, y DNS - Hoy se hace con Traefik (ya en uso) pero sin un modelo sistematico - Los clientes deberian poder declarar "quiero publicar este servicio" y que se configure automaticamente ### Opciones evaluadas | Herramienta | Autodiscovery | SSL auto | API | Estado | |-------------|--------------|---------|-----|--------| | **Traefik** | Docker labels, ficheros, API | Let's Encrypt nativo | REST | **ELEGIDA** (ya en uso) | | **Nginx Proxy Manager** | Manual (UI web) | Let's Encrypt | No | Descartada (manual) | | **HAProxy** | Config estatica | Manual o con ACME | Stats | Descartada (sin auto-SSL) | | **Caddy** | Caddyfile, API | Automatico por defecto | REST | Alternativa viable | ### DECISION 4.3 (confirmada) #### Traefik como proxy inverso centralizado Ya se usa. Se amplia como punto de publicacion de servicios para clientes. #### Modelo: cliente declara servicio → Traefik lo expone ``` Cliente ACME quiere publicar: - Portal web: portal.acme.com - API: api.acme.com Flujo: 1. Cliente declara en panel (o ICSManager): "quiero publicar portal.acme.com" 2. DNS: AAAA de portal.acme.com → IPv6 del Traefik 3. Traefik: route portal.acme.com → VM del cliente (por WG IPv6) 4. SSL: Let's Encrypt automatico 5. Cliente solo tiene IPv6 WG, Traefik pone la cara publica ``` #### Arquitectura ``` Internet (IPv4+IPv6) │ ▼ ┌──────────────────────────────────────┐ │ Traefik (proxy inverso) │ │ │ │ Escucha: │ │ - IPv4 publica :443 (legacy) │ │ - IPv6 publica :443 (preferido) │ │ │ │ Routes: │ │ portal.acme.com → [fdab:..acme::100]:8080 │ │ api.acme.com → [fdab:..acme::101]:3000 │ │ grafana.mgmt.vpn6.com.es → [fdab:..mgmt::30]:3000 │ │ │ │ SSL: Let's Encrypt (automatico) │ │ Middleware: rate limiting, headers │ │ │ │ Config: ficheros dinamicos │ │ (generados por Ansible/API) │ └──────────────────────────────────────┘ │ │ WG IPv6 ▼ ┌──────────────┐ ┌──────────────┐ │ VM cliente │ │ VM cliente │ │ portal ACME │ │ API ACME │ │ (solo IPv6 │ │ (solo IPv6 │ │ WG, sin IP │ │ WG, sin IP │ │ publica) │ │ publica) │ └──────────────┘ └──────────────┘ ``` #### Eliminacion progresiva de IPv4 La tendencia es que las VMs de clientes NO tengan IPv4 publica: | Antes | Despues | |-------|---------| | Cada VM con IP publica | VM solo tiene IPv6 WG | | SSL configurado en cada VM | SSL centralizado en Traefik | | Firewall por VM | Firewall centralizado | | El cliente gestiona su SSL | Traefik gestiona Let's Encrypt | Para clientes que aun necesiten IPv4 entrante (legacy): - Traefik escucha en IPv4 publica y reenvia a IPv6 WG del cliente - El cliente no necesita IPv4 propia - Transicion transparente #### Configuracion dinamica Traefik puede leer rutas de: - **Ficheros YAML** (generados por Ansible al dar de alta un servicio) - **Docker labels** (para servicios en Docker) - **API** (para automatizacion) ```yaml # /etc/traefik/dynamic/clientes/acme.yml # Generado automaticamente al dar de alta el servicio http: routers: portal-acme: rule: "Host(`portal.acme.com`)" service: portal-acme tls: certResolver: letsencrypt services: portal-acme: loadBalancer: servers: - url: "http://[fdab:1234:vpn:acme::100]:8080" ``` Flujo automatizado: ``` ICSManager/Panel → API → Ansible playbook → Crea fichero YAML en Traefik → Crea AAAA en PowerDNS → Traefik recarga automaticamente (file watcher) → Servicio publicado con SSL en segundos ``` #### Servicios internos via Traefik Los paneles internos (Grafana, Netbox, Wazuh, etc.) tambien pasan por Traefik pero solo accesibles via WG: ```yaml # Servicios internos - solo escuchan en IPv6 WG http: routers: grafana: rule: "Host(`grafana.mgmt.vpn6.com.es`)" service: grafana tls: certResolver: letsencrypt netbox: rule: "Host(`netbox.mgmt.vpn6.com.es`)" service: netbox tls: certResolver: letsencrypt ``` Accesibles solo desde la red WG de gestion. Desde internet: no alcanzable. #### Recursos | Componente | CPU | RAM | Disco | |-----------|-----|-----|-------| | Traefik | 1 vCPU | 256-512 MB | 5 GB | Puede correr en la VM de servicios (como contenedor Docker) o en los WG Hubs. No necesita VM dedicada. #### Prioridades despliegue proxy | Prioridad | Que | Esfuerzo | |-----------|-----|----------| | **Semana 1** | Traefik para servicios internos (Grafana, Netbox, etc.) | 2h | | **Semana 2** | SSL automatico para servicios internos | 1h | | **Mes 1** | Primer servicio de cliente publicado via Traefik | 2h | | **Mes 2** | Template Ansible para alta automatica de servicios | 4h | | **Mes 3+** | Panel para que clientes declaren servicios | Desarrollo | --- ## 4.4 Salida a internet: NAT CGNAT + política restrictiva ### El problema Las VMs de clientes son IPv6-only (WG). Necesitan salida a internet para: - apt update / Docker pull - APIs externas - Navegación (si aplica) Sin un mecanismo de salida, las VMs están aisladas. Pero dar salida sin control abre riesgos: un cliente puede enviar spam, hacer scraping, o abusar y provocar que la IP compartida se blacklistee. ### DECISION 4.4 (confirmada) #### Dual-stack con IPv4 CGNAT para salida Cada VM de cliente tiene dual-stack: ``` VM de cliente: - IPv6 WG: fdab:1234:vpn:acme::10 → acceso entrante (RDP, SSH, servicios) - IPv4 CGNAT: 100.64.1.10/24 → solo salida NAT (navegar, apt, APIs) Regla de oro: IPv4 = solo SALE, nunca ENTRA (outbound NAT) IPv6 = ENTRA controlado (el cliente/técnico accede vía WG) Web = publicado por Traefik (HTTP/HTTPS al mundo) ``` #### Rango CGNAT (100.64.0.0/10) Se usa el rango CGNAT (RFC 6598) en vez de RFC1918 para evitar colisiones con redes de oficina de clientes: ``` 100.64.0.0/10 total (~4 millones de IPs) Asignación: /24 por cliente 100.64.1.0/24 → Cliente ACME 100.64.2.0/24 → Cliente BETA 100.64.3.0/24 → Cliente GAMMA ... 100.64.254.0/24 → Cliente 254 100.65.x.0/24 → Overflow si hace falta ``` Ventaja sobre 10.x o 192.168.x: un cliente con oficina en 192.168.1.0/24 conectada por WG no tiene conflicto con la IP CGNAT de su VM. #### Política de salida restrictiva (nftables en WG Hub) ```nft table ip filter { chain forward { type filter hook forward priority 0; policy drop; # Tráfico establecido/relacionado ct state established,related accept # --- PERMITIDO PARA TODOS --- ip saddr 100.64.0.0/10 tcp dport { 80, 443 } accept # Web ip saddr 100.64.0.0/10 udp dport { 53, 123 } accept # DNS, NTP ip saddr 100.64.0.0/10 tcp dport 53 accept # DNS TCP # --- SMTP: BLOQUEADO POR DEFECTO --- ip saddr 100.64.0.0/10 tcp dport 25 drop # NUNCA # Puerto 465/587: solo whitelist ip saddr 100.64.0.0/10 tcp dport { 465, 587 } ip daddr @smtp_whitelist accept # --- RESTO: DENEGADO --- ip saddr 100.64.0.0/10 log prefix "CGNAT-BLOCKED: " drop } } table ip nat { chain postrouting { type nat hook postrouting priority 100; # NAT compartido (base) ip saddr 100.64.0.0/10 oifname "eth0" masquerade } } # Whitelist SMTP set smtp_whitelist { type ipv4_addr flags interval elements = { # WildDuck propio (IP interna) 100.64.0.0/10, # Añadir IPs de servicios de correo autorizados según necesidad } } ``` #### Puertos permitidos por defecto | Puerto | Política | Razón | |--------|----------|-------| | 80, 443 | ✅ Permitido | Navegación, APIs, apt, Docker pulls | | 53, 123 | ✅ Permitido | DNS, NTP | | 25 | ✗ Bloqueado siempre | Prevenir spam. Sin excepciones. | | 465, 587 | ✅ Solo whitelist | SMTP autenticado a servidores conocidos | | Todo lo demás | ✗ Bloqueado | El cliente pide, se evalúa, se abre específicamente | Logs de intentos bloqueados → Alloy → Loki. Visibles en Grafana. #### Rate limiting por cliente ```nft # Evitar abuso incluso en puertos permitidos ip saddr 100.64.1.0/24 tcp dport { 80, 443 } limit rate 200/second burst 50 accept ip saddr 100.64.1.0/24 tcp dport { 80, 443 } drop ``` #### Definición de producto: base vs premium ``` SALIDA INTERNET (NAT) - Definición de producto ───────────────────────────────────────────────── INCLUIDO (base): - Salida compartida: HTTP/HTTPS, DNS, NTP - SMTP solo a servidores autorizados (465/587 whitelist) - Puerto 25 bloqueado sin excepciones - IP pública compartida - IP puede cambiar sin preaviso - Sin garantía de reputación de IP - Política restrictiva no negociable PREMIUM (contratación adicional): - IP dedicada fija con PTR personalizable - Si se blacklistea, solo afecta al cliente que la contrató - Apertura de puertos adicionales (previa evaluación) - Reglas firewall personalizadas - Rate limits ajustados ───────────────────────────────────────────────── ``` Implementación del premium: ```nft # Cliente ACME tiene IP dedicada (premium) ip saddr 100.64.1.0/24 oifname "eth0" snat to 1.2.3.101 # Resto de clientes: IP compartida ip saddr 100.64.0.0/10 oifname "eth0" snat to 1.2.3.100 ``` #### Gestión - Reglas nftables gestionadas con Ansible (un fichero por cliente, versionado en Forgejo) - Aperturas de puertos: el cliente solicita → se evalúa → se aplica con playbook → queda en git - Asignación de rangos CGNAT: en Netbox (IPAM), prefijo 100.64.X.0/24 asignado al tenant - Alertas: logs de tráfico bloqueado correlacionados con cliente en Grafana --- ## 4.5 Modelo de VMs: una VM = un servicio (caja negra) ### El problema Si varios servicios con BBDD comparten una VM: - Un técnico trabajando en Zammad puede afectar a Netbox - Diagnosticar "qué consume IO" es complejo cuando hay 5 servicios mezclados - Migrar un servicio a otro servidor requiere separarlo primero - El backup de la VM incluye todo, no puedes restaurar solo un servicio ### DECISION 4.5 (confirmada) **Cada servicio con estado (BBDD) tiene su propia VM.** Es una caja negra: todo lo que necesita está dentro (app + BBDD + Redis). Se backupea entera, se migra entera, se diagnostica de forma aislada. #### Mapa de VMs por servicio | VM | Contenido (todo Docker dentro) | BBDD | Stateful | |----|-------------------------------|------|----------| | **VM Zammad** | Zammad + PostgreSQL + Redis + Elasticsearch | PostgreSQL | Sí, VM dedicada | | **VM Authentik** | Authentik + PostgreSQL + Redis | PostgreSQL | Sí, VM dedicada | | **VM Netbox** | Netbox + PostgreSQL + Redis | PostgreSQL | Sí, VM dedicada | | **VM Outline** | Outline + PostgreSQL + Redis | PostgreSQL | Sí, VM dedicada | | **VM ICSManager** | ICSManager + PostgreSQL (todo Docker) | PostgreSQL | Sí, VM dedicada | | **VM WildDuck** | WildDuck + MongoDB + Redis (todo Docker) | MongoDB | Sí, VM dedicada | | **VM Servicios** | Semaphore, Uptime Kuma, Technitium, Traefik | Ninguna pesada | Stateless/ligeros | | **VM Monitoring** | VictoriaMetrics, Loki, Grafana, Alertmanager | Time-series | Ya dedicada | | **VM Wazuh** | Wazuh Manager + Dashboard (OpenSearch) | OpenSearch | Ya dedicada | #### Ventajas operativas | Aspecto | Servicios mezclados | Caja negra por servicio | |---------|-------------------|----------------------| | Diagnosticar | "¿Qué de los 5 servicios consume IO?" | "La VM Zammad consume IO → es Zammad" | | Migrar | Separar primero, luego mover | Mover la VM entera | | Backup/Restore | Todo o nada | Restauras solo Zammad sin tocar nada | | Un técnico trabaja | Con miedo de romper otros servicios | Libertad total dentro de su VM | | Actualizar | Coordinar 5 servicios | Actualizar uno, si falla, rollback VM | | Escalar | Dar más RAM a la VM afecta a todos | Dar más RAM solo a Zammad | #### WildDuck y MongoDB MongoDB dockerizado dentro de la VM WildDuck. De momento es correo interno del equipo técnico, volumen bajo. Si el correo se abre a más usuarios o el volumen crece significativamente, se revalúa: - MongoDB en VM dedicada (si el tamaño lo justifica) - O migración a servicio gestionado Los tokens SMTP de WildDuck para avisos de PVE son operativamente críticos. El backup de la VM WildDuck los incluye automáticamente. #### Impacto en dimensionamiento La tabla actualizada de VMs: | VM | vCPU | RAM | Disco | |----|------|-----|-------| | Monitoring | 4 | 8-12 GB | 200-500 GB | | Wazuh | 4 | 12 GB | 100 GB | | Zammad | 2-3 | 4-6 GB | 50 GB | | Authentik | 1-2 | 2-3 GB | 20 GB | | Netbox | 1-2 | 2-3 GB | 20 GB | | ICSManager | 1-2 | 2-3 GB | 20 GB | | WildDuck | 1-2 | 2-3 GB | 30-50 GB | | PMG | 1-2 | 2-3 GB | 20 GB | | PDM | 1-2 | 1-2 GB | 20 GB | | Outline | 1 | 1-2 GB | 20 GB | | Servicios (stateless) | 1-2 | 2-3 GB | 20 GB | | Bastion | 1 | 1 GB | 20 GB | | WG Hub A | 1 | 512 MB | 10 GB | | WG Hub B | 1 | 512 MB | 10 GB | | Forgejo | exist. | exist. | exist. | | **Total** | **~24-32 vCPU** | **~44-58 GB** | **~580-910 GB** | --- ## 4.6 Proxmox Datacenter Manager (PDM) ### El problema Con múltiples PVE en distintos DCs (Francia, potencialmente otros), la gestión es por nodo o por cluster. Si un cliente necesita mover una VM de un PVE a otro (por mantenimiento, por rendimiento, o por fallo), hay que hacerlo manualmente conectándose a cada PVE. ### DECISION 4.6 (confirmada) **PDM como panel único de gestión multi-cluster.** Proxmox Datacenter Manager permite: - Vista global de todos los nodos PVE y clusters en un solo panel - Migración de VMs entre clusters/nodos (lo que los clientes necesitan) - Gestión centralizada de recursos, storage, red - Planificación de capacidad a nivel de datacenter ``` ┌─────────────────────┐ │ PDM │ │ (panel único) │ └──────┬──────────────┘ │ ┌────────────┼────────────┐ │ │ │ ┌─────▼─────┐ ┌───▼──────┐ ┌──▼────────┐ │ PVE-1 │ │ PVE-2 │ │ PVE-3 │ │ Francia │ │ Francia │ │ Alemania │ │ 20 VMs │ │ 15 VMs │ │ 10 VMs │ └───────────┘ └──────────┘ └───────────┘ El técnico ve todo desde PDM. Puede migrar VM-web de PVE-1 a PVE-2 desde la misma interfaz. ``` #### Recursos | Componente | CPU | RAM | Disco | |-----------|-----|-----|-------| | PDM (VM dedicada) | 1-2 vCPU | 1-2 GB | 20 GB | VM dedicada (patrón caja negra). Ligero: es solo plano de gestión, no compute. SSO con Authentik. Accesible solo vía WG (red de gestión). --- ## 4.7 Proxmox Mail Gateway (PMG) ### El problema WildDuck gestiona el correo (buzones, IMAP, SMTP, tokens). Pero no tiene filtrado avanzado de spam ni antivirus. Si se abre el correo a más usuarios (más allá del equipo técnico), el volumen de spam y malware entrante será un problema. ### DECISION 4.7 (confirmada) **PMG como gateway de filtrado delante de WildDuck.** ``` Internet (puerto 25) │ ▼ ┌──────────────────────┐ │ PMG │ │ Proxmox Mail Gateway │ │ │ │ - Anti-spam │ │ (SpamAssassin) │ │ - Anti-virus │ │ (ClamAV) │ │ - Greylisting │ │ - SPF/DKIM/DMARC │ │ - Cuarentena con UI │ │ - Reglas por dominio │ │ - Reglas por usuario │ │ │ │ Puerto 25 entrante │ │ → filtra │ │ → reenvía limpio a │ │ WildDuck │ └──────────┬───────────┘ │ (email limpio) ▼ ┌──────────────────────┐ │ WildDuck │ │ (buzones, IMAP, │ │ tokens SMTP PVE) │ └──────────────────────┘ ``` #### Funcionalidades clave | Función | Qué hace | |---------|----------| | **Anti-spam** | SpamAssassin + reglas custom, scoring por mensaje | | **Anti-virus** | ClamAV, escanea adjuntos entrantes y salientes | | **Greylisting** | Retrasa primer intento desde IP desconocida (mata el 80% del spam) | | **SPF/DKIM/DMARC** | Verifica autenticidad del remitente | | **Cuarentena** | UI web donde el usuario ve mensajes filtrados y puede liberar falsos positivos | | **Reglas por dominio** | Políticas diferentes por dominio gestionado | | **Reporting** | Estadísticas: spam filtrado, virus detectados, volumen | #### Email saliente PMG también filtra salida: si un usuario comprometido envía spam, PMG lo detecta y bloquea antes de que salga y blacklistee la IP. ``` WildDuck (usuario envía) │ ▼ ┌──────────────────────┐ │ PMG │ │ - Verifica que no │ │ sea spam saliente │ │ - Firma DKIM │ │ - Rate limiting │ └──────────┬───────────┘ │ ▼ Internet (destino) ``` #### Integración con el stack | Integración | Qué aporta | |-------------|-----------| | PMG → Loki (logs) | Logs de spam filtrado, virus detectados → visibles en Grafana | | PMG → Alertmanager | Alerta si el volumen de spam sube anómalamente | | PMG → Wazuh | Eventos de seguridad (virus detectados) correlacionados | | PMG SSO Authentik | Panel de administración con cuenta unificada | | PMG cuarentena → usuario | El usuario accede vía web a su cuarentena | #### Recursos | Componente | CPU | RAM | Disco | |-----------|-----|-----|-------| | PMG (VM dedicada) | 1-2 vCPU | 2-3 GB | 20 GB | ClamAV consume la mayor parte de la RAM (~1-1.5 GB para las firmas). SpamAssassin ligero. VM dedicada siguiendo patrón caja negra. #### Prioridades | Paso | Qué | Cuándo | |------|-----|--------| | Desplegar PMG con reglas básicas | Cuando WildDuck se abra a más usuarios | | Configurar DKIM signing | Junto con el despliegue | | Integrar logs PMG → Loki | Semana 1 post-despliegue | | Cuarentena accesible a usuarios | Junto con el despliegue | No es urgente mientras WildDuck solo sirva al equipo técnico. **Se despliega cuando se amplíe el correo.** --- ## Resumen Capa 4 completa (DECISIONES FINALES) ``` ┌─────────────────────────────────────────────────────┐ │ CAPA 4: RED Y CONECTIVIDAD │ ├─────────────────────────────────────────────────────┤ │ │ │ 4.1 VPN (confirmada) │ │ ┌───────────────────────────────────┐ │ │ │ WireGuard + wgdashboard + IPv6 │ │ │ │ │ │ │ │ Rol 1: PBS = WG server (backups) │ │ │ │ - Peers: solo PVEs con storage │ │ │ │ - Sin forwarding (aislados) │ │ │ │ │ │ │ │ Rol 2: WG Hub HA (clientes) │ │ │ │ - Par HA keepalived │ │ │ │ - Tecnicos + clientes + VMs │ │ │ │ - nftables aislamiento/cliente │ │ │ │ - wgdashboard (UI + API) │ │ │ │ - Reemplaza 1-GW-por-cliente │ │ │ │ │ │ │ │ Rol 3: Mesh gestion (vRack) │ │ │ │ - WG cifrado sobre vRack 25Gbps │ │ │ │ - Servicios internos solo aqui │ │ │ │ │ │ │ │ Direccionamiento: IPv6 ULA │ │ │ │ fdab:1234:bkp::/48 (backups) │ │ │ │ fdab:1234:vpn::/48 (clientes) │ │ │ │ fdab:1234:mgmt::/48 (gestion) │ │ │ └───────────────────────────────────┘ │ │ │ │ 4.2 DNS (confirmada) │ │ ┌───────────────────────────────────┐ │ │ │ PowerDNS Auth (ya existente) │ │ │ │ - Zonas clientes (pri/sec) │ │ │ │ - vpn6.com.es (nombres WG) │ │ │ │ - PTR IPv6 │ │ │ │ - ← Netbox + wgdashboard API │ │ │ │ │ │ │ │ Technitium DNS (resolver nuevo) │ │ │ │ - Resolver + bloqueo multi-tenant│ │ │ │ - Panel web por cliente │ │ │ │ - Perfiles: malware/ads/adult │ │ │ │ - Reenvio zonas internas → PDNS │ │ │ │ │ │ │ │ Naming automatico: │ │ │ │ - wgdashboard → PDNS API │ │ │ │ - Netbox → netbox-dns → PDNS │ │ │ │ - PVE → Netbox → DNS automatico │ │ │ │ │ │ │ │ Panel diagnostico (mes 3+) │ │ │ │ - Self-service checks cliente │ │ │ └───────────────────────────────────┘ │ │ │ │ 4.3 PROXY INVERSO (confirmada) │ │ ┌───────────────────────────────────┐ │ │ │ Traefik (ya en uso, ampliar) │ │ │ │ - Proxy inverso centralizado │ │ │ │ - SSL Let's Encrypt automatico │ │ │ │ - Clientes declaran servicio → │ │ │ │ Traefik lo publica │ │ │ │ - VMs sin IPv4 publica │ │ │ │ (Traefik pone la cara publica) │ │ │ │ - Config YAML dinamica via │ │ │ │ Ansible/API │ │ │ │ - Servicios internos solo via WG │ │ │ └───────────────────────────────────┘ │ │ │ │ 4.4 SALIDA INTERNET NAT (confirmada) │ │ ┌───────────────────────────────────┐ │ │ │ Dual-stack por VM: │ │ │ │ - IPv4 CGNAT (100.64.x.x/24) │ │ │ │ solo salida NAT, nunca entra │ │ │ │ - IPv6 WG: acceso entrante │ │ │ │ │ │ │ │ Política restrictiva: │ │ │ │ - 80,443,53,123: permitido │ │ │ │ - Puerto 25: BLOQUEADO siempre │ │ │ │ - 465,587: solo whitelist SMTP │ │ │ │ - Resto: bloqueado por defecto │ │ │ │ │ │ │ │ Producto base: IP compartida │ │ │ │ (puede cambiar sin preaviso) │ │ │ │ Premium: IP dedicada fija │ │ │ └───────────────────────────────────┘ │ │ │ │ 4.5 MODELO VMs (confirmada) │ │ ┌───────────────────────────────────┐ │ │ │ Una VM = un servicio (caja negra)│ │ │ │ App + BBDD + Redis todo dentro │ │ │ │ - Zammad, Authentik, Netbox, │ │ │ │ ICSManager, WildDuck, Outline │ │ │ │ cada uno en su VM dedicada │ │ │ │ - Backup/migración: VM entera │ │ │ │ - Diagnóstico aislado │ │ │ │ - Total: ~24-32 vCPU, 44-58 GB │ │ │ └───────────────────────────────────┘ │ │ │ │ 4.6 PDM (confirmada) │ │ ┌───────────────────────────────────┐ │ │ │ Proxmox Datacenter Manager │ │ │ │ - Gestión multi-cluster PVE │ │ │ │ - Migración VMs entre PVEs │ │ │ │ - Vista global de recursos │ │ │ │ - SSO Authentik, solo via WG │ │ │ │ - VM dedicada: 1-2 vCPU, 1-2GB │ │ │ └───────────────────────────────────┘ │ │ │ │ 4.7 PMG (confirmada, desplegar al ampliar correo) │ │ ┌───────────────────────────────────┐ │ │ │ Proxmox Mail Gateway │ │ │ │ - Anti-spam (SpamAssassin) │ │ │ │ - Anti-virus (ClamAV) │ │ │ │ - SPF/DKIM/DMARC │ │ │ │ - Greylisting │ │ │ │ - Cuarentena con UI web │ │ │ │ - Filtro saliente (anti-spam) │ │ │ │ - Delante de WildDuck │ │ │ │ - VM dedicada: 1-2 vCPU, 2-3GB │ │ │ └───────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────┘ Recursos Capa 4: - WG Hub A: 1 vCPU, 512 MB RAM, 10 GB disco - WG Hub B: 1 vCPU, 512 MB RAM, 10 GB disco - Technitium DNS: 1 vCPU, 512 MB - 1 GB RAM (en VM Servicios) - Traefik: contenedor Docker, 256-512 MB RAM (en VM Servicios) - PDM: 1-2 vCPU, 1-2 GB RAM, 20 GB disco - PMG: 1-2 vCPU, 2-3 GB RAM, 20 GB disco - PowerDNS: ya existente - WG en servidores: kernel module, 0 extra - NAT CGNAT: en WG Hub (nftables, 0 extra) Dimensionamiento revisado (todas las VMs de gestión): ~24-32 vCPU, ~44-58 GB RAM, ~580-910 GB disco Recomendación servidor: 128 GB RAM, 8-12c/16-24t, 2×2TB NVMe ```