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 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.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