SOPS + Age: Gestão de Segredos para IaC na PME
Gerir segredos — palavras-passe, chaves de API, tokens, certificados — em repositórios Git é um dos maiores desafios de segurança em infraestrutura como código (IaC). A maioria das pequenas e médias empresas (PME) recorre a soluções inadequadas: ficheiros .env em .gitignore, variáveis de ambiente partilhadas por mensagens, ou até segredos em texto simples commitados por engano. O SOPS (Secrets OPerationS), originalmente da Mozilla e agora mantido pela comunidade, em conjunto com o Age como backend de encriptação, oferece uma alternativa leve e auditável ao HashiCorp Vault, perfeitamente adequada a equipas pequenas que precisam de protecção real sem infraestrutura adicional.
ℹ Nota: O SOPS foi originalmente desenvolvido pela Mozilla e transferido para a comunidade em 2023. O repositório oficial encontra-se em github.com/getsops/sops. O Age é desenvolvido por Filippo Valsorda em github.com/FiloSottile/age.
Neste artigo
- SOPS vs Vault vs git-crypt
- Instalar SOPS e Age
- Gerar chaves Age
- Encriptar ficheiros com SOPS
- Integração com Ansible
- Integração com OpenTofu
- Boas Práticas
SOPS vs Vault vs git-crypt
Existem várias abordagens para gerir segredos em IaC. O quadro seguinte compara as três soluções mais comuns para PME:
| Critério | SOPS + Age | HashiCorp Vault | git-crypt |
|---|---|---|---|
| Complexidade | Baixa — binário único | Alta — servidor dedicado | Mínima |
| Encriptação granular | Sim — por chave/valor | Sim — via policies | Não — ficheiro completo |
| Formatos suportados | YAML, JSON, ENV, INI | KV store dinâmico | Qualquer ficheiro binário |
| Rotação de chaves | Manual, via re-encrypt | Automática | Difícil |
| Deps de infraestrutura | Nenhuma | Servidor + storage backend | Nenhuma |
| Auditoria | Git history + diff visível | Audit log dedicado | Git history (difícil de ler) |
O git-crypt encripta ficheiros inteiros, o que significa que um git diff mostra apenas “ficheiro encriptado” — impossível auditar o que mudou. O Vault é poderoso mas exige um servidor sempre disponível, TLS, policies e armazenamento persistente. Para uma PME com 5 a 50 servidores, o Vault é frequentemente desproporcional.
O SOPS resolve o problema de forma elegante: encripta apenas os valores em ficheiros YAML/JSON, mantendo as chaves legíveis. Isto permite ver a estrutura do ficheiro no git diff e perceber imediatamente qual segredo foi alterado, sem nunca o revelar.
⚠ Atenção: Nunca armazene segredos em texto simples no Git, mesmo em repositórios privados. Um repositório privado comprometido, um fork acidental ou um log de CI exposto podem revelar todos os segredos. O SOPS + Age garante que mesmo que o repositório seja acedido, os segredos permanecem encriptados.
Instalar SOPS e Age
Ambas as ferramentas são binários simples sem dependências. A instalação é rápida em qualquer distribuição Linux.
Instalar o Age
O Age é o motor de encriptação. Transfere-se o binário pré-compilado do GitHub:
# Descarregar a versão mais recente do Age
AGE_VERSION="1.2.1"
wget "https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-linux-amd64.tar.gz"
tar xzf "age-v${AGE_VERSION}-linux-amd64.tar.gz"
sudo mv age/age /usr/local/bin/
sudo mv age/age-keygen /usr/local/bin/
# Verificar a instalação
age --version
age-keygen --version
Instalar o SOPS
O SOPS pode ser instalado a partir do repositório oficial ou via gestor de pacotes:
# Opção 1: Descarregar binário do GitHub
SOPS_VERSION="3.9.2"
wget "https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64"
sudo mv "sops-v${SOPS_VERSION}.linux.amd64" /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops
# Opção 2: Via Homebrew (macOS ou Linuxbrew)
brew install sops age
# Verificar a instalação
sops --version
Em Debian/Ubuntu, o SOPS também está disponível via repositório oficial com .deb:
# Ubuntu/Debian via repositório oficial
curl -fsSL https://packages.getsops.io/github.key | sudo gpg --dearmor -o /usr/share/keyrings/sops-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/sops-archive-keyring.gpg] https://packages.getsops.io/apt stable main" | sudo tee /etc/apt/sources.list.d/sops.list
sudo apt update && sudo apt install sops
ℹ Dica: O SOPS suporta vários backends de encriptação: GPG, AWS KMS, Google Cloud KMS, Azure Key Vault e Age. Para PME sem infraestrutura cloud, o Age é a escolha ideal por ser simples, rápido e não requerer serviços externos.
Gerar chaves Age
O Age usa criptografia assimétrica: uma chave pública para encriptar e uma chave privada para desencriptar. A chave privada deve ser guardada em segredo; a chave pública pode ser partilhada (por exemplo, em CI/CD).
# Gerar um par de chaves Age
age-keygen -o age-key.txt
# Output:
# # created: 2026-08-03T12:00:00Z
# # public key: age1ql3z7hjy54pw3hyzm5xc...
# AGE-SECRET-KEY-1QZP...
# A chave pública pode ser mostrada novamente
age-keygen -y age-key.txt
A chave privada (o ficheiro age-key.txt) deve ser guardada num local seguro e nunca commitada no Git. Uma boa prática é armazená-la num gestor de palavras-passe (ex: KeePassXC, Bitwarden) ou numa pen USB encriptada.
O SOPS precisa de saber onde está a chave privada. Define-se via variável de ambiente:
# Indicar ao SOPS onde está a chave privada do Age
export SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt"
# Em CI/CD, usar uma secret com o conteúdo da chave
# export SOPS_AGE_KEY_FILE="/tmp/age-key.txt"
# echo "$AGE_SECRET_KEY" > "$SOPS_AGE_KEY_FILE"
# chmod 600 "$SOPS_AGE_KEY_FILE"
⚠ Crítico: Se a chave privada for perdida, todos os segredos encriptados com ela tornam-se irrecuperáveis. Gere sempre uma cópia de segurança offline da chave e considere usar múltiplas chaves (uma por ambiente) para limitar o impacto de uma chave comprometida.
Encriptar ficheiros com SOPS
O fluxo típico é: cria-se o ficheiro em texto simples, encripta-se com SOPS e commita-se a versão encriptada. Vamos ver um exemplo com YAML.
Exemplo: ficheiro de segredos YAML
Cria-se um ficheiro secrets.yaml em texto simples:
# secrets.yaml — texto simples (NÃO commitar)
database:
host: db.exemplo.pt
port: 5432
user: admin
password: SuperSecreta123!
api:
token: ghp_AbcDefGhiJklMnoPqr
endpoint: https://api.exemplo.pt/v2
Encripta-se com SOPS usando a chave pública do Age:
# Encriptar o ficheiro com SOPS + Age
sops --encrypt --age age1ql3z7hjy54pw3hyzm5xc... \
--in-place secrets.yaml
# O ficheiro fica assim (valores encriptados, chaves legíveis):
# database:
# host: db.exemplo.pt
# port: 5432
# user: ENC[AES256_GCM,data:...,type:str]
# password: ENC[AES256_GCM,data:...,type:str]
# api:
# token: ENC[AES256_GCM,data:...,type=str]
# endpoint: https://api.exemplo.pt/v2
# sops:
# age:
# - recipient: age1ql3z7hjy54pw3hyzm5xc...
# ...
Repara: apenas os valores sensíveis são encriptados (user, password, token). As chaves e valores não sensíveis (host, port, endpoint) permanecem legíveis. O SOPS adiciona um bloco sops: no fim com os metadados de encriptação.
Para desencriptar e visualizar:
# Desencriptar e mostrar no terminal
sops --decrypt secrets.yaml
# Desencriptar para um novo ficheiro
sops --decrypt secrets.yaml > secrets-decrypt.txt
# Editar o ficheiro encriptado (desencripta, abre editor, re-encripta)
sops secrets.yaml
Ficheiro .sops.yaml
O SOPS suporta um ficheiro de configuração .sops.yaml na raiz do repositório para definir regras de encriptação automáticas:
# .sops.yaml — configuração de encriptação
creation_rules:
# Ficheiros de produção: chave Age específica
- path_regex: secrets/prod/.*\.yaml$
age: "age1prodxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Ficheiros de staging: outra chave
- path_regex: secrets/staging/.*\.yaml$
age: "age1stagingxxxxxxxxxxxxxxxxxxxxxxxx"
# Restantes: chave por defeito
- path_regex: .*\.yaml$
age: "age1defaultxxxxxxxxxxxxxxxxxxxxxxx"
Com este ficheiro, basta executar sops secrets/prod/db.yaml e o SOPS selecciona automaticamente a chave correcta.
Integração com Ansible
O Ansible é uma das ferramentas IaC mais usadas em PME. A integração com SOPS é directa através de um script de lookup que desencripta os ficheiros automaticamente durante a execução dos playbooks.
Instalar o lookup plugin
# Instalar via pip o módulo sops para Ansible
pip install sops
# OU colocar o script de lookup no directório do projecto
mkdir -p lookup_plugins
wget -O lookup_plugins/sops.py \
https://raw.githubusercontent.com/getsops/sops/master/usage/ansible/sops.py
No ansible.cfg, activar o directório de plugins:
# ansible.cfg
[defaults]
lookup_plugins = ./lookup_plugins
Usar SOPS num playbook
# playbook.yml
- hosts: servidores
vars_files:
# O SOPS desencripta automaticamente
- secrets/prod/secrets.yaml
tasks:
- name: Criar ficheiro de configuração com segredos
ansible.builtin.template:
src: app.conf.j2
dest: /etc/app/app.conf
mode: "0600"
no_log: true # Não mostrar segredos no output
- name: Usar um valor individual via lookup
ansible.builtin.debug:
msg: "Token da API: {{ lookup('sops', 'secrets/prod/secrets.yaml item=api.token') }}"
no_log: true
ℹ Dica: Usa sempre no_log: true em tarefas que manipulam segredos. Sem esta directiva, o Ansible mostra os valores desencriptados no output da execução, o que pode ficar em registos de CI/CD acessíveis a pessoas não autorizadas.
Integração com OpenTofu
O OpenTofu (fork comunitário do Terraform) e o próprio Terraform podem consumir segredos SOPS através de um provider dedicado. O fluxo é simples: os segredos são encriptados no repositório e desencriptados em tempo de execução.
Provider SOPS
# main.tf — declaração do provider
terraform {
required_providers {
sops = {
source = "carlpett/sops"
version = "~> 1.0"
}
}
}
provider "sops" {}
# Ler o ficheiro de segredos encriptado
data "sops_file" "secrets" {
source_file = "secrets/prod/secrets.yaml"
}
# Usar os valores nos recursos
resource "aws_db_instance" "principal" {
username = data.sops_file.secrets.data["database.user"]
password = data.sops_file.secrets.data["database.password"]
# ...
}
Durante o tofu plan ou tofu apply, o provider lê o ficheiro SOPS, desencripta-o com a chave Age (que deve estar disponível via SOPS_AGE_KEY_FILE) e injecta os valores nos recursos.
Variáveis sensíveis em OpenTofu
# variables.tf
variable "db_password" {
type = string
sensitive = true
description = "Palavra-passe da base de dados"
}
# tfvars encriptado com SOPS
# secrets.auto.tfvars.enc → desencriptar antes do apply:
sops --decrypt secrets.auto.tfvars.enc > secrets.auto.tfvars
tofu apply
rm secrets.auto.tfvars # Apagar imediatamente
⚠ Atenção: O estado do OpenTofu/Terraform (terraform.tfstate) pode conter segredos em texto simples. Armazene o ficheiro de estado num backend remoto encriptado (S3 com SSE, Azure Blob, GCS) e nunca o commit no Git. O estado em texto simples é uma vulnerabilidade frequentemente ignorada.
Boas Práticas
Para tirar o máximo proveito do SOPS + Age em ambiente de PME, segue-se um conjunto de práticas recomendadas:
| Prática | Descrição |
|---|---|
| Chaves por ambiente | Usar chaves Age diferentes para produção, staging e desenvolvimento. Limita o impacto de uma chave comprometida. |
| Rotação periódica | Gerar novas chaves e re-encriptar os segredos a cada 6-12 meses. Manter as chaves antigas até todos os segredos estarem migrados. |
| Múltiplos destinatários | Encriptar com mais do que uma chave pública (ex: equipa de operações + chave de recuperação). Se uma chave for perdida, a outra permite desencriptar. |
| Pre-commit hook | Instalar um hook que bloqueia commits de ficheiros não encriptados em directórios de segredos. Evita exposição acidental. |
| Backup das chaves | Guardar as chaves privadas em pelo menos dois locais seguros (ex: gestor de palavras-passe + pen USB encriptada). Sem a chave, os segredos são irrecuperáveis. |
| CI/CD com secrets | Em pipelines (GitHub Actions, GitLab CI), injectar a chave Age via secret do CI. O SOPS desencripta em runtime sem persistir a chave. |
| Auditoria via Git | Como o SOPS mantém as chaves YAML legíveis, o git log -p mostra que segredo foi alterado e quando, sem revelar o valor. |
Pre-commit hook de protecção
Um hook simples que impede o commit de ficheiros não encriptados:
#!/bin/bash
# .git/hooks/pre-commit — bloquear segredos em texto simples
for f in $(git diff --cached --name-only -- 'secrets/**'); do
if ! grep -q "^sops:" "$f" 2>/dev/null; then
echo "ERRO: $f não está encriptado com SOPS. Execute:"
echo " sops --encrypt --age --in-place $f"
exit 1
fi
done
Múltiplos destinatários
Para garantir resiliência, encripta-se com várias chaves públicas:
# .sops.yaml com múltiplas chaves (destinatários)
creation_rules:
- path_regex: secrets/prod/.*\.yaml$
age:
- "age1equipaopsxxxxxxxxxxxxxxxxxxxx" # Equipa de operações
- "age1recuperacaoxxxxxxxxxxxxxxxxxxxx" # Chave de recuperação
- "age1cixxxxxxxxxxxxxxxxxxxxxxxxx" # CI/CD
Qualquer uma das chaves privadas correspondentes consegue desencriptar o ficheiro. Isto garante que mesmo que a chave da equipa de operações seja perdida, a chave de recuperação ou a do CI ainda permitem acesso aos segredos.
ℹ Resumo: O SOPS + Age oferece gestão de segredos encriptados em Git sem infraestrutura adicional, com auditoria via git diff legível, encriptação granular por valor, e integração nativa com Ansible e OpenTofu/Terraform. Para uma PME, é uma alternativa séria ao HashiCorp Vault — sem servidor dedicado, sem licenças, e sem dependências cloud.