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

BackendMejor ParaInfraestructura
agePredeterminado moderno simpleNinguna
PGP/GPGLegado, ampliamente soportadoKeyring GPG
AWS KMSEquipos nativos de AWSCuenta AWS + IAM
GCP KMSEquipos nativos de GCPProyecto GCP
Azure Key VaultEntornos Azure/MicrosoftSuscripción Azure
HashiCorp VaultMulti-nube, on-premClú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

HerramientaCifraDiffableInfra NecesariaGitOps
SOPSSolo valoresOpcional (age)Excelente
Sealed SecretsSecret k8s completoNoControladorSolo Kubernetes
External SecretsValores en runtimeN/AVault/KMSSí, solo en línea
git-cryptArchivos completosNoGPG keyringLimitado
BlackBoxArchivos completosNoPGP keyringLimitado

Casos Especiales y Errores Comunes

  • Nunca confirme la clave privada — Solo confirme la clave pública en .sops.yaml.
  • .sops.yaml debe estar en la raíz del repositorio — O use --config ruta/.sops.yaml explí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.yaml creation_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.

Artículos Relacionados