Identidade e enrollment
O RootPilot Agent se autentica por mTLS. Esta página é sobre a credencial que ele usa para isso: como ele a obtém, como ela se mantém viva, e — a pergunta que custa caro quando ninguém a fez antes — o que acontece quando o container é substituído.
Vale separar as duas coisas desde já, porque elas se confundem:
- Como a identidade nasce — três caminhos, e você escolhe um.
- Como ela sobrevive — renovação (dentro do processo) e persistência (através da troca de container).
A segunda metade é a que decide se um deploy exige alguém no navegador.
Como a identidade nasce
Seção intitulada “Como a identidade nasce”| Caminho | Quando usar | O que você configura |
|---|---|---|
| Atestação de nuvem | Onde a plataforma prova identidade sozinha: GKE, EC2/EKS | RUNNER_ATTESTATION_MODE + RUNNER_ENROLL_URL |
| Bootstrap token | Onde não há identidade de plataforma: VPS, host próprio, primeiro piloto | RUNNER_BOOTSTRAP_TOKEN + RUNNER_ENROLL_URL |
| Certificado provisionado | Ponte: nós emitimos, você monta | RUNNER_CERT_PEM + RUNNER_KEY_PEM |
A atestação de nuvem é a melhor onde existe, e a razão é concreta: não há segredo para mintar, entregar ou girar. Os modos disponíveis e o que cada um exige estão em Atestação.
O fluxo do enrollment
Seção intitulada “O fluxo do enrollment”O boot é uma máquina de estados: liveness → config → secrets → enroll → connect → ready. O passo
enroll faz isto:
- Gera um par de chaves EC P-256. A chave privada existe só em memória, em PKCS#8.
- Gera um nonce e monta um CSR com ele no campo
challengePassword, que é a prova de posse. - Coleta a atestação da plataforma, conforme o
RUNNER_ATTESTATION_MODE. POSTpara/api/enrollcom{ version, attestation, csr, challengeNonce }.- O control-plane valida a atestação, assina o CSR e devolve o certificado com o SPIFFE id do
tenant dentro:
spiffe://rootpilot/tenant/<slug>/runner. - O Agent abre o túnel WSS com mTLS usando esse certificado.
O tenant vem do certificado, não da configuração
Seção intitulada “O tenant vem do certificado, não da configuração”Vale insistir nisso porque é a garantia que sustenta o resto: quem determina o tenant é o material de atestação, não uma variável de ambiente. O enrollment devolve um certificado com o SPIFFE id dentro, e é ele que o control-plane lê em cada chamada.
RUNNER_TENANT, que aparece nos scripts de frota, não escolhe tenant nenhum: escolhe a pasta do
cofre de onde ler as credenciais. Errar esse slug não conecta você na organização errada — e, se o slug
declarado discordar do que a credencial enrollou, o Agent recusa subir em vez de servir a organização
errada.
Como ela se mantém viva: renovação
Seção intitulada “Como ela se mantém viva: renovação”O certificado tem vida curta — 15 minutos por padrão. Isso é de propósito: um certificado que vale pouco tempo vale pouco para quem o roubar.
RUNNER_CERT_RENEWAL=on é o default, e a renovação dispara a 2/3 do tempo que resta, não perto do
vencimento. A razão é operacional: renovar em cima do notAfter transformaria qualquer instabilidade
transitória de rede em perda de identidade. Renovando a 2/3, sobra um terço da vida como janela de retry.
O retry tem backoff com teto de 5 minutos e jitter de ±25% — sem o jitter, a frota inteira bateria no
control-plane no mesmo segundo. Se o servidor responder Retry-After, ele vence o cálculo local.
A renovação exige RUNNER_ENROLL_URL: é para lá que ela fala.
O relógio que a renovação consulta
Seção intitulada “O relógio que a renovação consulta”O Agent verifica de 30 em 30 segundos se já é hora de renovar, comparando o relógio do sistema com o vencimento do certificado. Ele não arma um despertador para daqui a 40 minutos.
A diferença aparece quando o host para de contar o tempo: uma máquina suspensa, uma VM restaurada de snapshot, um container sem CPU por quota. Um despertador longo conta tempo de processo e não avança enquanto o host dorme — ele acordaria achando que ainda faltam minutos para renovar um certificado que morreu horas antes. Perguntando ao relógio do sistema a cada 30 segundos, o Agent descobre a situação real logo depois de voltar:
- Voltou antes do vencimento — renova na hora. O certificado é salvo.
- Voltou depois — declara a perda e sai, para o supervisor subir um substituto (ver Troubleshooting), em vez de passar meia hora tentando reconectar com uma identidade morta.
Como a renovação prova identidade
Seção intitulada “Como a renovação prova identidade”A renovação não é um RUNNER_ATTESTATION_MODE. Renovar não é escolha de configuração: é o que o
Agent faz quando já tem certificado.
Ele assina o nonce do novo CSR com a chave privada atual e envia o certificado atual junto. O control-plane valida a cadeia contra a CA, confere a assinatura com a chave pública do certificado e extrai o tenant do certificado, nunca do corpo da requisição. Se lesse do corpo, renovar viraria uma via de trocar de tenant.
Como ela sobrevive ao restart: persistência
Seção intitulada “Como ela sobrevive ao restart: persistência”Aqui está a diferença que mais custa caro se ninguém a configurar.
A renovação vive dentro do processo. Substituído o container, a identidade morre com ele — e o fallback é enrollment do zero. Onde a identidade nasceu de um bootstrap token, isso significa um token novo, mintado à mão, a cada deploy (o token é de uso único).
RUNNER_IDENTITY_DIR resolve isso: aponta para um diretório onde o Agent grava a identidade
(identity.json, modo 600). No boot seguinte ele renova a partir dela em vez de enrolar do zero.
volumes: - rootpilot-identity:/identity # volume nomeado: herda a posse certa da imagemenvironment: RUNNER_IDENTITY_DIR: /identityÉ só isso: a variável basta sozinha. O Agent cria, na primeira vez que usa o diretório, um id de dono
que fica gravado ali (instance-id, modo 600) — então a identidade continua sendo reconhecida como
dele depois de o container ser recriado, que é o ponto inteiro da feature.
O RUNNER_INSTANCE_ID continua valendo a pena, mas por outro motivo: é ele que identifica a instância nos
logs e no Fleet. Não tem mais relação com a persistência.
Três coisas que valem saber antes de ligar:
- O diretório precisa ser gravável pelo uid do Agent (a imagem roda como não-root, uid 1000). Um
volume nomeado resolve sozinho: a imagem traz
/identitycom a posse certa, e o volume a herda ao ser criado. Um bind mount não herda — a posse é a do host. Se não for gravável, o boot não falha: ele avisa e segue, e você só descobre no deploy seguinte, comcould not persist identity — next boot will re-enroll from scratch. - Um diretório por Agent. Dois containers no mesmo volume dividiriam identidade.
- A janela tem limite. O que sobrevive é a renovação, e ela vale até 6 minutos além do vencimento do certificado (5 de graça + 1 de margem de relógio). Um deploy normal cabe folgado; um host desligado a noite inteira, não — aí é enrollment do zero.
Quando a identidade se perde
Seção intitulada “Quando a identidade se perde”Passados os 6 minutos, o control-plane responde presented cert expired beyond the renewal grace window,
e vai responder isso para sempre: a atestação de renovação prova posse com o certificado que morreu.
O comportamento então é sair do processo, para que o supervisor suba um Agent novo que enrolla do zero. Sem essa saída, o processo viraria zumbi: de pé, sem servir, e invisível para qualquer probe.
identity expired beyond the renewal grace window — runner needs re-enrollmentO certificado provisionado
Seção intitulada “O certificado provisionado”O RootPilot emite um certificado para o seu tenant e você o monta em /certs (ou passa inline em
RUNNER_CERT_PEM/RUNNER_KEY_PEM/RUNNER_CA_PEM). Não exige atestação de plataforma nem egress para o
endpoint de enrollment, e é a ponte para ambientes onde nenhum dos outros dois se aplica ainda.
O custo é declarado: ele não se renova. Quando vencer, a frota para de conectar, e o Agent não sai do
processo nem avisa — ele fica tentando reconectar indefinidamente, com erro de TLS que não diz que é isso.
A data está no notAfter da linha de boot; anote-a no calendário no dia em que receber o certificado.
A validação de configuração cobre isso
Seção intitulada “A validação de configuração cobre isso”O schema exige pelo menos um caminho de identidade, e falha rápido se não houver nenhum:
either provide RUNNER_CERT_PEM+RUNNER_KEY_PEM (provisioned cert) or RUNNER_ENROLL_URL (enrollment)