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:

  1. Verifica validade do proprio cert; renova se < threshold
  2. GET /v1/manifest via mTLS
  3. Pra cada entrega nao processada: dispatch por tipo (csr_cert no MVP)
  4. Apos sucesso: POST /v1/events (entrega_instalada) + executa hook + grava em processed.json
  5. 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).

Referencias

By Borlot.com.br on 23/05/2026