stackctl

Times pequenos e projetos indie merecem uma ferramenta de infraestrutura sólida — sem o peso corporativo.

stackctl reúne gerenciamento de configurações do Kubernetes, segredos do HashiCorp Vault e VPN NetBird em uma única CLI com interface consistente. O objetivo é permitir que um time pequeno opere de forma segura e confiante: segredos nunca são expostos em texto puro, kubeconfigs ficam centralizados no Vault, o acesso à VPN é automatizado e tudo pode rodar dentro de um pipeline de CI/CD sem ferramentas adicionais.

Seja você um desenvolvedor solo, uma startup ou um pequeno time de operações, stackctl te entrega as mesmas práticas de segurança usadas em escala — sem a complexidade.

Instalar via curl (recomendado):

curl -fsSL https://eliasmeireles.com.br/tools/stackctl/install.sh | bash

Instalar uma versão específica:

curl -fsSL https://eliasmeireles.com.br/tools/stackctl/install.sh | bash -s v0.0.9

Instalar a partir do código (requer Go):

go install github.com/eliasmeireles/stackctl/cmd/stackctl@latest

Atualizar uma instalação existente:

# Última release estável (ignora pré-releases por padrão)
sudo stackctl self-update

# Fixar uma versão específica
sudo stackctl self-update --version v0.0.9

# Optar por um release candidate
sudo stackctl self-update --version v0.1.0-rc-05

Execute stackctl sem argumentos para abrir a TUI interativa.


Autenticação no Vault

Todos os comandos do Vault resolvem credenciais nesta ordem:

PrioridadeOrigem
1Flags da CLI: --addr, --token, --role-id/--secret-id, --k8s-role
2Variáveis de ambiente: VAULT_ADDR, VAULT_TOKEN, VAULT_ROLE_ID, VAULT_SECRET_ID, VAULT_K8S_ROLE
3Arquivo ~/.vault-token (gerado por vault login)
Método de authNecessário
TokenVAULT_ADDR + VAULT_TOKEN
AppRoleVAULT_ADDR + VAULT_ROLE_ID + VAULT_SECRET_ID
Kubernetes SAVAULT_ADDR + VAULT_K8S_ROLE (+ opcional VAULT_K8S_MOUNT_PATH, VAULT_SA_TOKEN_PATH)

Flags Globais

FlagPadrãoDescrição
--output / -otableFormato de saída: table, json, yaml

Quando --output json ou --output yaml é usado, emojis decorativos e mensagens de progresso são suprimidos para que a saída seja legível por máquina.

stackctl database postgres list --host localhost --admin-user postgres --admin-password secret --output json
stackctl vault secret list --output yaml

Contexto de Projeto — stackctl context

Evite repetir --host, --port, --admin-user em cada comando armazenando os padrões em um arquivo .stackctl.yaml. O arquivo é buscado hierarquicamente, do diretório atual até o seu home.

# Cria .stackctl.yaml interativamente no diretório atual
stackctl context init

# Mostra a configuração ativa
stackctl context show

Formato do .stackctl.yaml:

version: "1"
databases:
  postgres:
    host: localhost
    port: 5432
    user: postgres
    vault-login: secret/databases/postgres/admin   # opcional
  mysql:
    host: localhost
    port: 3306
    user: root
  mongodb:
    host: localhost
    port: 27017
    user: admin
messagebrokers:
  rabbitmq:
    host: localhost
    port: 5672
    user: guest

Nota: adicione .stackctl.yaml ao seu .gitignore — ele pode conter senhas ou paths de Vault.

Flags explícitas na CLI sempre sobrescrevem os padrões do contexto.


Comandos

TUI Interativa

stackctl

Navega por todas as funcionalidades através de um menu. Cada submenu mostra uma nota contextual explicando o passo atual, e as telas de input mostram o breadcrumb completo de navegação mais um contador de passos (step N of M).

Funcionalidades interativas principais:

  • Navegação por paths do Vault — percorra a árvore KV do Vault para escolher credenciais de admin em vez de digitar o caminho
  • Auto-gerar senha — digite auto ou auto:<tamanho> para gerar uma senha aleatória (impressa após sair da TUI)
  • Seleção de banco — escolha em uma lista numerada de bancos existentes ou digite um novo nome
  • KV engine ausente — criado automaticamente quando o destino de --vault-path ainda não existe

Tenta autenticar no Vault novamente a cada 5 segundos caso o token ainda não esteja disponível.

Personalização de cores da TUI (códigos de cor ANSI 256):

VariávelPadrãoControla
STACK_CTL_TITLE_COLOR86Título do menu
STACK_CTL_ITEM_COLOR86Itens da lista
STACK_CTL_SELECTED_ITEM_COLOR82Item selecionado

Kubeconfig — stackctl kubeconfig

SubcomandoDescrição
list-contextsLista todos os contextos locais
get-context <nome> [--encode]Imprime um contexto (opcionalmente em Base64)
set-context <nome>Troca o contexto atual
set-namespace <ns> [--context <nome>]Define o namespace padrão
cleanRemove entradas duplicadas
addImporta configuração (veja flags abaixo)
remove <nome>Remove um contexto
save-to-vault <nome>Envia o contexto para o Vault
add-from-vault <path>Baixa e mescla a partir do Vault
contextsLista os kubeconfigs guardados no Vault
from-saConstrói um kubeconfig a partir de um token de SA
apply -f <manifest>Executa um fluxo descrito em um manifest YAML
revert -f <manifest>Desfaz o que apply fez, usando o mesmo manifest

Flags do add:

FlagDescrição
<base64>Posicional: importa de uma string Base64
--file <path>Importa de um arquivo local
--host <ip> --ssh-user <user>Importa via SSH
--k3sUsa o caminho padrão do k3s (/etc/rancher/k3s/k3s.yaml)
-r <nome>Renomeia o contexto importado
stackctl kubeconfig add --k3s --host 192.168.1.10 --ssh-user root -r home-lab
stackctl kubeconfig save-to-vault home-lab
stackctl kubeconfig add-from-vault secret/data/kubeconfig/home-lab

Flags do from-sa:

FlagDescrição
--sa <nome>Nome da ServiceAccount (obrigatório)
--namespace <ns>Namespace onde a SA/Secret reside (padrão kube-system)
--secret <nome>Nome do Secret do token (padrão: <sa>-token)
--cluster-name <nome>Nome do cluster a embutir (padrão: kubernetes)
--context-name <nome>Nome do contexto (padrão: <sa>@<cluster-name>)
--default-namespaceNamespace padrão para o novo contexto
--server <url>Sobrescreve a URL do API server (padrão: lê do kubeconfig ativo)
--kube-context <nome>Contexto kube de onde ler server/CA (padrão: atual)
--output-file <path>Escreve o kubeconfig em um arquivo em vez de mesclar no kubeconfig ativo
# Mescla um kubeconfig para a SA dentro do kubeconfig ativo
stackctl kubeconfig from-sa \
  --sa <nome-sa> --secret <token-secret> \
  --cluster-name <nome-cluster> \
  --default-namespace <ns-padrao>

# Ou escreve em um arquivo separado (útil para entregar o kubeconfig a um colega)
stackctl kubeconfig from-sa --sa <nome-sa> --secret <token-secret> \
  --output-file ./<nome-sa>.kubeconfig

apply — fluxos de kubeconfig orientados a manifest

O discriminador kind escolhe o fluxo. Atualmente suportado:

KindComando equivalente da CLI
KubeconfigFromSAstackctl kubeconfig from-sa ...

O manifest é validado antes de qualquer chamada ao cluster — kind ausente, spec ausente ou um campo obrigatório do spec faltando aborta com um erro claro.

# kubeconfig-from-sa.yaml
apiVersion: stackctl/v1
kind: KubeconfigFromSA
spec:
  serviceAccount: dev-user           # obrigatório
  namespace: kube-system             # padrão: kube-system
  secret: dev-user-token             # padrão: <serviceAccount>-token
  clusterName: homelab               # padrão: kubernetes
  contextName: dev-user@homelab      # padrão: <sa>@<clusterName>
  defaultNamespace: homelab-dev      # padrão: default
  # server: https://10.0.0.1:6443    # override opcional
  # kubeContext: my-cluster          # opcional, usa o atual por padrão
  # outputFile: ./dev-user.kubeconfig  # opcional; mescla no kubeconfig ativo se vazio
stackctl kubeconfig apply  -f kubeconfig-from-sa.yaml   # avança
stackctl kubeconfig revert -f kubeconfig-from-sa.yaml   # desfaz usando o mesmo arquivo

revert usa o mesmo switch de kind do apply. Para KubeconfigFromSA, remove o contexto gerado (e entradas órfãs de cluster/usuário) do kubeconfig ativo, ou apaga o arquivo apontado por spec.outputFile quando definido. A operação é idempotente — um arquivo ou contexto ausente gera um aviso em vez de erro.

Há um exemplo funcional em example/kubeconfig-from-sa.yaml. Novos kinds serão adicionados aqui conforme forem lançados.


Vault — stackctl vault

Secrets

stackctl vault secret list [path]
stackctl vault secret get <path>
stackctl vault secret put <path> chave=valor [chave=valor ...]
stackctl vault secret delete <path>

Path padrão de listagem: secret/metadata/resources/kubeconfig

Policies

stackctl vault policy list
stackctl vault policy get <nome>
stackctl vault policy put <nome> <arquivo.hcl>
stackctl vault policy delete <nome>

Métodos de auth

stackctl vault auth list
stackctl vault auth enable <tipo> [--path <path>] [--description <desc>]
stackctl vault auth disable <path>

Engines de secrets

stackctl vault engine list
stackctl vault engine enable <tipo> [--path <path>] [--description <desc>]
stackctl vault engine disable <path>

Roles

stackctl vault role list <auth-mount>
stackctl vault role get <auth-mount> <nome>
stackctl vault role put <auth-mount> <nome> [flags]
stackctl vault role delete <auth-mount> <nome>

Flags do role put: --bound-sa-names, --bound-sa-namespaces, --policies, --token-policies, --ttl, --token-max-ttl, --secret-id-ttl, --secret-id-num-uses

Apply declarativo

stackctl vault apply   -f config.yaml      # valida primeiro; aborta em qualquer erro
stackctl vault revert  -f config.yaml      # desfaz o que foi aplicado
stackctl vault generate-manifest --all     # gera um template totalmente comentado

Aplica engines → auth → policies → roles → service_accounts → users → secrets → kubernetes nessa ordem. A validação roda antes de qualquer mudança ser feita — todos os problemas de schema do manifest são reportados de uma vez para que sejam corrigidos em uma única passada.

O bloco kubernetes: pode gerenciar declarativamente:

RecursoNotas
namespacesCria/atualiza com labels/annotations
registry_secretsSecrets dockerconfigjson, credenciais inline ou puxadas de um path KV do Vault
service_accountsServiceAccounts do Kubernetes com image pull secrets e controle de automount
secretsSecrets genéricos (Opaque por padrão) — suporta kubernetes.io/service-account-token
config_mapsConfigMaps com data arbitrário
role_bindingsRoleBinding namespaced para uma Role ou ClusterRole, múltiplos subjects
cluster_role_bindingsBinding em escopo de cluster para uma ClusterRole

Veja example/vault-config.yaml e example/homelab-rbac.yaml para referências funcionais.

Fetch (CI/CD)

Busca um secret e mescla como kubeconfig, ou exporta os campos como variáveis de ambiente. As flags de auth (--addr, --token, --role-id, etc.) são herdadas do comando pai vault.

stackctl vault fetch \
  --addr $VAULT_ADDR \
  --role-id $VAULT_ROLE_ID --secret-id $VAULT_SECRET_ID \
  --secret-path secret/data/ci/kubeconfig/prod \
  -r prod-cluster
FlagDescrição
--secret-pathPath KV v2 do secret
--secret-fieldCampo a ser lido (padrão: kubeconfig)
--as-kubeconfigMescla o valor do campo (Base64) no kubeconfig local (padrão)
--export-envExporta todos os campos como variáveis de ambiente
--github-envTambém escreve em $GITHUB_ENV
-rRenomeia o contexto na importação

Gerenciamento de secrets — stackctl get secret

Pega secrets do Vault e copia para a clipboard ou salva em arquivo. O valor do secret nunca é impresso no terminal.

Tratamento de paths: todos os paths recebem automaticamente o prefixo secret/data/ para compatibilidade com KV v2.

# Copia um secret para a clipboard
stackctl get secret <KEY>

# Pega de um path customizado (secret/data/ é prefixado automaticamente)
stackctl get secret <KEY> --path resources/vps/elias-oracle

# Salva em arquivo
stackctl get secret PUB_KEY --path resources/vps/elias-oracle --to-file ~/.ssh/id_rsa.pub

# Decodifica de base64 antes de salvar
stackctl get secret ENCODED_KEY --path apps/production --to-file ./decoded.txt --decode-from-b64

# Substitui um arquivo existente
stackctl get secret PUB_KEY --to-file ~/.ssh/id_rsa.pub --replace

Flags:

FlagDescrição
--path <path>Path do Vault (sem o prefixo secret/data/)
--to-file <arquivo>Salva o secret em arquivo em vez da clipboard
--decode-from-b64Decodifica o secret de base64 antes de salvar/copiar
--replaceSubstitui o arquivo se já existir (apenas com --to-file)
STACK_CTL_DEFAULT_SECRET_PATHVariável para definir o path padrão (sem o prefixo secret/data/)
(path padrão)users/all/passwords (vira secret/data/users/all/passwords)

Comandos de gestão de senhas:

# Adiciona uma senha (gerada automaticamente se --pass for omitido; também copiada para a clipboard)
stackctl add pass <KEY> [--pass <valor>] [--size <bytes>]

# Atualiza uma senha
stackctl update pass <KEY> [--pass <valor>] [--size <bytes>]

# Apaga uma senha
stackctl delete pass <KEY>

Generate — stackctl generate

Gera senhas e nomes de usuário aleatórios, automaticamente copiados para a clipboard.

# Gera uma senha aleatória (copiada para a clipboard)
stackctl generate password

# Gera uma senha de tamanho específico (bytes de entropia)
stackctl generate password --size 32

# Gera um username aleatório
stackctl generate username

# Imprime o valor em vez de copiar (útil em scripts)
stackctl generate password --output json

Quando a clipboard não está disponível (ex.: em CI/CD), o valor gerado é salvo em ~/.stackctl/pass.


VPN NetBird — stackctl netbird

stackctl netbird install
stackctl netbird up --netbird-key <key> [--api-host <host>] [--wait-dns]
stackctl netbird status
VariávelDescrição
STACK_CLT_NETBIRD_KEYSetup key
API_HOSTHost da API de gerenciamento (padrão: api.netbird.io)

Gerenciamento de Banco de Dados — stackctl database

Gerencie bancos, usuários, schemas e teste conexões (PostgreSQL, MySQL, MongoDB).

Os comandos seguem uma hierarquia que prioriza o tipo de banco: stackctl database {postgres|mysql|mongodb} {list|create|delete|test} ...

# Lista bancos e usuários
stackctl database postgres list \
  --host localhost \
  --admin-user postgres \
  --admin-password secret

# Cria um usuário (gera senha automaticamente; lista bancos existentes interativamente)
stackctl database postgres create user \
  --host localhost \
  --admin-user postgres \
  --admin-password secret \
  --username myapp_user \
  --password auto \
  --vault-path secret/databases/postgres/myapp_user

# Cria um usuário com senha e banco explícitos
stackctl database postgres create user \
  --vault-login secret/databases/postgres/admin \
  --username myapp_user \
  --password myapp_pass \
  --database myapp_db \
  --vault-path secret/databases/postgres/myapp_user

# Apaga um usuário — omita --username para ver uma lista numerada e selecionar interativamente
stackctl database postgres delete user \
  --host localhost \
  --admin-user postgres \
  --admin-password secret

# Apaga um usuário específico diretamente (pede confirmação por se tratar de ação irreversível)
stackctl database postgres delete user \
  --host localhost \
  --admin-user postgres \
  --admin-password secret \
  --username old_user

# Apaga um banco — omita --database para selecionar da lista; --force pula a confirmação
stackctl database postgres delete database \
  --host localhost \
  --admin-user postgres \
  --admin-password secret \
  --database old_db \
  --force

# Testa as credenciais de um usuário
stackctl database postgres test user \
  --host localhost \
  --username myapp_user \
  --password myapp_pass \
  --database myapp_db

Bancos suportados: PostgreSQL · MySQL · MongoDB

Veja DATABASE_COMMANDS.md para a referência completa de comandos.


Gerenciamento de Message Brokers — stackctl messagebroker

Gerencie usuários e credenciais de message broker (RabbitMQ).

# Cria um usuário no RabbitMQ
stackctl messagebroker rabbitmq create user \
  --host localhost \
  --admin-user admin \
  --admin-password secret \
  --username myapp_user \
  --password myapp_pass \
  --tags "administrator,management" \
  --vault-path secret/messagebroker/rabbitmq/myapp_user

# Lista todos os usuários
stackctl messagebroker rabbitmq list user \
  --host localhost \
  --admin-user admin \
  --admin-password secret

# Apaga um usuário — omita --username para ver uma lista numerada e selecionar interativamente
stackctl messagebroker rabbitmq delete user \
  --host localhost \
  --admin-user admin \
  --admin-password secret

# Testa as credenciais do usuário
stackctl messagebroker rabbitmq test-user \
  --host localhost \
  --username myapp_user \
  --password myapp_pass

Brokers suportados: RabbitMQ

Tags comuns do RabbitMQ: administrator · management · policymaker · monitoring

Veja MESSAGEBROKER_COMMANDS.md para a referência completa de comandos.


Self-update — stackctl self-update

Substitui o binário em execução pelo da última GitHub Release. Tags de pré-release são ignoradas por padrão; use --version para fixar uma versão ou para optar por um release candidate.

FlagPadrãoDescrição
--version(última estável)Tag específica para instalar. Ignora o filtro de só-estável.
--install-path/usr/local/bin/stackctlPath a ser substituído (defina se o stackctl está em outro lugar).
sudo stackctl self-update                          # última estável
sudo stackctl self-update --version v0.0.9         # fixa uma versão estável
sudo stackctl self-update --version v0.1.0-rc-05   # opta por um RC
stackctl self-update --install-path "$HOME/bin/stackctl"

O download cai em um arquivo temporário ao lado do diretório de instalação sempre que possível (para rename atômico), com fallback para sistemas de arquivo distintos (EXDEV) que copia o binário com segurança.


Exemplo de CI/CD (GitHub Actions)

- name: Instalar stackctl
  run: curl -fsSL https://eliasmeireles.com.br/tools/stackctl/install.sh | bash

- name: Conectar VPN
  run: |
    stackctl netbird install
    stackctl netbird up --netbird-key ${{ secrets.NETBIRD_KEY }} --wait-dns

- name: Buscar kubeconfig
  env:
    VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
    VAULT_ROLE_ID: ${{ secrets.VAULT_ROLE_ID }}
    VAULT_SECRET_ID: ${{ secrets.VAULT_SECRET_ID }}
  run: |
    stackctl vault fetch \
      --secret-path secret/data/ci/kubeconfig/prod \
      -r prod-cluster

- name: Deploy
  run: kubectl apply -f k8s/

Testes

Rodando os testes

# Roda todos os testes
make test

# Roda os testes com cobertura
go test -cover ./...

# Roda os testes de um pacote específico
go test ./cmd/stackctl/cmd/vault/...

Ambiente de Desenvolvimento Local

Para testes de integração e desenvolvimento local, você pode subir um ambiente completo de Vault + Kubernetes usando Multipass:

# Faz o bootstrap de um cluster k3s local com Vault
make multipass

# Isso cria uma VM com:
# - cluster Kubernetes k3s
# - HashiCorp Vault (auto-inicializado e desbloqueado)
# - NGINX Ingress Controller
# - CLI stackctl pré-instalada

Veja .dev/multipass/README.md para instruções detalhadas de setup e requisitos.


Contribuindo

Contribuições são muito bem-vindas! Sinta-se à vontade para abrir um Pull Request.