Skip to content

Cloner de Sitios Públicos — Referencia Técnica

Versión concisa en español. Para detalles más profundos (algoritmo de captura browser engine, reescritura de URLs, tuning de rendimiento) consulte CLONER.md (inglés).

Estado del alcance (post-congelación de alcance 2026-05-10) — CLONER ahora es formalmente MÓDULO CLONER.Art con 9 funciones (no sólo clonación web). Vea modules/cloner-art.md para el desglose canónico de las 9 funciones y help-center/primers/obp-operator-bridge-proxy.md para la integración de egress air-gap.


1. Para qué sirve

El Cloner descarga sitios públicos reales (noticias, e-commerce, media, etc.) y produce una copia local servible por la flota de Cloned Personas. De esta forma, la flota de agentes puede probar inspección TLS a través del NGFW usando contenido real de sitios conocidos — sin depender de internet durante las pruebas.

2. Por qué existe

La inspección TLS de sitios reales es el caso de uso central de NGFWs modernos. Las 20 Synthetic Personas sirven contenido determinístico (Caddy estático, mock APIs, replay HAR), lo cual cubre la superficie TLS pero no la heterogeneidad real de la web. Los 10 slots de Cloned Persona resuelven eso: el Cloner captura sitios reales y los replica en los slots, manteniendo la prueba reproducible pero representativa.

3. Arquitectura

┌─────────────────────────────────────────────────────────────────────┐
│  Cloner Pod (deployment: cloner, namespace: web-agents)            │
│                                                                     │
│  Internet (TCP 80/443, UDP 443/QUIC, DNS, ICMP)                    │
│              ▲                                                      │
│              │  net1 ─── VLAN 40 macvlan (DHCP vía CNI plugin)     │
│  ┌───────────┴────────────────────────────────────────────────┐   │
│  │  browser engine headful + plugin stealth                        │   │
│  │  (headless browser, navigator.webdriver = undefined)                │   │
│  └───────────┬────────────────────────────────────────────────┘   │
│              │                                                      │
│              ▼ escribe en                                           │
│  /mnt/cloned/{personaName}/   (PVC NFS RWX cloned-sites)           │
│                                                                     │
│  Dashboard (eth0 / OOBI) ◄──── controla jobs, heartbeat            │
└─────────────────────────────────────────────────────────────────────┘
         ▼ servidor NFS in-cluster (k8s/dut/35-nfs-server.yaml)
NFS server (web-agents/nfs-server) — respaldado por hostPath
en UCS-4 (multi-node) o en el único nodo (single-node).
         ▼ exportado vía NFSv4 / OOBI ClusterIP
PVCs por namespace (k8s/dut/36-cloned-sites-pvs.yaml)
         ▼
┌─────────────────────────────────────────────────────────────────────┐
│  Cloned Persona Pods (clone-persona-1 … clone-persona-10)          │
│  VLANs 200–209 / 10.2.{1..10}.0/27                                │
│  Caddy: file_server /mnt/cloned/{SITE_NAME}                        │
└─────────────────────────────────────────────────────────────────────┘
         ▲ NGFW inspecciona TLS
┌─────────────────────────────────────────────────────────────────────┐
│  Agentes (browser-engine VLAN 20, synthetic-load VLAN 30)                          │
│  https://cloned-{n}.persona.internal/  → ruteado vía NGFW          │
└─────────────────────────────────────────────────────────────────────┘

Separación de planos de red:

  • Cloner descarga vía net1 (VLAN 40) → ISP → internet pública. NGFW no está en ese camino.
  • Agentes prueban vía net1 (VLAN 20/30) → NGFW → Cloned Persona Caddy. NGFW está en ese camino.
  • Storage NFS entre cloner y slots pasa SOLO por la OOBI (eth0 / pod network). Nunca toca el data plane.

4. Red

Interfaz Función VLAN Subnet
eth0 OOBI / pod network: Dashboard, NFS, DNS k8s pod CIDR
net1 macvlan VLAN 40: egreso ISP vía DHCP 40 DHCP del router upstream

DNS forzado (vía dnsPolicy: None): - 10.43.0.10 → CoreDNS (k3s) - 10.96.0.10 → CoreDNS (kubeadm) - 8.8.8.8 → Google Public DNS (sitios externos) - 208.67.222.222 → OpenDNS (fallback)

Policy routing: paquetes no-RFC1918 (TCP/UDP/ICMP) reciben fwmark 100 y usan tabla 100 (default vía gateway DHCP). Tráfico RFC1918 (incluyendo OOBI y NFS vía 10.x.x.x) sigue por eth0.

CNI DHCP daemon (k8s/dut/30-cni-dhcp-daemon.yaml): DaemonSet en todos los nodos que expone el socket /run/cni/dhcp.sock requerido por el IPAM tipo dhcp del NAD VLAN 40.

5. Almacenamiento — NFS in-cluster, solo OOBI

El volumen cloned-sites se comparte entre el Cloner (escritor) y los 10 slots Cloned Persona (lectores). Como los PVCs son namespace-scoped y los slots viven en 10 namespaces separados, el compartido se hace mediante 11 pares estáticos PV/PVC apuntando al mismo export del servidor NFS in-cluster.

Componente Manifiesto Propósito
NFS Deployment + Service k8s/dut/35-nfs-server.yaml Servidor NFSv4 réplica única, nodeAffinity prefiere role=infra. Respaldado por hostPath /var/lib/agent-cluster/cloned-sites. Estrategia Recreate garantiza writer único. InitContainer prepare-share chowna a UID 10004 antes que el daemon arranque.
11 PV/PVC estáticos k8s/dut/36-cloned-sites-pvs.yaml 1 par RWX writer (web-agents/cloned-sites) + 10 ROX readers (clone-persona-N/cloned-sites). Todos apuntan al mismo export. storageClassName: "" previene CSI race.

Topología multi-node:

  • Cloner corre en role=infra (UCS-4) — mismo nodo que el NFS server.
  • Slots Cloned Persona corren en role=ngfw-dut (UCS-1).
  • Tráfico NFS fluye por OOBI (eth0 → Service nfs-server.web-agents.svc.cluster.local:2049).
  • Data plane (net1 macvlan VLAN 40 + 200-209) nunca lleva I/O de storage.

Topología single-node: todo en el mismo nodo. NFS round-trip es loopback, overhead despreciable.

Atributo Valor
Backend NFSv4 in-cluster Service
Capacidad 50 Gi (claim) — limitada por el disco del nodo del NFS server
Access mode (writer) ReadWriteMany
Access mode (10 readers) ReadOnlyMany
Mount (Cloner) /mnt/cloned (read-write)
Mount (Cloned Personas) /mnt/cloned (read-only)
Plano de red OOBI solamente (eth0)
Co-localización multi-node Cloner + NFS server en el mismo nodo (role=infra)

6. Ciclo de vida de un job

  1. Operador crea job: POST /api/clone/jobs {url, personaName: "shop"}.
  2. Cloner hace long-poll en /api/clone/agents/{id}/job y reclama el job.
  3. browser engine headful (con plugin stealth) navega a la URL, intercepta cada response vía route.fetch(), captura body+headers.
  4. Reescritura HTML: (href|src|action)="${origin}=" (paths relativos).
  5. Persistencia: /mnt/cloned/{personaName}/index.html + assets (mismo dominio en carpeta original, cross-origin bajo _ext/).
  6. Cloner reporta: PATCH /api/clone/jobs/{id} {status: "completed", assetCount, totalBytes}.
  7. Operador asigna slot: PATCH /api/clone/persona-slots/N {siteName: "shop", replicas: 1}.
  8. Dashboard: actualiza ConfigMap (SITE_NAME), escala Deployment, upsert en la tabla targets (la URL https://cloned-N.persona.internal/ se vuelve target automáticamente — ver PR #155).
  9. Stakater Reloader detecta ConfigMap → reinicia pod.
  10. Caddy slot arranca sirviendo /mnt/cloned/shop/ en la IP del macvlan VLAN 200+N-1.
  11. Agentes browser-engine/synthetic-load toman la nueva target en el siguiente poll, navegan a través del NGFW.

7. Troubleshooting esencial

Síntoma Verificar
Cloner en pod incorrecto (multi-node) kubectl get pod -o wide — NODE debe ser UCS-4
net1 sin IP kubectl get ds cni-dhcp-daemon -n web-agents (Ready en todo nodo)
Slot pod MountVolume.SetUp failed kubectl get pod -n web-agents -l app.kubernetes.io/name=nfs-server -o wide
PVC cloned-sites Pending kubectl describe pv cloned-sites-writer
Slots Ready pero sirviendo vacío Cloner escribió en otro nodo? Reinicie NFS y luego cloner
Sin internet kubectl exec deploy/cloner -- ping -c 3 -I net1 8.8.8.8

Detalles operativos: CLONER_OPERATIONS.es.md.

8. Archivos clave

Archivo Descripción
cloner/src/cloner.ts Motor de cloning con browser engine + stealth
cloner/src/index.ts Loop principal: register, heartbeat, poll, run
cloner/src/health-monitor.ts Ping ICMP + métricas Prometheus
k8s/80-cloner-nad.yaml NAD ISP (master: eth1.40, IPAM dhcp)
k8s/81-cloner-deployment.yaml Deployment del cloner con initContainer routing
k8s/dut/30-cni-dhcp-daemon.yaml DaemonSet CNI DHCP (requerido para IPAM de VLAN 40)
k8s/dut/35-nfs-server.yaml NFS server in-cluster para cloned-sites cross-node
k8s/dut/36-cloned-sites-pvs.yaml 11 PV/PVC estáticos (1 writer + 10 readers)
dashboard/src/app/api/clone/ APIs de jobs, slots y agents del cloner

© 2026 André Luiz Gallon — Distribuido bajo PolyForm Noncommercial 1.0.0 con Restricciones Adicionales de Uso (Apéndice A).