Secrets e Encriptacao

O Runner implementa um sistema de encriptacao em duas camadas para proteger secrets e permitir recuperacao completa em caso de perda do servidor.

Nota v2.12.0+: o --branch agora e obrigatorio em runner add. Os exemplos abaixo usam --branch dist por convencao mas sirva sempre o branch real do repo (main, dev, etc). O --ckey sem valor (ex: runner add --repo X --branch main --ckey) gera ckey random de 32 chars automaticamente — guarde no password manager (mostrado uma unica vez no stderr).

Nota v2.14.0+: o runner agora BLOQUEIA runner add se detectar key sensitive (DB_PASSWORD, API_KEY, JWT_SECRET, etc) em environment: sem ckey configurada. Mover pra secrets: no .deploy.yml ou adicionar --ckey. Bypass via `--insecure` (NAO RECOMENDADO).

Problema

Quando um servidor de producao morre, voce perde:

  • Arquivos .env e .secrets de cada aplicacao
  • Configuracoes manuais (API keys, senhas externas)
  • Estado das aplicacoes

Reconstruir tudo manualmente e lento e propenso a erros.

Solucao: Encriptacao em Duas Camadas

Camada 1: ckey (por projeto)           Camada 2: master key (por servidor)
+---------------------------+          +-----------------------------+
| Fornecida pelo usuario    |          | Auto-gerada no runner init  |
| via: runner add --ckey    |          | Efemera (morre com server)  |
|                           |          |                             |
| Encripta: .env + .secrets |          | Encripta: ckeys em repouso |
| Destino: .runner/         |          | no state file               |
|   secrets.enc (no repo)   |          |                             |
|                           |          | Servidor morre ->           |
| Usuario memoriza/salva    |          | gera nova master key,       |
| em password manager       |          | user re-passa as ckeys      |
+---------------------------+          +-----------------------------+

Master Key

  • Gerada automaticamente pelo runner init
  • Armazenada em /opt/runner/.keys/master.key (permissao 600)
  • Usada para encriptar ckeys e tokens no state file
  • Efemera: se o servidor morre, uma nova e gerada
  • Nao precisa ser memorizada pelo usuario

Master Key Backup (v2.8.0+)

Para disaster recovery, exporte a master key antes de perder o servidor:

runner mkey reveal --json    # mostra mkey + fingerprint + created_at + rotated_count
runner mkey export --sealed --json   # blob mkseal:v1:... pra storage offline

Injete numa nova instalacao:

runner init --mkey "BASE64_DA_MASTER_KEY"

Master Key Rotation (v2.11.0+)

Rotacionar a master key re-sela todas as ckeys atomicamente:

runner mkey rotate --force --json
# {
#   "old_fingerprint": "mk_2c1a…b87f",
#   "new_fingerprint": "mk_9001…3f0a",
#   "ckeys_resealed": 4,
#   "duration_ms": 1820,
#   "backup_path": "/opt/runner/.keys/master.key.rotated.2026-04-25T....bak"
# }

Usar quando suspeitar de comprometimento da mkey atual ou em rotação periodica de seguranca.

Content Key (ckey)

  • Fornecida pelo usuario ao registrar uma app (runner add --ckey)
  • Usada para encriptar .env e .secrets do projeto
  • O resultado e salvo como .runner/secrets.enc dentro do repositorio
  • Persistente: o usuario deve salvar em um password manager
  • Mesma ckey restaura os secrets em qualquer servidor

Display de ckey gerada (v2.18.0+)

Por default, runner add --ckey (sem valor) gera ckey random e mostra apenas o fingerprint:

Generated random ckey (fingerprint: ck_XEpj…JRLx).
  Stored encrypted in /opt/runner/state/<app>.yml.
  Retrieve later: `runner ckeys export <app>` (with mkey).
  Display plaintext: rerun with --ckey-show (only on private terminals).

A ckey fica encriptada em repouso com a mkey local (state file mode 600). Pra recuperar plaintext: runner ckeys export <app>.

Pra exibir plaintext ao gerar (ex: pra colar no password manager imediatamente), use --ckey-show:

runner add --repo X --branch main --ckey --ckey-show
# Generated random ckey (32 chars). Save it now: shown only here.
#   ckey: <32-char-alfanum-aleatoria>     # exemplo: XEpjzTTA…JRLx (seu valor sera diferente)
#   fingerprint: ck_XEpj…JRLx

Justificativa: terminal compartilhado, log de CI, paste em chat/issue eram vetores de leak antes da v2.18.0. Default agora protege.

Env Wizard (v2.8.1+)

Os arquivos .env e .secrets podem ser gerados automaticamente a partir das secoes environment: e secrets: do .deploy.yml, sem depender de templates separados.

Veja Env Wizard para sintaxe completa e exemplos.

Wizard CLI (v2.17.0+) — gerar `.secrets.enc` standalone

Voce pode gerar .runner/secrets.enc antes mesmo de instalar o runner usando o subcomando runner wizard. O wizard:

  1. Detecta variaveis sensiveis em .env/.env.example/docker-compose.yml
  2. Pergunta o que fazer com cada uma (manter, editar, marcar como secret, remover)
  3. Gera ckey random (ou aceita uma sua) e encripta em .runner/secrets.enc
  4. Mostra a ckey uma unica vez — anote
  5. Comita junto com .deploy.yml no repo
runner wizard --path /caminho/repo --ckey         # gera ckey random
runner wizard --path . --ckey "minha-ckey"        # usa ckey existente

No deploy, basta passar a mesma ckey:

runner add --repo user/app --branch main --ckey "minha-ckey"
# Runner detecta .runner/secrets.enc no repo e restaura .env + .secrets

Detalhes: `runner wizard`.

Vault (v2.11.0+)

Listar e gerenciar ckeys

runner ckeys list --json                              # enumera ckeys (per-app + shared)
runner ckeys add <project> --algorithm AES-256-GCM    # cria ckey shared selada com mkey
runner ckeys export <id> --json                       # exporta ckey selada (transferivel entre servers com mesma mkey)
runner ckeys import-sealed --blob <b64> --project <p> # importa ckey de outro server

Config Backups

Snapshots automaticos de .deploy.yml + .secrets.enc + state.yml por app:

runner config-backups list --json
runner config-backups create <app> --json             # snapshot manual
runner config-backups restore <id> --json             # restaura (auto pre-restore snapshot)

Cross-project secrets

# Local (mesmo server)
runner secrets paste-encoded --project hogi --key DB_PASS --mode manual --value xxx
runner secrets copy --from inboxer --to hogi --keys SMTP_HOST,SMTP_PASS

# Cross-server (sys orquestra via export/import-sealed)
runner secrets export --project hogi --keys SMTP_PASS,DB_URL --json
# → blob secseal:v1:... pra import em outro server com mesma mkey

runner secrets import-sealed --blob <secseal> --project hogi --json

Bug Resolvido (v2.11.2)

A versao v2.4.2 ate v2.11.1 tinha um bug onde o .secrets nao era injetado em redeploys se o state.ckey da app estivesse ausente (perdido em migracao, removido manualmente, etc). Resultado: containers subiam com credenciais vazias e workers falhavam ao bootar.

A v2.11.2 remove a verificacao app_has_ckey — agora o .secrets e sempre injetado se o arquivo existir. Atualize com runner self-update se voce esta em uma versao afetada.

Fluxo Completo

Primeiro Deploy (Setup)

# 1. Inicializar Runner (gera master key)
runner init

# 2. Registrar app com ckey
runner add \
  --repo devborlot/minha-app \
  --branch dist \
  --token ghp_xxxxxxxxx \
  --ckey "minha-chave-secreta-do-projeto"

# 3. Fazer deploy (provisiona .env/.secrets, encripta e commita no repo)
runner deploy minha-app

Apos o deploy, o Runner:

  1. Provisiona .env e .secrets a partir de .env.template
  2. Encripta ambos com a ckey usando AES-256-GCM
  3. Salva como .runner/secrets.enc no repositorio
  4. Commita e faz push automaticamente

Deploys Seguintes

runner deploy minha-app

A cada deploy, os secrets sao re-encriptados e atualizados no repo. Se voce alterar uma variavel via runner env set, o proximo deploy atualiza o secrets.enc.

Recovery (Servidor Morre)

# 1. Inicializar Runner no novo servidor (nova master key)
runner init

# 2. Registrar app com a MESMA ckey
runner add \
  --repo devborlot/minha-app \
  --branch dist \
  --token ghp_xxxxxxxxx \
  --ckey "minha-chave-secreta-do-projeto"

# 3. Runner detecta .runner/secrets.enc no repo e restaura .env/.secrets

# 4. Deploy funciona sem intervencao manual
runner deploy minha-app

O runner add --ckey detecta automaticamente se .runner/secrets.enc existe no repo e, se .env/.secrets nao existem localmente, restaura-os usando a ckey.

Regra de Encriptacao (v2.4.2)

O .secrets so existe com encriptacao. Sem --ckey, nao ha .secrets — tudo vai pro .env.

Registro .secrets Comportamento
runner add --repo X --ckey K Criado (chmod 600) Encriptado em repouso
runner add --repo X (sem ckey) Nao criado Secrets vao pro .env

Deteccao automatica

O runner detecta automaticamente variaveis sensiveis pelo nome (Aho-Corasick, v2.4.2):

  • Alta confianca (95%+): DB_PASSWORD, API_KEY, GITHUB_TOKEN → com ckey, encripta automaticamente. Sem ckey, salva no .env com warning
  • Media confianca (75%): MY_TOKEN, APP_SECRET → aviso "pode ser sensivel"
  • Falsos positivos filtrados: keyboard, primary_key, max_tokens, cache_key, public_key

Forcando como secret

runner env set myapp --key CUSTOM --value xxx --secret   # requer ckey
runner env set myapp --key CUSTOM --value xxx --force    # requer ckey, sem questionamento

Sem ckey, --secret e --force retornam erro: "Secrets requerem encriptacao. Registre com --ckey".

Detalhes Tecnicos

Algoritmo de Encriptacao

Componente Algoritmo
Derivacao de chave SHA-256 (ckey -> chave AES)
Encriptacao AES-256-GCM
Nonce 12 bytes aleatorios
Formato nonce (12 bytes) + ciphertext

Formato do secrets.enc

O arquivo .runner/secrets.enc contem:

[12 bytes nonce][ciphertext AES-256-GCM]

Ao decriptar, o conteudo e:

---ENV---
CHAVE1=valor1
CHAVE2=valor2
---SECRETS---
SECRET_KEY=valor_secreto
DB_PASSWORD=senha

Permissoes de Arquivos

Arquivo Permissao Motivo
.env 644 Configuracao nao sensivel
.secrets 600 Credenciais sensiveis
master.key 600 Chave mestra do servidor
secrets.enc Repo Seguro (encriptado)

Estrutura no Repositorio

minha-app/
├── .deploy.yml
├── .env.template
├── .runner/
│   └── secrets.enc      # Encriptado com ckey
├── dist/
│   └── ...
└── package.json

O .runner/secrets.enc e versionado no repositorio. O Runner garante que nao esta no .gitignore adicionando uma regra de negacao se necessario.

State File

A ckey e armazenada encriptada no state file (/opt/runner/state/{app}.yml):

app_path: /data/apps/minha-app
project: minha-app
ckey: "enc:base64_nonce:base64_ciphertext"   # Encriptada com master key
github_token: "enc:base64_nonce:base64_ciphertext"

Seguranca

O que e seguro

  • A ckey nunca e armazenada em texto plano no disco
  • Os secrets no repo estao encriptados com AES-256-GCM
  • A master key tem permissao 600 (somente root/runner)
  • Sem a ckey, secrets.enc e ilegivel

Recomendacoes

  1. Use ckeys fortes: minimo 16 caracteres, alfanumericos + especiais
  2. Salve ckeys em password manager: 1Password, Bitwarden, etc.
  3. Uma ckey por projeto: nao reutilize entre projetos
  4. Nao commite ckeys: nunca coloque a ckey em .deploy.yml ou codigo

O que acontece se...

Cenario Resultado
Servidor morre Novo runner init + runner add --ckey restaura tudo
Esqueceu a ckey Secrets perdidos. Reconfigure manualmente
Repo comprometido Atacante ve secrets.enc mas precisa da ckey para decriptar
Master key comprometida Atacante ve ckeys nos state files. Rotacione ckeys

Boas Praticas

Para Novos Projetos

# Gerar ckey forte
openssl rand -base64 32
# Resultado: dG9wX3NlY3JldF9rZXlfMTIzNDU2Nzg5...

# Salvar no password manager ANTES de usar
# Depois:
runner add --repo devborlot/meu-app --branch main --ckey "dG9wX3NlY3JldF9rZXlfMTIzNDU2Nzg5..."

Para Projetos Existentes

Se ja tem uma app registrada sem ckey, adicione usando runner edit:

# Editar para adicionar ckey (futuro - use re-add por enquanto)
# Remova e re-adicione a app com --ckey

Documentacao de ckeys

Mantenha um registro das ckeys em local seguro:

Projeto: minha-app
Repo: devborlot/minha-app
ckey: dG9wX3NlY3JldF9rZXlfMTIzNDU2Nzg5...
Criada em: 2026-03-11

Deploy All

O comando deploy --all permite deployar todas as apps registradas de uma vez:

runner deploy --all

Util apos recovery do servidor:

# 1. Re-registrar todas as apps com suas ckeys (--branch obrigatorio v2.12.0+)
runner add --repo devborlot/app1 --branch main --ckey "ckey1" --token ghp_xxx
runner add --repo devborlot/app2 --branch main --ckey "ckey2" --token ghp_xxx
runner add --repo devborlot/app3 --branch dev  --ckey "ckey3" --token ghp_xxx

# 2. Deployar todas de uma vez
runner deploy --all

O comando itera todos os state files em /opt/runner/state/ e executa deploy para cada app.

Opcoes

runner deploy --all              # Deploy todas as apps
runner deploy --all --force      # Forca redeploy de todas
runner deploy --all --verbose    # Output detalhado

Network Auto-Creation

O Runner agora cria automaticamente networks Docker que nao existem. Se o .deploy.yml configura networks: [public, mysql, redis] e a network mysql nao existe, o Runner a cria antes de iniciar o container.

Alem disso, o Runner emite warnings quando uma network nao-publica esta vazia (sem containers), indicando que a infraestrutura pode nao estar rodando:

WARN: Network 'mysql' has no containers. minha-app may not connect to mysql.

Troubleshooting

Secrets nao restaurados no add

# Verificar se secrets.enc existe no repo
ls /data/apps/minha-app/.runner/secrets.enc

# Verificar se .env ja existe (restauracao so ocorre se .env NAO existe)
ls /data/apps/minha-app/.env

Decriptacao falha

A ckey esta incorreta. Verifique:

# Tentar com ckey correta
runner add --repo devborlot/minha-app --branch main --ckey "ckey-correta"

secrets.enc nao atualizado no repo

Verifique se o git push funciona:

cd /data/apps/minha-app
git remote -v
git status

O Runner faz git add .runner/secrets.enc && git commit && git push. Se falhar, um warning e emitido mas o deploy continua.

By Borlot.com.br on 11/03/2026