Secrets em produção: Doppler e HashiCorp Vault
Hardcoded secrets em código e .env no servidor são os vetores de vazamento mais comuns. Gerenciadores de secrets como Doppler e HashiCorp Vault centralizam o controle, criptografam em repouso, auditam cada acesso e permitem rotação sem downtime — eliminando a dependência de arquivos .env espalhados por servidores.
Doppler: secrets como variáveis de ambiente
Injetar secrets automaticamente sem arquivos .env:
# Instalar Doppler CLI no VPS:
(curl -Ls --tlsv1.2 --proto "=https" -o install.sh https://cli.doppler.com/install.sh) && sudo sh install.sh
# Autenticar (em desenvolvimento, usa browser; em CI/CD, usa service token):
doppler login
# Configurar projeto no VPS:
doppler setup --project minha-api --config prd
# Executar aplicação com secrets injetados como env vars:
doppler run -- node server.js
# ou:
doppler run -- npm start
# Verificar quais secrets serão injetados:
doppler secrets --only-names
# Configurar como serviço systemd que injeta secrets:
sudo tee /etc/systemd/system/minha-api.service << 'EOF'
[Unit]
Description=Minha API Node.js
After=network.target
[Service]
User=nodejs
WorkingDirectory=/app
ExecStart=/usr/local/bin/doppler run -- node server.js
Restart=always
RestartSec=5
# Token do Doppler para autenticação no servidor:
Environment=DOPPLER_TOKEN=dp.st.prd.xxxxxxxxxxxxxxxxxxxx
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload && sudo systemctl enable minha-apiHashiCorp Vault: gerenciamento enterprise de secrets
Instalar e configurar Vault para gerenciamento avançado:
# Instalar HashiCorp Vault no Ubuntu:
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
# Configuração básica do Vault:
sudo tee /etc/vault.d/vault.hcl << 'EOF'
ui = true
storage "file" {
path = "/opt/vault/data"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = "true" # Em produção: usar TLS
}
api_addr = "http://127.0.0.1:8200"
EOF
sudo systemctl enable vault && sudo systemctl start vault
# Inicializar o Vault (primeira vez):
export VAULT_ADDR='http://127.0.0.1:8200'
vault operator init -key-shares=3 -key-threshold=2
# Salva as 3 unseal keys e o root token em local SEGURO
# Desselar (necessário após cada restart):
vault operator unseal UNSEAL_KEY_1
vault operator unseal UNSEAL_KEY_2Armazenar e ler secrets no Vault
Usar o KV secrets engine para armazenar credenciais:
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='s.xxxxxxxxxxxxxxxxxxxxxxxx' # root token
# Habilitar KV secrets engine versão 2:
vault secrets enable -path=minha-api kv-v2
# Armazenar secrets:
vault kv put minha-api/producao DATABASE_URL="postgresql://user:senha@localhost/db" JWT_SECRET="$(openssl rand -base64 32)" REDIS_URL="redis://localhost:6379" STRIPE_SECRET_KEY="sk_live_xxxxxxxxx"
# Ler secrets:
vault kv get minha-api/producao
vault kv get -field=DATABASE_URL minha-api/producao
# Versionar secrets (KV v2 mantém histórico):
vault kv put minha-api/producao JWT_SECRET="novo-secret"
vault kv get -version=1 minha-api/producao # versão anterior
# Criar AppRole para autenticação da aplicação (sem root token):
vault auth enable approle
vault policy write minha-api-policy - << 'EOF'
path "minha-api/data/producao" {
capabilities = ["read"]
}
EOF
vault write auth/approle/role/minha-api token_policies="minha-api-policy" token_ttl=1h token_max_ttl=4h
# Obter credentials da aplicação:
vault read auth/approle/role/minha-api/role-id
vault write -f auth/approle/role/minha-api/secret-idLer secrets do Vault na aplicação Node.js
Integrar Vault com aplicação Node.js para injetar secrets em startup:
// npm install node-vault
import vault from 'node-vault'
async function carregarSecrets(): Promise<Record<string, string>> {
const client = vault({
apiVersion: 'v1',
endpoint: process.env.VAULT_ADDR || 'http://127.0.0.1:8200',
})
// Autenticar via AppRole:
const auth = await client.approleLogin({
role_id: process.env.VAULT_ROLE_ID!,
secret_id: process.env.VAULT_SECRET_ID!,
})
client.token = auth.auth.client_token
// Ler secrets:
const { data } = await client.read('minha-api/data/producao')
return data.data
}
// Inicialização da aplicação:
async function main() {
const secrets = await carregarSecrets()
process.env.DATABASE_URL = secrets.DATABASE_URL
process.env.JWT_SECRET = secrets.JWT_SECRET
process.env.REDIS_URL = secrets.REDIS_URL
// Iniciar a aplicação após injetar os secrets:
const app = await criarApp()
app.listen(3000)
}
// Rotação de secrets sem restart (Vault lease renew):
// O token tem TTL de 1h — renovar antes de expirar:
setInterval(async () => {
try {
await client.tokenRenewSelf()
} catch {
// Reutenticar se o token não puder ser renovado:
const secrets = await carregarSecrets()
atualizarSecrets(secrets)
}
}, 30 * 60 * 1000) // renovar a cada 30 minutosBoas práticas de segurança para .env em produção
Alternativas simples quando Vault/Doppler são excesso para o projeto:
# Prática mínima se .env for necessário:
# 1. Nunca commitar .env no git:
echo ".env" >> .gitignore
echo ".env.*" >> .gitignore
echo "*.env" >> .gitignore
# 2. Permissões restritas no arquivo:
chmod 600 /app/.env # apenas o dono pode ler/escrever
chown nodejs:nodejs /app/.env # dono = usuário da aplicação
# 3. .env fora do diretório da aplicação:
# /etc/minha-api/env (não dentro de /app que pode ter outros arquivos)
chmod 640 /etc/minha-api/env
chown root:nodejs /etc/minha-api/env
# 4. Usar systemd EnvironmentFile para carregar sem .env no diretório da app:
# /etc/systemd/system/minha-api.service:
# [Service]
# EnvironmentFile=/etc/minha-api/env
# ExecStart=/usr/bin/node /app/server.js
# 5. Verificar se secrets não estão em processo ou logs:
ps aux | grep node # não deve mostrar DATABASE_URL=... como argumento
# Logs não devem logar objetos req (que podem conter Authorization headers)
# 6. Rotação: quando rotacionar uma credencial:
# Atualizar .env → reload da aplicação sem downtime:
sudo systemctl reload minha-api # envia SIGHUP, mais suave que restart$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS Linux em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.
Perguntas frequentes
Conteúdos relacionados