Nueva estructura de VPN para clientes (replace de apolo)
Contexto
Pendiente capturado por Sergio el 2026-05-08. Reemplazar la VPN actual (alojada en apolo — servidor público en 64.150.190.34, root SSH puerto 58695) por una nueva infraestructura WireGuard con segmentación por cliente, portal de visualización, scripts de tooling y manuales de uso.
apolo actualmente sirve como concentrador VPN para acceso de Electrosystems a los servers de sus clientes — se ve en ~/.ssh/config que muchos hosts de clientes llevan IPs en 10.11.0.0/16 (la subred de la VPN). Eso significa que muchos accesos críticos pasan por apolo hoy: arias-juarez, gi-corp, gi-siptrunks, adfsa-voip, adfsa-vpn, indelek-*, capsonic-elpaso, monitoreo-hercules, neptuno, etc. Tirar apolo sin migración planeada = perder acceso a producción de todos los clientes.
Distinto de wireguard-jrz-elp: ese es site-to-site Juárez ↔ El Paso (oficinas internas). Este es la VPN de acceso a servers de clientes (operadores de Electrosystems → red del cliente). Different threat model, different peers, distinta segmentación.
Componentes a construir
1. Servidor / concentrador WireGuard
- 📅 sin-fecha — Definir host: VM nueva en
poseidon, o servicio enreverse-proxy, o un VPS dedicado fuera de Electrosystems (mejor disponibilidad si el datacenter cae). - 📅 sin-fecha — Definir si es un solo concentrador o múltiples por región (mejor latencia por geografía).
- 📅 sin-fecha — Definir el patrón: hub-and-spoke (operadores y servers se conectan al hub; el hub rutea), mesh (cada operador con cada cliente), o per-client subnet (un túnel por cliente, segmentación dura).
2. Segmentación por cliente
- 📅 sin-fecha — Política: un operador no debería ver hosts de un cliente con el que no trabaja. Hoy con la VPN plana de
apolocualquier operador con acceso ve toda la10.11.0.0/16. - 📅 sin-fecha — Diseño: asignar
/24(o más fino) por cliente. Routing y firewall del concentrador permiten solo a operadores autorizados llegar a la subred del cliente que les corresponde. - 📅 sin-fecha — Mapping de operadores a clientes: tabla de quién accede a qué. Necesita ser actualizable cuando rote personal.
3. Portal para visualizar clientes
- 📅 sin-fecha — Qué muestra: lista de clientes y sus hosts (con IPs en la nueva segmentación), estado de conectividad (last handshake), peers activos.
- 📅 sin-fecha — Quién lo ve: Sergio + admins de Electrosystems. ¿Operadores también? Probable que sí (con vista filtrada a sus clientes asignados).
- 📅 sin-fecha — Stack sugerido: SvelteKit + algo simple para la API (Laravel o Node), datos desde
wg showparseado y/o desde una DB con la asignación de peers. Auth via Google Workspace OAuth (alineado condocs.*ybackups.*). - 📅 sin-fecha — Hosting: detrás de
reverse-proxycon TLS y allow-list LAN/VPN, igual que los demás portales.
4. Scripts de tooling
- 📅 sin-fecha —
wg-create-key.sh— script para crear par de llaves nuevas + entrada en el concentrador + plantilla de config para el peer (operador o server).- Inputs: nombre del peer, cliente al que pertenece (o “operador”), subnet asignada.
- Outputs: archivo de config WG listo para copiar al peer + entrada actualizada en el concentrador.
- 📅 sin-fecha —
wg-install-key-server.sh— script para instalar la llave en un server existente del cliente (SSHea al server, instala wireguard si no está, configura el túnel, hacesystemctl enable wg-quick@wg0).- Inputs: alias SSH del server + el archivo de config generado por el script anterior.
- Validación post-install: ping al concentrador desde el server,
wg showcon handshake, primer paquete que pase.
- 📅 sin-fecha —
wg-revoke-peer.sh— script para revocar (rotación de personal, fuga de llave). Quita la llave del concentrador + opcionalmente del peer (si se puede SSHear). - 📅 sin-fecha —
wg-list-peers.sh— listado plano del estado de peers para troubleshooting rápido (probablemente lo que el portal expone, en CLI).
5. Manuales de uso
- 📅 sin-fecha — Manual del operador: cómo levantar el túnel en su máquina (Linux/Mac/Windows), cómo verificar conectividad, qué hacer si el handshake no pasa.
- 📅 sin-fecha — Manual del admin: cómo agregar un cliente nuevo (subnet, peer del concentrador, server peers, mapping a operadores), cómo rotar llaves, cómo decommissionar un peer.
- 📅 sin-fecha — Runbook de migración desde
apolo: orden de cutover, validación por cliente, plan de rollback si algo no llega.
Migración desde apolo
Riesgo alto: muchos accesos críticos pasan por ahí
Cliente activo con acceso vía apolo (inferido de ~/.ssh/config):
- ADFSA (
adfsa-voip,adfsa-vpn) - Grupo Imperial (
gi-corp,gi-siptrunks) - ARIAS (
arias-juarez) - INDELEK (
indelek-chihuahua,indelek-jrz) - CAPSONIC (
capsonic-elpaso) - Subred
10.11.100.0/24con muchosmonitoreo-*(Hércules, Namiquipa, Villa Ahumada, etc.) - Más routers internos (
router-elpaso,router-parral-izzi, etc.)
Si apolo cae sin reemplazo: pérdida total de acceso operacional a todos esos clientes. Migración debe ser paralela (apolo sigue vivo hasta que el reemplazo esté validado por cliente), no en una ventana.
Plan ordenado
- #073 📅 2026-06-02 · 🤖 alto — Fase 0 — diseño + scaffolding del concentrador WG nuevo. No toca producción. ✅ Diseño APROBADO 2026-05-29 (las 5 decisiones cerradas) →
vpn-clientes/PLAN.md. Instanciawg-cliseparada en la VMwireguard; solo /32 de cada servidor, NO se rutean LANs (LAN de cliente = SSH tunnel, por LANs solapadas); subredes por función + bloque ADFSA/22; source of truth en la VM; segmentación rol-based. Todas las decisiones cerradas. Siguiente: materializar scaffolding (/etc/wireguard/wg-cli/+ 4 scripts) y lab en poseidon. - (2026-05-29) #413 Fase 0a — scaffolding
wg-cli. HECHO envpn-clientes/wg-cli/(seed reviewable del dir que irá a la VM):peers.yaml(roles + ADFSA/22) +peers.schema.md+render-wg-cli.sh+ los 4 scripts (wg-create-key/install/revoke/list) + parser propioparse_peers.py+lab/README.md+.gitignore. Probado: render conf/firewall OK, validación atrapa IP fuera de subred, auto-IP-libre arreglado (bug de heredoc → env vars), guards de prod (install/revoke se niegan a prod sin--i-know-prod), dry-run no muta. Ver bitácora. - #414 📅 2026-06-05 · ⏱ 0.5d — Fase 0b — lab de validación en poseidon. VM(s)/netns: 1 hub + servidores + 2 operadores; correr los tests de segmentación rol-based (operador→pool PASA, no-permitido DROP, servidor↔servidor DROP, revoke en vivo). Verde = se desbloquea #077.
- #077 📅 cond:fase-0b-verde — Fase 1 — piloto ARIAS (confirmado 2026-05-29). Levantar
wg-clien su server en paralelo a apolo, dar acceso a operadores (default amplio), validar conectividad + SSH tunnel a su LAN + monitoreo, soak, retirar ruta apolo. - 📅 sin-fecha — #077b Fase 2 — clientes restantes en orden de menor a mayor criticidad. ADFSA y Grupo Imperial al final (telefonía en producción).
- 📅 sin-fecha — #077c Fase 3 — migración de operadores. Una vez todos los clientes están en el WG nuevo, los operadores cambian su
~/.ssh/configo equivalente para usar el nuevo VPN. - 📅 sin-fecha — #077d Fase 4 — apagar
apolo. Solo cuando ningún operador ni ningún flujo automatizado lo necesite.
Decisiones marco — ✅ CERRADAS 2026-05-29
Detalle en
vpn-clientes/PLAN.md. Contexto clave de Sergio: no nos interesan las LANs de cliente, solo el acceso directo a los servidores; para la LAN se hace SSH tunnel (hay LANs solapadas entre clientes/nosotros). Subredes por función, no por cliente; solo ADFSA con bloque propio.
- (D1) Único o múltiple → ✅ Único.
- (D2) Dónde vive → ✅ VM
wireguard, instanciawg-cliseparada (puerto 51821; reusa endpoint+reachability, sin el MASQUERADE ciego delwg0). - (D3) Subnets → ✅
10.99.0.0/16con subredes por función (operadores/routers/monitoreos/servidores) + ADFSA/22(cientos de tiendas). Cada peer un /32; no se rutean LANs. - (D4) Auth del portal → ✅ Google Workspace OAuth (portal diferido).
- (D5) Source of truth → ✅ En la VM (
/etc/wireguard/wg-cli/, git → NAS para backup); NetBox reserva los prefijos10.99.x.
Segmentación: ✅ rol-based (operadores → pools; ADFSA gateado; servidor↔servidor DROP). El campo allow por peer deja per-operador /32 disponible para casos puntuales. Todas las decisiones cerradas → listo para scaffolding.
Notas técnicas
Hosts existentes ya con WireGuard activo
wireguardalias →192.168.20.5(User electrosystems) — algún WG existente en Electrosystems. Confirmar: ¿es road-warrior, site-to-site, o algo más? Si ya está sirviendo, podría ser candidato a expansion en lugar de standup nuevo.- Aliases
vpn-elpaso(192.168.10.100, port 58690) yvpn-push(192.168.3.50) — ¿OpenVPN/IPSec activos? Confirmar antes de empezar (parte del wireguard-jrz-elp).
Por qué WireGuard
- Performance (kernel-native en Linux moderno).
- Configuración corta y revisable (un archivo
.confde ~10 líneas por peer). - Llaves modernas (Curve25519). No depende de PKI compleja.
- Sin estado persistente del túnel (más fácil de reiniciar sin perder sesiones).
Gotchas conocidos
- Sin NAT-T explícito como IPSec — depende de keepalives para túneles detrás de NAT.
AllowedIPstambién es la política de routing — un error ahí puede mandar tráfico al concentrador o blackhole.- Handshake silencioso si la llave del peer no coincide en el concentrador — debug con
wg show+journalctl -u wg-quick@wg0.
Bitácora
2026-05-29 (tarde, 3) — #413 scaffolding wg-cli construido y probado
- Pidió Sergio: empezar con #413 (scaffolding Fase 0a).
- Hice: construí el seed completo en
vpn-clientes/wg-cli/— mirror del dir canónico que vivirá en la VM (/etc/wireguard/wg-cli/, D5). Versionado en GitHub para revisar sin tocar prod; el despliegue a la VM es paso gated posterior (piloto). Contenido:README.md,peers.schema.md(shape v1),peers.yaml(plantilla con roles + ADFSA/22, solo pubkeys),tooling/conlib/common.sh(guardas dry-run/refuse-prod) +lib/parse_peers.py(parser YAML propio sin PyYAML),render-wg-cli.sh(generawg-cli.conf+firewall.rulesnft),wg-create-key.sh/wg-install-key-server.sh/wg-revoke-peer.sh/wg-list-peers.sh,lab/README.md(plan #414 con 6 tests de segmentación),.gitignore(nunca commitear confs/llaves). - Diseño aplicado:
AllowedIPs= solo el/32de cada peer (jamás una LAN); firewall rol-based default-amplio (operadores → servidores+monitoreos+routers+adfsa, DROP por default, servidor↔servidor DROP); el campoallowpor peer permite apretar a per-operador /32. - Probado (todo local, sin tocar prod): render conf+firewall correctos;
validateatrapa IP fuera de subred / duplicados;wg-create-keygenera keypair real y auto-sugiere IP libre; guards de prod (install/revoke se niegan contra el endpoint201.174.219.154sin--i-know-prod); dry-run por default no mutapeers.yaml. - Bug encontrado y corregido:
wg-create-keysugería IPs ya en uso (.10) por un fallo al pasar el arg tras el heredoc; reescrito pasandocidr/usedpor variables de entorno → ahora sugiere correctamente la primera libre (operador.0.2, servidor.10.3, adfsa.16.2). - Falta: #414 (lab en poseidon con los 6 tests de segmentación) y, ya en piloto, sembrar el dir en la VM como git repo → NAS. Nada toca prod aún.
2026-05-29 (tarde, 2) — #073 cerrado al 100%: 4 confirmaciones + segmentación rol-based
- Confirmó Sergio: (1) el firewall de Juárez sí puede DNAT un 2º puerto UDP (
51821) a la VM; (2) ADFSA cabe en/22(~1022); (3) piloto = ARIAS; (4) apolo es concentrador únicamente, no gateway; política de acceso inicial = todos los operadores a todos los clientes, pero flexible para restringir por operador después. - Segmentación elegida: rol-based con default amplio (v1:
operadores → pool servidores+monitoreos+routers+ADFSA); el campoallowpor peer permite apretar a per-operador /32 sin rehacer. Default amplio aceptable porque entrar a la LAN igual exige SSH+creds. - Estado: diseño 100% cerrado (las 5 decisiones + segmentación + 4 confirmaciones), todo en
PLAN.md. Convertí el trabajo restante en pendientes accionables: #413 (scaffolding del dir canónico + scripts, 📅 3-jun), #414 (lab de validación en poseidon, 📅 5-jun), y #077 (piloto ARIAS) pasa acond:fase-0b-verde._next-id413→415. - Falta: ejecutar #413/#414. Nada toca prod hasta el piloto.
2026-05-29 (tarde) — #073: contexto de Sergio simplifica el diseño, 5 decisiones cerradas
- Pidió Sergio: dar contexto antes de decidir D2/D3/D5. Puntos: (1) no interesa ver LANs de cliente, solo acceso directo a los servidores; (2) para la LAN del cliente se hace SSH tunnel vía el servidor — necesario porque hay LANs solapadas entre clientes y con nosotros; (3) no hace falta /24 por cliente, mejor subredes por función (operadores/routers/monitoreos); (4) solo ADFSA necesita subred propia (cientos de tiendas son cliente). Cerró: D1 único · D2 wg-cli en VM wireguard · D3 10.99/16 (con subredes por función) · D4 OAuth · D5 source of truth en la VM.
- Impacto en el diseño (revisé
PLAN.md): el concentrador deja de rutear LANs y solo expone el /32 de túnel de cada servidor. Eso elimina SNAT, rutas de retorno, el riesgo “apolo es gateway de la /16” y el choque de LANs solapadas — todo de un golpe. Plan de direcciones reescrito por función (operadores.0/24, routers.1/24, monitoreos.2/24, servidores.10/24, ADFSA.16/22). Source of truth movido a/etc/wireguard/wg-cli/en la VM (git → NAS). Segmentación ahora sobre /32 (DROP-default + reglas por rol). - Falta: materializar scaffolding (dir canónico en la VM + 4 scripts esqueleto + lab en poseidon). Nada toca prod aún. (La única pregunta abierta, segmentación rol-based vs per-operador, Sergio la cerró el mismo día → rol-based; ver entrada siguiente.)
2026-05-29 — #073 Fase 0: diseño completo (architect)
- Pidió Sergio: empezar con #073 (Fase 0, diseño + scaffolding del concentrador WG).
- Hice: diseño completo en
vpn-clientes/PLAN.md(subagente architect, read-only sobre el proyecto + README de la VMwireguard+ backups-infra + ssh config). Cuestioné la premisa de “concentrador nuevo desde cero”. - Recomendación central: híbrido — levantar una instancia
wg-cliseparada (puerto51821, subnet10.99.0.0/16, firewall propio) en la misma VMwireguardque ya corre el road-warrior. Reusa endpoint público (201.174.219.154, ya DNAT’eado),ip_forwardy reachability LAN/10.11.x, pero sin heredar elMASQUERADEciego delwg0— la segmentación dura se hace conFORWARDDROP-por-default + reglas operador↔cliente. Mezclar clientes en elwg0actual = la VPN plana de apolo con otro nombre (descartado). - Diseñado: topología hub-and-spoke; subnets
/24por cliente sobre10.99.0.0/16; source-of-truth en YAML versionado (NetBox solo reserva prefijos); los 4 scripts de tooling en esqueleto; plan de validación en lab aislado con 4 tests de segmentación; esqueleto de migración paralela desde apolo (ADFSA/Grupo Imperial al final). - Hallazgos: (1) el endpoint público de la VM
wireguard= IP del aliasrouter-juarez→ ya hay DNAT del firewall de Juárez; (2)apolo2=10.11.0.1sugiere que apolo es el gateway de la /16, no solo un peer (a confirmar antes de cutover). - Falta (bloqueante): Sergio cierra D2 (poseidon), D3 (
10.99/16), D5 (YAML) — las tres con rec clara, sí/no rápido. Con eso se materializa el scaffolding (subdirtooling/+peers.yaml/schema) y se levanta el lab. Pendientes a confirmar: DNAT de 2º puerto UDP, rol de apolo, mapping operador→cliente, cliente piloto.
2026-05-08
- Pidió Sergio: crear nueva estructura de VPN para clientes — replace de
apolocon WireGuard, segmentación por cliente, portal, scripts (crear llaves, instalar en server existente), manuales de uso. - Hice: capturado el proyecto con la lista de componentes (concentrador, segmentación, portal, scripts, manuales), inventario implícito de clientes que dependen de
apolohoy (riesgo alto si se tira sin migración paralela), plan de fases para migración sin downtime, y decisiones marco que requieren input de Sergio antes de tocar nada. - Falta: todo el trabajo real — el proyecto es captura. Implementación arrancará cuando Sergio priorice. Recomendación: cuando arranque, hacer Fase 0 (diseño) primero y validar antes de tocar peers reales.
2026-05-11
- Pidió Sergio (implícito): reafirmado durante la sesión de cleanup de
orion— al desactivar el viejoesconfigbackup(proceso que hacía backups automáticos de servidores vía VPN, ya desactualizado), Sergio mencionó que esto le recordaba el pendiente de “rehacer la estructura VPN de servidores”. - Hice: confirmado que este proyecto ya cubre lo que mencionó; no se duplicó. Anotada la referencia para tener el contexto del trigger.
- Falta: sin cambios — sigue esperando que Sergio priorice Fase 0 (diseño).