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:
| Prioridade | Origem |
|---|---|
| 1 | Flags da CLI: --addr, --token, --role-id/--secret-id, --k8s-role |
| 2 | Variáveis de ambiente: VAULT_ADDR, VAULT_TOKEN, VAULT_ROLE_ID, VAULT_SECRET_ID, VAULT_K8S_ROLE |
| 3 | Arquivo ~/.vault-token (gerado por vault login) |
| Método de auth | Necessário |
|---|---|
| Token | VAULT_ADDR + VAULT_TOKEN |
| AppRole | VAULT_ADDR + VAULT_ROLE_ID + VAULT_SECRET_ID |
| Kubernetes SA | VAULT_ADDR + VAULT_K8S_ROLE (+ opcional VAULT_K8S_MOUNT_PATH, VAULT_SA_TOKEN_PATH) |
Flags Globais
| Flag | Padrão | Descrição |
|---|---|---|
--output / -o | table | Formato 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.yamlao 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
autoouauto:<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-pathainda 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ável | Padrão | Controla |
|---|---|---|
STACK_CTL_TITLE_COLOR | 86 | Título do menu |
STACK_CTL_ITEM_COLOR | 86 | Itens da lista |
STACK_CTL_SELECTED_ITEM_COLOR | 82 | Item selecionado |
Kubeconfig — stackctl kubeconfig
| Subcomando | Descrição |
|---|---|
list-contexts | Lista 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 |
clean | Remove entradas duplicadas |
add | Importa 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 |
contexts | Lista os kubeconfigs guardados no Vault |
from-sa | Constró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:
| Flag | Descrição |
|---|---|
<base64> | Posicional: importa de uma string Base64 |
--file <path> | Importa de um arquivo local |
--host <ip> --ssh-user <user> | Importa via SSH |
--k3s | Usa 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:
| Flag | Descriçã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-namespace | Namespace 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:
| Kind | Comando equivalente da CLI |
|---|---|
KubeconfigFromSA | stackctl 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:
| Recurso | Notas |
|---|---|
namespaces | Cria/atualiza com labels/annotations |
registry_secrets | Secrets dockerconfigjson, credenciais inline ou puxadas de um path KV do Vault |
service_accounts | ServiceAccounts do Kubernetes com image pull secrets e controle de automount |
secrets | Secrets genéricos (Opaque por padrão) — suporta kubernetes.io/service-account-token |
config_maps | ConfigMaps com data arbitrário |
role_bindings | RoleBinding namespaced para uma Role ou ClusterRole, múltiplos subjects |
cluster_role_bindings | Binding 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
| Flag | Descrição |
|---|---|
--secret-path | Path KV v2 do secret |
--secret-field | Campo a ser lido (padrão: kubeconfig) |
--as-kubeconfig | Mescla o valor do campo (Base64) no kubeconfig local (padrão) |
--export-env | Exporta todos os campos como variáveis de ambiente |
--github-env | Também escreve em $GITHUB_ENV |
-r | Renomeia 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:
| Flag | Descriçã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-b64 | Decodifica o secret de base64 antes de salvar/copiar |
--replace | Substitui o arquivo se já existir (apenas com --to-file) |
STACK_CTL_DEFAULT_SECRET_PATH | Variá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ável | Descrição |
|---|---|
STACK_CLT_NETBIRD_KEY | Setup key |
API_HOST | Host 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.
| Flag | Padrão | Descrição |
|---|---|---|
--version | (última estável) | Tag específica para instalar. Ignora o filtro de só-estável. |
--install-path | /usr/local/bin/stackctl | Path 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.