SSD Nodes Learn 🎉 VPS desde $5.50/mês
Guias Matt ConnorPor Matt Connor

Gestor de segredos self-hosted: qual escolher?

Compare OpenBao, Infisical, SOPS com age, credenciais do systemd e ficheiros env protegidos para escolher o gestor de segredos certo num único VPS e estimar o custo.

O que um gestor de segredos self-hosted faz que um gestor de palavras-passe não faz

Um gestor de segredos self-hosted entrega credenciais a processos. Um gestor de palavras-passe entrega credenciais a pessoas. Todo o resto resulta desta diferença. Um gestor de palavras-passe é desbloqueado por uma pessoa presente e atenta. Um gestor de segredos tem de fornecer a palavra-passe de uma base de dados à sua aplicação às 03:00, quando ninguém está acordado.

Os modos de falha são diferentes, e isso é importante. Um gestor de palavras-passe bloqueado é apenas um incómodo: introduz novamente a palavra-passe principal. Um gestor de segredos selado provoca uma interrupção: todos os serviços que reiniciarem enquanto ele estiver selado arrancam sem credenciais e permanecem inativos. Executar o Vaultwarden como o seu próprio gestor de palavras-passe resolve bem o problema das pessoas. Não resolve o problema das máquinas, e nunca foi concebido para isso.

As opções realistas para um único servidor dividem-se em dois grupos. OpenBao e Infisical são serviços: uma API, uma base de dados, TLS (transport layer security), uma etapa de início de sessão e um processo que terá de manter em execução. SOPS com age, credenciais do systemd e secrets do Docker são ficheiros: encriptados em repouso, desencriptados por algo que já está em execução, sem nada adicional para monitorizar.

A resposta honesta é esta. Para um único servidor com uma ou duas pessoas, as opções baseadas em ficheiros são normalmente as corretas. Um OpenBao que ninguém sela corretamente e cujas credenciais ninguém roda é pior do que um ficheiro de ambiente com mode 600, porque acrescenta um componente que pode falhar e uma cópia de segurança que será configurada incorretamente, sem oferecer uma rotação que já não estivesse a ser feita manualmente.

Um ficheiro env com modo 600 é suficiente?

Muitas vezes, sim. A ameaça contra a qual ele protege é outro utilizador no servidor ler a palavra-passe da base de dados. As permissões de ficheiros Unix fazem isso antes de a rede estar ativa.

sudo install -d -m 750 -o root -g myapp /etc/myapp
sudo install -m 640 -o root -g myapp /dev/null /etc/myapp/env
sudoedit /etc/myapp/env

Verifique o acesso a partir dos dois lados:

sudo -u myapp cat /etc/myapp/env
sudo -u nobody cat /etc/myapp/env

O primeiro comando mostra o conteúdo do ficheiro. O segundo mostra cat: /etc/myapp/env: Permission denied, porque nobody não pertence ao grupo myapp e o ficheiro não tem permissões para outros utilizadores. Esse é todo o modelo de segurança, e é um modelo real.

A fuga acontece a seguir. Uma unidade systemd com EnvironmentFile= copia esses valores para o ambiente do processo, e o ambiente do processo pode ser lido.

[Service]
User=myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/usr/local/bin/myapp
sudo cat /proc/$(pgrep -n myapp)/environ | tr '\0' '\n'

Esse comando mostra os seus segredos em texto simples, porque /proc/<pid>/environ pode ser lido por root e pelo utilizador com que o processo é executado. Um sistema de recolha de falhas que anexe o ambiente a um relatório verá a mesma coisa. O mesmo acontece com qualquer ferramenta executada na mesma conta. Por isso, manter os segredos fora dos agentes de IA começa por os retirar do ambiente. Associe o ficheiro a um utilizador de serviço dedicado e com poucos privilégios para que "o utilizador com que o processo é executado" não seja root.

SOPS com age: secrets encriptados que pode enviar para o git

O SOPS (secrets operations) encripta os valores de um ficheiro YAML ou JSON e mantém as chaves em texto simples. O age é uma ferramenta de encriptação pequena que fornece um par de chaves e não requer um servidor de chaves. Em conjunto, permitem-lhe enviar secrets.enc.yaml para o repositório juntamente com o código. O git diff continua a indicar que definição foi alterada, sem mostrar ao leitor para que valor foi alterada.

O age está disponível no Ubuntu 24.04. O SOPS não está, por isso, descarregue o .deb a partir da página de releases. A versão 3.13.3 era a atual em August 2026.

sudo apt update && sudo apt install -y age
curl -LO https://github.com/getsops/sops/releases/download/v3.13.3/sops_3.13.3_amd64.deb
sudo apt install -y ./sops_3.13.3_amd64.deb
sops --version

Gere um par de chaves. age-keygen escreve a chave privada no ficheiro e apresenta a chave pública, por isso verá uma linha que começa por Public key: age1....

mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
chmod 600 ~/.config/sops/age/keys.txt
age-keygen -y ~/.config/sops/age/keys.txt

Coloque a chave pública em .sops.yaml, na raiz do repositório, para não ter de memorizar o destinatário na linha de comandos.

creation_rules:
  - age: age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c
sops encrypt secrets.yaml > secrets.enc.yaml
sops decrypt secrets.enc.yaml

Uma regra sem path_regex corresponde a tudo, que é o comportamento pretendido inicialmente. Se adicionar uma regra mais tarde, escreva-a para corresponder ao ficheiro que passa a sops, porque as regras são verificadas com base no caminho de entrada e não no ficheiro para o qual redireciona a saída.

Em runtime, entregue os valores a um único processo e a mais nenhum:

sops exec-env secrets.enc.yaml './myapp'

sops exec-env desencripta os valores na memória e define-os no ambiente do processo filho, por isso nenhum texto simples é escrito no disco. A ressalva sobre o ambiente da secção anterior continua a aplicar-se a esse processo filho.

Há dois problemas frequentes neste ponto. O erro Failed to get the data key required to decrypt the SOPS file no systemd quase sempre significa que o SOPS procurou no diretório home errado, porque uma unidade não herda o seu HOME. Defina explicitamente o caminho com Environment=SOPS_AGE_KEY_FILE=/etc/sops/age.txt na unidade. Separadamente, editar .sops.yaml não volta a encriptar nada do que já existe: adicionar a chave pública de um colega afeta apenas ficheiros novos, por isso execute sops updatekeys secrets.enc.yaml em cada ficheiro existente. Se a sua configuração já usa Ansible, encriptar os mesmos valores com o Ansible Vault produz o mesmo resultado sem uma segunda ferramenta.

credenciais do systemd: segredos que nunca chegam ao ambiente

O Ubuntu 24.04 inclui o systemd 255, portanto não é necessário instalar nada. systemd-creds encripta um segredo no host, e o systemd desencripta-o num diretório privado que apenas esse serviço pode ler.

sudo systemd-creds setup
sudo install -d -m 700 /etc/myapp
echo -n 'hunter2' | sudo systemd-creds encrypt --name=db_password - /etc/myapp/db_password.cred
[Service]
User=myapp
LoadCredentialEncrypted=db_password:/etc/myapp/db_password.cred
ExecStart=/usr/local/bin/myapp

O serviço lê o valor a partir de um ficheiro chamado db_password dentro do diretório indicado por $CREDENTIALS_DIRECTORY. O valor não está no ambiente, portanto /proc/<pid>/environ não mostra nada útil, e o texto simples nunca é gravado no sistema de ficheiros raiz.

Verifique se o ficheiro é desencriptado antes de o associar a uma unidade:

sudo systemd-creds decrypt /etc/myapp/db_password.cred -

Saiba qual chave foi usada para o encriptar, porque isso determina se a sua cópia de segurança será útil. O --with-key=auto predefinido usa o chip TPM2 (trusted platform module version 2) quando este está presente e utilizável; caso contrário, usa a chave do host. A maioria das instâncias VPS não tem TPM2.

systemd-analyze has-tpm2

no significa que a chave do host foi usada, e essa chave está em /var/lib/systemd/credential.secret, legível apenas por root. Restaure db_password.cred num VPS novo sem esse ficheiro e nada o desencripta, nunca. Copie credential.secret para a mesma cópia de segurança ou mantenha o texto simples num local a que ainda consiga aceder.

Segredos do Docker: ficheiros em /run/secrets

O Compose lê um ficheiro do host e monta-o no contentor em /run/secrets/<name>.

services:
  app:
    image: myapp:latest
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt
docker compose exec app cat /run/secrets/db_password
docker compose exec app env | grep -i password

O primeiro comando imprime o segredo. O segundo imprime apenas DB_PASSWORD_FILE=/run/secrets/db_password, que é o objetivo: o valor nunca fica no ambiente do contentor, por isso não aparece na saída de docker inspect. Muitas imagens oficiais já esperam este formato, e a imagem do Postgres lê POSTGRES_PASSWORD_FILE exatamente desta forma.

É importante perceber o que isto significa. Fora do modo Swarm, não existe encriptação em nenhuma camada: ./db_password.txt é um ficheiro de texto simples no host, e a única proteção é o seu modo e proprietário. Defina ambos manualmente, porque o Compose monta um ficheiro legível por todos sem apresentar qualquer aviso. O conjunto mais amplo de compromissos face ao atalho simples env_file está no guia sobre ficheiros env e segredos do Compose.

O custo real de executar OpenBao e Vault

OpenBao é o fork do HashiCorp Vault mantido pela Linux Foundation, iniciado depois de a HashiCorp relicenciar o Vault sob a Business Source License em 2023. O OpenBao continua sob a MPL 2.0 (Mozilla Public License). A versão 2.6.2 era a atual em agosto de 2026. Quase tudo o que se segue também se aplica ao Vault, porque o fork manteve a mesma interface de comandos.

docker pull docker.io/openbao/openbao

Os pacotes para Debian e Ubuntu estão disponíveis na página de downloads do OpenBao, caso prefira gerir as atualizações com o apt. O servidor precisa de um ficheiro de configuração que contenha um listener e um backend de armazenamento:

listener "tcp" {
  address       = "127.0.0.1:8200"
  tls_cert_file = "/path/to/full-chain.pem"
  tls_key_file  = "/path/to/private-key.pem"
}

storage "raft" {
  path = "/path/to/raft/data"
  node_id = "raft_node_1"
}

Depois, inicie-o uma vez:

bao operator init

Por predefinição, isto divide a root key em 5 partes e exige 3 delas para fazer unseal. Essas opções são os flags -key-shares e -key-threshold. O comando apresenta as partes e o token root inicial uma única vez e nunca mais os apresenta.

Agora, a parte que a maioria das comparações ignora. Um servidor reiniciado é um servidor sealed. O OpenBao mantém a root key apenas na memória. Por isso, depois de um reinício, não consegue desencriptar o próprio armazenamento até alguém fornecer o número mínimo de partes. Uma atualização do kernel ou uma eliminação por falta de memória termina, portanto, com um servidor sealed e aplicações que não conseguem iniciar sessão.

Num VPS usado por uma só pessoa, a divisão de Shamir não oferece proteção, porque as 5 partes acabam no mesmo gestor de palavras-passe pertencente à mesma pessoa. O auto unseal transfere a key para um dispositivo ou serviço fidedigno. Numa cloud de grande escala, isso normalmente significa um serviço gerido de keys. No seu VPS, normalmente significa um ficheiro de key no mesmo disco que os dados que protege. Isto reduz efetivamente a segurança, em troca de um servidor que volta a funcionar sozinho depois de um reinício. Faça esta escolha conscientemente e registe qual das opções adotou.

Infisical: uma interface web, uma base de dados e uma chave mestra que continua na sua posse

Infisical é uma plataforma de secrets com interface web, projetos, ambientes e controlo de acesso por utilizador. A instalação self-hosted com Compose é curta:

curl -o docker-compose.prod.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.prod.yml
curl -o .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example
docker compose -f docker-compose.prod.yml up -d

Edite .env antes desse último comando. Dois valores têm de ser seus, e um deles nunca pode mudar depois:

openssl rand -hex 16
openssl rand -base64 32

O primeiro é ENCRYPTION_KEY, uma string hexadecimal de 16 bytes. É a chave usada para encriptar os seus secrets dentro do PostgreSQL. Se a perder, um backup perfeito da base de dados transforma-se num conjunto de ciphertext. Se a alterar numa instância em execução, os secrets existentes deixam de ser desencriptados. O segundo é AUTH_SECRET, uma string base64 de 32 bytes usada nas sessões. SITE_URL tem de ser o URL absoluto que irá realmente utilizar, incluindo o protocolo. Caso contrário, o redirecionamento do login falha.

Infisical é mais adequado do que OpenBao quando aquilo de que realmente precisa são utilizadores: uma interface web para uma equipa pequena e separação entre ambientes, em vez de credenciais de base de dados que expiram automaticamente. Em contrapartida, terá de manter e fazer backup do PostgreSQL, do Redis e de um certificado TLS.

O que acontece quando o serviço de segredos está indisponível e a aplicação reinicia

Esta pergunta determina se um serviço de segredos deve ficar num único servidor. Os ficheiros podem ser lidos antes de a rede arrancar. Um serviço não.

Reinicie o servidor e a aplicação e o OpenBao arrancará no mesmo momento. A aplicação pede a palavra-passe da base de dados, o OpenBao ainda está selado, o pedido falha e o systemd reinicia a aplicação em ciclo até uma pessoa introduzir manualmente as partes necessárias para abrir o selo. Nada está avariado. Mas também nada está operacional.

Há duas formas corretas de tratar esta situação. Ordene as unidades e permita que a aplicação tente novamente: After= o serviço de segredos, além de Restart=on-failure e de um RestartSec= suficientemente longo para não sobrecarregar a API. Ou obtenha o segredo durante a implementação, em vez do arranque: grave-o num ficheiro com modo 600 ou numa credencial do systemd, para que o sistema em execução dependa de um ficheiro e não de uma API.

A expiração do token é o mesmo problema, mas com um relógio mais lento. Os tokens e os leases do OpenBao têm um tempo de vida. Por isso, um processo de longa duração que nunca os renova perde o acesso num momento sem relação com qualquer implementação. Essa falha é confusa precisamente porque nada mudou nesse dia.

Fazer backup do próprio armazenamento

Todas as opções aqui têm uma chave, e um backup sem essa chave não tem utilidade. Anote onde ela está guardada.

Num ficheiro de ambiente, o próprio ficheiro é o segredo, por isso o backup tem de ser encriptado. No SOPS, o ficheiro encriptado pode ficar em qualquer local público, mas a chave privada age em ~/.config/sops/age/keys.txt é o elemento que não pode perder. Para credenciais do systemd, faça backup de /var/lib/systemd/credential.secret juntamente com os ficheiros .cred. No Infisical, faça um dump do PostgreSQL e guarde ENCRYPTION_KEY num local separado.

O OpenBao com armazenamento raft cria o seu próprio snapshot:

bao operator raft snapshot save backup.snap
bao operator raft snapshot restore backup.snap

O snapshot contém o armazenamento encriptado, por isso a restauração num servidor novo continua a exigir os unseal shares de bao operator init. Um job noturno que copia snapshots para o armazenamento de objetos, enquanto os shares não são guardados em lado nenhum, não é um backup de nada. Teste a restauração numa VPS descartável antes de depender dela.

Registo de auditoria: quem leu que segredo

Os ficheiros não fornecem uma trilha de auditoria. O modo e o proprietário indicam quem poderia ler o segredo. Nunca indicam quem o leu. auditd com uma monitorização do caminho é o substituto mais próximo e informa que um ficheiro foi aberto, mas não qual valor foi utilizado.

O OpenBao regista cada pedido num dispositivo de auditoria que tem de ativar explicitamente:

bao audit enable file file_path=/var/log/openbao_audit.log

Há dois factos sobre esse registo que alteram a forma como administra o servidor. A maioria das cadeias de caracteres nos pedidos e nas respostas é submetida a HMAC-SHA256 com um salt. Assim, pode comparar um valor que já conhece com o registo sem que este contenha o texto simples. Os inteiros e os booleanos são escritos sem alteração, por isso um segredo numérico não obtém proteção através desse hashing.

Existe ainda uma armadilha operacional: o OpenBao não responde aos pedidos quando nenhum dispositivo de auditoria ativado os consegue registar. Um dispositivo que falhe de forma bloqueante faz os pedidos ficarem pendentes até alguém corrigir o problema. Um disco cheio em /var/log coloca a API de segredos indisponível por conceção. Reserve espaço próprio para o registo de auditoria e configure uma regra do logrotate logo no primeiro dia, não depois da primeira indisponibilidade.

Qual gerenciador de segredos self-hosted deve executar?

Conte as máquinas e conte as pessoas. Depois escolha.

  1. Uma máquina, uma pessoa: use um ficheiro de ambiente com modo 600, pertencente a root e lido por um utilizador de serviço. Adicione credenciais do systemd quando quiser retirar o valor do ambiente do processo.
  2. Uma máquina, duas a cinco pessoas, com a configuração já no git: use SOPS com age. Cada pessoa recebe um par de chaves, e .sops.yaml lista todas as chaves públicas autorizadas a desencriptar.
  3. Várias máquinas, um repositório de configuração e sem necessidade de credenciais com validade: continue a usar SOPS com age, com uma chave destinatária por host, para que uma chave de host roubada desencripte apenas os ficheiros desse host.
  4. Várias máquinas e várias equipas que realmente precisam de credenciais de base de dados com validade, além de um registo de auditoria que alguém consulte: use OpenBao e reserve no orçamento uma hora por mês do tempo do operador para procedimentos de unseal e exercícios de restauração.

A regra subjacente aos quatro casos é a mesma. Execute a solução mais pequena que cumpra um requisito que consiga declarar claramente, porque um gerenciador de segredos indisponível é indistinguível de um gerenciador de segredos vazio.

FAQ

Um gestor de segredos self-hosted vale a pena para um único VPS?

Normalmente, não, se estiver a falar de um serviço como OpenBao ou Infisical. Num único servidor, com uma ou duas pessoas, um ficheiro de ambiente com permissões mode 600 ou uma credencial encriptada do systemd oferece a mesma proteção contra outro utilizador local, sem uma etapa de unseal e sem outro serviço para atualizar. Um serviço de segredos começa a compensar quando existem várias máquinas e várias pessoas, ou quando há uma necessidade real de credenciais que expirem sem que alguém tenha de as rodar manualmente.

Qual é a diferença entre um gestor de palavras-passe e um gestor de segredos?

Um gestor de palavras-passe armazena credenciais introduzidas por uma pessoa, e uma pessoa desbloqueia-o enquanto está presente. Um gestor de segredos fornece credenciais a processos, por isso tem de funcionar às 03:00 sem ninguém a monitorizá-lo. Esta é a diferença essencial: um gestor de palavras-passe bloqueado obriga a introduzir novamente uma palavra-passe principal, enquanto um gestor de segredos selado interrompe todos os serviços que reiniciem enquanto permanecer selado.

O que acontece às minhas aplicações se o OpenBao ficar selado depois de um reboot?

Não conseguem obter os segredos, por isso falham ao iniciar, e o systemd reinicia-as num ciclo até alguém fornecer o limiar de unseal, que por predefinição é de 3 de 5 partes. O OpenBao mantém a root key apenas na memória, por isso cada reinício volta a selá-lo. Ative o auto unseal, aceitando que, num único VPS, a unseal key acaba no mesmo disco que os dados, ou grave os segredos num ficheiro durante o deploy para que o arranque nunca dependa da API.

Posso fazer commit de ficheiros encriptados pelo SOPS num repositório público?

Os valores estão encriptados, por isso estão protegidos contra qualquer pessoa sem a age private key. As chaves não estão encriptadas: um leitor consegue ver que tem STRIPE_SECRET_KEY e SMTP_PASSWORD, e com que frequência cada uma muda. Esses metadados são aceitáveis para a maioria dos projetos e inaceitáveis para alguns. Mantenha a age private key fora do repositório e execute sops updatekeys em todos os ficheiros existentes sempre que adicionar ou remover um destinatário.