TL;DR — Resumen Rápido
SOPS cifra valores en YAML/JSON dejando las claves legibles. Guía: age, KMS, rotación de claves, CI/CD, Kubernetes e integración GitOps con ArgoCD.
Mozilla SOPS (Secrets OPerationS) resuelve uno de los problemas más difíciles de GitOps: cómo almacenar secretos en control de versiones sin exponerlos. A diferencia de herramientas que cifran archivos completos, SOPS cifra solo los valores en archivos YAML, JSON, ENV e INI — dejando las claves en texto plano para que los diffs sigan siendo legibles y las revisiones de código tengan sentido.
¿Por Qué SOPS? El Problema de los Secretos en Git
Confirmar secretos en texto plano en Git es un incidente de seguridad esperando ocurrir:
- Solo variables de entorno — Funciona en runtime pero no versiona ni audita cambios en secretos.
- Vault / External Secrets — Potente pero requiere infraestructura y acceso en línea al desplegar.
- git-crypt — Cifra archivos completos; los diffs se vuelven binarios e imposibles de revisar.
- SOPS — Cifra solo valores. Las claves permanecen en texto plano. Los archivos se comparan normalmente.
Un archivo cifrado con SOPS luce así:
database:
password: ENC[AES256_GCM,data:abc123...,iv:...,tag:...,type:str]
host: db.example.com # ← sin cifrar, no es un secreto
Instalación
# macOS
brew install sops age
# Ubuntu/Debian
apt install age
wget https://github.com/getsops/sops/releases/latest/download/sops-v3.x.x.linux.amd64 \
-O /usr/local/bin/sops && chmod +x /usr/local/bin/sops
Backends de Cifrado
| Backend | Mejor Para | Infraestructura |
|---|---|---|
| age | Predeterminado moderno simple | Ninguna |
| PGP/GPG | Legado, ampliamente soportado | Keyring GPG |
| AWS KMS | Equipos nativos de AWS | Cuenta AWS + IAM |
| GCP KMS | Equipos nativos de GCP | Proyecto GCP |
| Azure Key Vault | Entornos Azure/Microsoft | Suscripción Azure |
| HashiCorp Vault | Multi-nube, on-prem | Clúster Vault |
Para proyectos nuevos, age es el valor predeterminado recomendado.
age: Uso Detallado
# Generar un par de claves
age-keygen -o ~/.config/sops/age/keys.txt
# Public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Establecer variable de entorno
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
# Permisos correctos
chmod 600 ~/.config/sops/age/keys.txt
age también soporta claves SSH como destinatarios, útil si su equipo ya tiene claves SSH pero no ha generado claves age dedicadas.
Configuración de .sops.yaml
creation_rules:
- path_regex: secrets/.*\.yaml$
age: >-
age1alice1111111111111111111111111111111111111111111111111111111,
age1bob2222222222222222222222222222222222222222222222222222222222
- path_regex: k8s/.*secret.*\.yaml$
kms: "arn:aws:kms:us-east-1:123456789:key/mrk-abc123"
Grupos de claves y umbral de Shamir para requerir M de N partes para descifrar:
creation_rules:
- path_regex: critical/.*
key_groups:
- age:
- age1alice...
- age1bob...
- kms:
- arn: "arn:aws:kms:us-east-1:123456789:key/mrk-abc123"
shamir_threshold: 2
Cifrar y Editar Archivos
# Cifrar a un nuevo archivo
sops --encrypt secrets.yaml > secrets.enc.yaml
# Abrir en editor (descifra, edita, vuelve a cifrar al guardar)
sops secrets.enc.yaml
# Descifrar a stdout
sops --decrypt secrets.enc.yaml
# Inyectar como variables de entorno
sops exec-env secrets.enc.yaml 'docker-compose up'
Rotación de Claves
# 1. Agregar nueva clave, eliminar antigua de .sops.yaml
# 2. Volver a cifrar archivos afectados
sops updatekeys --yes secrets/db.yaml
# 3. Verificar que la clave antigua ya no funciona
SOPS_AGE_KEY="clave_privada_antigua" sops --decrypt secrets/db.yaml
# Debe fallar: "could not decrypt data key"
Integración con CI/CD y Kubernetes
GitHub Actions:
- name: Descifrar secretos
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
run: sops --decrypt k8s/secrets.enc.yaml | kubectl apply -f -
Flujo para Kubernetes:
# Crear manifiesto Secret cifrado con SOPS
kubectl create secret generic db-creds \
--from-literal=password=supersecret \
--dry-run=client -o yaml > k8s/db-secret.yaml
sops --encrypt --in-place k8s/db-secret.yaml
git add k8s/db-secret.yaml && git commit -m "Agregar secreto db cifrado"
KSOPS para ArgoCD y el proveedor nativo de Flux permiten descifrado automático al desplegar.
SOPS vs Alternativas
| Herramienta | Cifra | Diffable | Infra Necesaria | GitOps |
|---|---|---|---|---|
| SOPS | Solo valores | Sí | Opcional (age) | Excelente |
| Sealed Secrets | Secret k8s completo | No | Controlador | Solo Kubernetes |
| External Secrets | Valores en runtime | N/A | Vault/KMS | Sí, solo en línea |
| git-crypt | Archivos completos | No | GPG keyring | Limitado |
| BlackBox | Archivos completos | No | PGP keyring | Limitado |
Casos Especiales y Errores Comunes
- Nunca confirme la clave privada — Solo confirme la clave pública en
.sops.yaml. .sops.yamldebe estar en la raíz del repositorio — O use--config ruta/.sops.yamlexplícitamente.- Permisos del archivo de clave age — SOPS rechazará un keys.txt legible por otros usuarios. Use
chmod 600. - El umbral de Shamir debe cumplirse al descifrar — Si KMS no está disponible y
shamir_threshold: 2, necesita 2 titulares de claves age simultáneamente.
Resumen
- SOPS cifra valores, no claves — Los archivos siguen siendo comparables y auditables en Git.
- age es el predeterminado moderno — Sin infraestructura, funciona sin conexión, soporta múltiples destinatarios.
.sops.yamlcreation_rules — Mapee patrones de ruta a claves de cifrado con grupos y umbral Shamir.- Soporte nativo para Kubernetes — KSOPS para ArgoCD, proveedor nativo para Flux.