Skip to content

Cloner de Sites Públicos — Referência Técnica

Versão concisa em português. Para detalhes mais profundos (algoritmo de captura browser engine, rewrite de URLs, tuning de performance), consulte CLONER.md (inglês).

Status do escopo (pós-congelamento de escopo 2026-05-10) — CLONER agora é formalmente MÓDULO CLONER.Art com 9 funções (não só clonagem web). Veja modules/cloner-art.md para o detalhamento canônico das 9 funções e help-center/primers/obp-operator-bridge-proxy.md para integração de egress em air-gap.


1. Para que serve

O Cloner baixa sites públicos reais (notícias, e-commerce, mídia, etc.) e produz uma cópia local servível pela frota de Cloned Personas. Dessa forma a frota de agentes pode testar inspeção TLS pelo NGFW usando conteúdo real de sites conhecidos — sem depender de conexão à internet durante os testes.

2. Por que existe

A inspeção TLS performante de sites reais é o caso de uso central de NGFWs modernos. As 20 Synthetic Personas servem conteúdo determinístico (Caddy estático, mock APIs, HAR replay), o que cobre superfície TLS mas não a heterogeneidade real da web. Os 10 slots de Cloned Persona resolvem isso: o Cloner captura sites reais e replica nas slots, mantendo o teste reprodutível mas representativo.

3. Arquitetura

┌─────────────────────────────────────────────────────────────────────┐
│  Cloner Pod (deployment: cloner, namespace: web-agents)            │
│                                                                     │
│  Internet (TCP 80/443, UDP 443/QUIC, DNS, ICMP)                    │
│              ▲                                                      │
│              │  net1 ─── VLAN 40 macvlan (DHCP via CNI plugin)     │
│  ┌───────────┴────────────────────────────────────────────────┐   │
│  │  browser engine headful + plugin stealth                        │   │
│  │  (headless browser, navigator.webdriver = undefined)                │   │
│  └───────────┬────────────────────────────────────────────────┘   │
│              │                                                      │
│              ▼ escreve em                                           │
│  /mnt/cloned/{personaName}/   (PVC NFS RWX cloned-sites)           │
│                                                                     │
│  Dashboard (eth0 / OOBI) ◄──── controla jobs, heartbeat            │
└─────────────────────────────────────────────────────────────────────┘
         ▼ NFS server in-cluster (k8s/dut/35-nfs-server.yaml)
NFS server (web-agents/nfs-server) — backed by hostPath
em UCS-4 (multi-node) ou no único nó (single-node).
         ▼ exportado via 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 inspeciona TLS
┌─────────────────────────────────────────────────────────────────────┐
│  Agentes (browser-engine VLAN 20, synthetic-load VLAN 30)                          │
│  https://cloned-{n}.persona.internal/  → roteado via NGFW          │
└─────────────────────────────────────────────────────────────────────┘

Separação de planos de rede:

  • Cloner baixa via net1 (VLAN 40) → ISP → internet pública. NGFW não está nesse caminho.
  • Agentes testam via net1 (VLAN 20/30) → NGFW → Cloned Persona Caddy. NGFW está nesse caminho.
  • Storage NFS entre cloner e slots passa SOMENTE pela OOBI (eth0 / pod network). Nunca toca o data plane.

4. Rede

Interface Função VLAN Subnet
eth0 OOBI / pod network: Dashboard, NFS, DNS k8s pod CIDR
net1 macvlan VLAN 40: egresso ISP via DHCP 40 DHCP do roteador upstream

DNS forçado (via dnsPolicy: None): - 10.43.0.10 → CoreDNS (k3s) - 10.96.0.10 → CoreDNS (kubeadm) - 8.8.8.8 → Google Public DNS (sites externos) - 208.67.222.222 → OpenDNS (fallback)

Policy routing: pacotes não-RFC1918 (TCP/UDP/ICMP) ganham fwmark 100 e usam tabela 100 (default via gateway DHCP). Tráfego RFC1918 (incluindo OOBI e NFS via 10.x.x.x) segue eth0.

CNI DHCP daemon (k8s/dut/30-cni-dhcp-daemon.yaml): DaemonSet em todos os nós que expõe o socket /run/cni/dhcp.sock exigido pelo IPAM tipo dhcp da NAD VLAN 40.

5. Storage — NFS in-cluster, OOBI only

O volume cloned-sites é compartilhado entre o Cloner (escritor) e os 10 slots Cloned Persona (leitores). Como PVCs são namespace-scoped e os slots vivem em 10 namespaces separados, o compartilhamento é feito por 11 pares estáticos PV/PVC apontando para o mesmo export do NFS server in-cluster.

Componente Manifest Propósito
NFS Deployment + Service k8s/dut/35-nfs-server.yaml Servidor NFSv4 réplica única, nodeAffinity prefere role=infra. Backed por hostPath /var/lib/agent-cluster/cloned-sites. Recreate strategy garante writer único. InitContainer prepare-share chowna pra UID 10004 antes do daemon iniciar.
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 apontam pro mesmo export. storageClassName: "" previne CSI race.

Topologia multi-node:

  • Cloner roda em role=infra (UCS-4) — mesmo nó do NFS server.
  • Slots Cloned Persona rodam em role=ngfw-dut (UCS-1).
  • Tráfego NFS flui via OOBI (eth0 → Service nfs-server.web-agents.svc.cluster.local:2049).
  • Data plane (net1 macvlan VLAN 40 + 200-209) nunca carrega I/O de storage.

Topologia single-node: tudo no mesmo nó. NFS round-trip é loopback, overhead desprezível.

Atributo Valor
Backend NFSv4 in-cluster Service
Capacidade 50 Gi (claim) — limitada pelo disco do nó do 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 rede OOBI somente (eth0)
Co-localização multi-node Cloner + NFS server no mesmo nó (role=infra)

6. Lifecycle de um job

  1. Operador cria job: POST /api/clone/jobs {url, personaName: "shop"}.
  2. Cloner faz long-poll em /api/clone/agents/{id}/job e claima o job.
  3. browser engine headful (com plugin stealth) navega até a URL, intercepta cada response via route.fetch(), captura body+headers.
  4. Reescrita HTML: (href|src|action)="${origin}=" (paths relativos).
  5. Persistência: /mnt/cloned/{personaName}/index.html + assets (mesmo domínio em pasta original, cross-origin sob _ext/).
  6. Cloner reporta: PATCH /api/clone/jobs/{id} {status: "completed", assetCount, totalBytes}.
  7. Operador binda slot: PATCH /api/clone/persona-slots/N {siteName: "shop", replicas: 1}.
  8. Dashboard: atualiza ConfigMap (SITE_NAME), escala Deployment, upsert na tabela targets (a URL https://cloned-N.persona.internal/ vira target automaticamente — ver PR #155).
  9. Stakater Reloader detecta ConfigMap → reinicia pod.
  10. Caddy slot sobe servindo /mnt/cloned/shop/ no IP da macvlan VLAN 200+N-1.
  11. Agentes browser-engine/synthetic-load puxam a nova target no próximo poll, navegam pela NGFW.

7. Troubleshooting essencial

Sintoma Verificar
Cloner em pod errado (multi-node) kubectl get pod -o wide — NODE deve ser UCS-4
net1 sem IP kubectl get ds cni-dhcp-daemon -n web-agents (Ready em todo nó)
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 mas servindo vazio Cloner gravou em outro nó? Reinicie NFS depois cloner
Sem internet kubectl exec deploy/cloner -- ping -c 3 -I net1 8.8.8.8

Detalhes operacionais: CLONER_OPERATIONS.pt-BR.md.

8. Arquivos-chave

Arquivo Descrição
cloner/src/cloner.ts Engine de cloning com 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 do cloner com initContainer routing
k8s/dut/30-cni-dhcp-daemon.yaml DaemonSet CNI DHCP (necessário pra IPAM da VLAN 40)
k8s/dut/35-nfs-server.yaml NFS server in-cluster pra 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 e agents do cloner

© 2026 André Luiz Gallon — Distribuído sob PolyForm Noncommercial 1.0.0 com Restrições Adicionais de Uso (Apêndice A).