Arquitetura
O Tatuzim e organizado em cinco componentes, dos quais dois ja existem off-the-shelf (step-ca, Cofre) e tres sao construidos especificamente (Server, Agent, identidade).
Visao geral
┌──────────────────────────────────────────────────────────────────┐
│ HUB │
│ │
│ ┌────────────────┐ ┌──────────────────────┐ │
│ │ step-ca │ ◄────── │ tatuzim-server │ │
│ │ (intermediate)│ HTTPS │ ┌──────────────┐ │ │
│ │ │ │ │ vault Stoolap│ │ │
│ └────────────────┘ │ │ criptograf. │ │ │
│ │ │ └──────────────┘ │ │
│ │ JWK provisioner │ │ │
│ │ (in-process) │ │ │
│ ▼ │ :8444 enroll │ │
│ ┌────────────────┐ │ :8443 mTLS │ │
│ │ root CA offline│ │ :8080 health │ │
│ │ (em USB) │ └──────────────────────┘ │
│ └────────────────┘ │
│ │
└────────────────────────────────────┬─────────────────────────────┘
│
┌──────────────────────┴──────────────────────┐
│ mTLS via Internet OU WireGuard │
└──────────────────────┬──────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
│ VPS 1 │ │ VPS 2 │ │ VPS N │
│ │ │ │ │ │
│ tatuzim-agent │ │ tatuzim-agent │ │ tatuzim-agent │
│ ├─ identity/ │ │ ├─ identity/ │ │ ├─ identity/ │
│ ├─ out/ │ │ ├─ out/ │ │ ├─ out/ │
│ ├─ hooks/ │ │ ├─ hooks/ │ │ ├─ hooks/ │
│ └─ state/ │ │ └─ state/ │ │ └─ state/ │
└────────────────────┘ └────────────────────┘ └────────────────────┘Componentes
1. step-ca (off-the-shelf)
CA hierarquica em container ao lado do Tatuzim Server. Responsavel por:
- Emitir cert TLS do proprio Tatuzim Server (auto-bootstrap)
- Emitir cert de identidade dos agentes (chamado em
POST /v1/enroll) - Emitir certs de uso pra agentes (chamado em
POST /v1/certs/issue)
Setup: root CA fica offline (em USB ou paper backup); intermediate CA fica online no container dev_stepca_tatuzim. Provisioner JWK e o que o Tatuzim Server usa pra assinar tokens (OTT) que o step-ca aceita.
Voce nao escreve uma linha de codigo de CA — usa o smallstep/step-ca de prateleira.
2. Tatuzim Server
Daemon Rust no hub. Responsavel por:
- Manter estado autoritativo em vault Stoolap criptografado
- Orquestrar enrollment de agentes (validar token + chamar step-ca)
- Emitir certs por demanda pro agente autenticado
- Servir lista de cargas pendentes (manifest)
- Receber e persistir eventos (audit log com hash chain)
Portas:
| Porta | Protocolo | Auth | Endpoint |
|---|---|---|---|
| 8080 | HTTP | nenhuma | GET /health |
| 8444 | HTTP* | enrollment token | POST /v1/enroll |
| 8443 | HTTPS + mTLS strict | cert do agent | POST /v1/certs/issue, GET /v1/manifest, POST /v1/events |
*Recomendado expor 8444 via reverse proxy com TLS (Let's Encrypt) — o conteudo nao precisa de mTLS mas o canal deve ser HTTPS.
Estado: vault em /var/lib/tatuzim/db.enc (criptografado em repouso com ChaCha20-Poly1305, chave derivada via Argon2id da master passphrase). Tabelas: agentes, enrollment_tokens, cargas, entregas, politicas, eventos.
3. Tatuzim Agent
Daemon Rust em cada VPS. Roda como user UNIX dedicado (tatuzim), em processo separado de qualquer outro agente do ecossistema.
Diretorios (em /var/lib/tatuzim-agent/, perm 0750):
identity/agent.crt(cert leaf, 0644)identity/agent.key(chave privada, 0600)identity/ca.pem(intermediate CA, 0644)out/<carga-nome>.{crt,key}(artefatos pra outros processos consumirem)state/processed.json(idempotencia das entregas)hooks/post-<tipo>(scripts executados apos processamento)
Subcomandos: enroll, identity, run, rotate, self-update.
Loop run:
- Verifica validade do proprio cert; renova se < threshold
GET /v1/manifestvia mTLS- Pra cada entrega nao processada: dispatch por tipo (
csr_certno MVP) - Apos sucesso:
POST /v1/events(entrega_instalada) + executa hook + grava emprocessed.json - Sleep
poll_interval
4. Cofre (planejado, ainda nao integrado)
Storage centralizado pra blobs sigilosos (modo selado). No MVP usamos Stoolap diretamente; quando o Cofre estiver pronto, o Tatuzim Server vai delegar storage de blobs grandes (plugins, configs sensiveis) pra ele.
Status: debito tecnico aberto, depende do Cofre existir.
5. WireGuard (opcional)
Camada de rede privada entre hub e VPS. Recomendada como defense-in-depth abaixo do mTLS.
Sem WireGuard: mTLS strict sobre Internet publica. Com WireGuard: mTLS strict sobre tunel privado — atacante na rede publica nao ve nem trafego encriptado.
O Tatuzim funciona com ou sem WireGuard — e ortogonal.
Fluxo end-to-end
Bootstrap do server
Operador: docker compose up tatuzim-server
│
▼
Server: lê config.json + passphrase env → unwrap DEK
│
▼
Unseal vault: db.enc → db.plain (Stoolap aberto)
│
▼
Verifica server.crt — se ausente ou expirado:
│
▼
Gera keypair + CSR (CN=tatuzim.dev.borlot.com.br)
│
▼
POST step-ca /1.0/sign com OTT (JWK provisioner)
│
▼
Salva server.crt + server.key + server.ca.crt
│
▼
Escuta nas 3 portas (health, enroll, mTLS)Enrollment de um agent
Admin: tatuzim token create --hostname X --role Y --ttl 1h
│
▼
Token = 32 random bytes URL-base64
Hash = SHA256(token)
INSERT enrollment_tokens (token_hash, expected_X, expected_Y, expires)
│
▼
Imprime token (single-use)
Admin entrega o token ao operador do VPS
│
▼
Operador (no VPS): tatuzim-agent enroll
│
▼
Gera keypair ECDSA P-256
Cria CSR com CN=X
│
▼
POST https://hub/v1/enroll
Body: { token, hostname=X, role=Y, csr_pem }
│
▼
Server: SHA256(token) → busca em enrollment_tokens
Valida: nao consumido, nao expirado, hostname/role batem
│
▼
POST step-ca /1.0/sign com CSR → cert assinado
│
▼
INSERT agentes (id, hostname, role, cert, ...)
UPDATE enrollment_tokens SET consumed_at=now()
│
▼
Responde { agent_id, cert_pem, ca_pem }
│
▼
Agent: salva identity/agent.{crt,key} + identity/ca.pem (perms corretas)Carga csr_cert end-to-end
Admin: tatuzim carga create --tipo csr_cert --nome traefik-cert --destino X
│
▼
INSERT cargas + INSERT entregas (agente_id=X, status=pendente)
Server roda; admin reinicia pro novo state ser visivel (limitacao atual)
Agent (loop run):
GET /v1/manifest via mTLS
│
▼
Recebe [{ id=E1, tipo=csr_cert, nome=traefik-cert }]
│
▼
Para E1 (não em processed.json):
Gera keypair + CSR (CN=hostname-do-agent)
POST /v1/certs/issue { csr_pem } via mTLS
│
▼
Server: middleware valida agent (CN do client cert)
Validates CSR CN == agent hostname
POST step-ca /1.0/sign → cert
Responde { cert_pem, ca_pem }
│
▼
Agent escreve out/traefik-cert.{crt,key} (perms)
POST /v1/events { tipo=entrega_instalada, entrega_id=E1, ... }
│
▼
Server: insert_evento (hash chain += SHA256)
UPDATE entregas SET status=instalada
│
▼
Agent: marca E1 em processed.json
Executa $HOOK_DIR/post-csr-cert traefik-cert <crt> <key>
(hook reload Traefik ou outro)Por que essa decomposicao?
Server separado dos outros sistemas
O server nao depende de Postgres compartilhado, broker, ou outro servico. Vault local + step-ca local = tudo o que precisa. Se o resto do ecossistema cair, o Tatuzim continua entregando.
Agent em processo separado
Outro binario, outro user UNIX, outro diretorio, outra systemd unit. Se um agente do ecossistema (Implant, Runner) for comprometido, o atacante nao tem acesso ao identity do Tatuzim — esta em /var/lib/tatuzim-agent/identity/ com perm 0750 do user tatuzim.
Pull-first
Agent inicia todas as operacoes sensiveis (CSR, manifest, events). Server nunca push pra agent (modo push e excecao planejada pra dados nao-confidenciais). Isso simplifica enormemente: agent so precisa de outbound HTTPS pro hub.
mTLS strict apos enrollment
Depois do enrollment (que e o unico ponto fraco), tudo exige cert do step-ca local. Atacante com cert de qualquer outra CA — inclusive Let's Encrypt valido — e rejeitado.
Limites de raio de blast
| Comprometimento de | Impacto |
|---|---|
| 1 agent | Atacante usa o cert daquele agent pra puxar manifest dele (so dele). Revogar agent, refazer enroll. |
| Hub (root no host) | Acesso a step-ca intermediate + vault. Pode emitir certs com chain raiz. Resposta: root CA offline, voce gera novo intermediate + atualiza root pinada nos agents. |
| Root CA offline | Cenario catastrofico. Tem que gerar nova root, redistribuir nos agents (rebuild + redeploy). |
| 1 cert vazado de uma chamada (ex: cert de operador) | Lifetime curto (planejado v0.4) limita janela. MVP usa cert "longo" (24h). |