Plan2026/07-decision-red.md

1148 lines
48 KiB
Markdown
Raw Normal View History

2026-03-14 19:33:00 +00:00
# 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
```