Gestión de Secretos en Linux 2026: HashiCorp Vault, SOPS con age y systemd-creds
Guía práctica 2026 para gestionar secretos en Linux con HashiCorp Vault, SOPS con age y systemd-creds sellado al TPM 2.0. Modelo de amenazas, ejemplos de producción y rotación automática.
La gestión de secretos en Linux consiste en almacenar, distribuir y rotar credenciales sensibles (claves API, contraseñas, tokens, certificados) fuera del código fuente y del disco en texto plano, usando un flujo criptográfico auditable. En 2026, la combinación estándar en producción es HashiCorp Vault como servidor central de secretos dinámicos, SOPS con age para secretos versionados en Git (GitOps), y systemd-creds para inyección local sellada al TPM. Esta guía te muestra cómo montar los tres, cuándo usar cada uno y qué se rompe primero si se combinan mal.
Vault 1.17+ ofrece secretos dinámicos, arrendamientos con TTL y auth methods para Kubernetes, AWS y OIDC. Su radio de impacto es alto, por eso exige unsealing con Shamir o auto-unseal contra un KMS.
SOPS 3.9 con age permite cifrar YAML/JSON en Git usando claves modernas X25519 sin depender de GPG; una clave age comprometida expone todos los ficheros cifrados con ella.
systemd-creds sella secretos al TPM 2.0 del host con --with-key=host+tpm2, imposibilitando la lectura si el disco se extrae. El modelo de amenaza cubre robo físico, no compromiso root.
External Secrets Operator (ESO) 0.14 sincroniza secretos de Vault o AWS Secrets Manager hacia Secrets de Kubernetes sin exponer credenciales en manifiestos.
La rotación automática con Vault (bases de datos, PKI, SSH) reduce la ventana de exposición de días a minutos. Sin rotación, cualquier secreto es un secreto muerto esperando ser filtrado.
Detectar secretos filtrados antes del push con Gitleaks en el pipeline DevSecOps es la última línea de defensa cuando el desarrollador olvida usar Vault.
Modelo de amenazas: ¿qué se rompe primero?
Antes de instalar una sola herramienta, hay que dibujar el diagrama de confianza. Honestamente, la conversación sobre secretos descarrila casi siempre cuando el equipo elige tecnología antes de definir de qué se está defendiendo. Los tres actores principales son: (1) un atacante externo con acceso de red al servidor, (2) un desarrollador legítimo que empuja código a Git, y (3) un atacante con acceso físico al disco o a un backup no cifrado. Cada uno rompe cosas distintas.
Un atacante externo que gana RCE en el host puede leer cualquier fichero que el proceso comprometido tenga permisos de leer, incluido un secreto cifrado con SOPS si la clave age está en el mismo disco. Un desarrollador que hace git commit -a con una clave API en config.yaml filtra el secreto al historial para siempre, incluso si lo borra en un commit posterior. Un atacante con el disco físico puede leer los secretos si no están sellados al TPM del host o cifrados con una clave que no viva en ese disco. La regla es simple: ningún secreto en texto plano debe existir jamás en un disco persistente sin cifrado y sin control de acceso. Todo lo demás se deriva de esta invariante.
El radio de impacto (blast radius) también importa. Una clave age que descifra 400 ficheros en Git tiene un radio muy distinto al de un token de Vault con TTL de 15 minutos y permisos limitados a leer secret/data/prod/db/read. Diseñar por radio implica tres cosas: secretos dinámicos por defecto, secretos estáticos solo cuando no queda alternativa, y siempre con TTL corto y auditoría.
Por qué tres herramientas y no una
La pregunta razonable es: ¿por qué no usar solo Vault? La respuesta es que cada herramienta cubre un modo de fallo distinto, y forzar una sola crea un punto único de fallo (SPOF) inaceptable en Zero Trust. Vault es fantástico como servidor central con secretos dinámicos y auditoría, aunque requiere red, unsealing y disponibilidad. Si Vault cae, tus workloads no arrancan. SOPS resuelve el problema GitOps: manifiestos de Kubernetes, valores de Helm y configuración de aplicaciones que necesitas versionar en Git sin filtrar. systemd-creds resuelve el problema del bootstrap: ¿cómo obtiene el host su token inicial de Vault sin que ese token viva en texto plano en /etc?
Un patrón sólido que he desplegado varias veces: systemd-creds sella un role_id AppRole al TPM del host durante el aprovisionamiento; ese role_id se usa para autenticarse contra Vault y obtener un token con TTL corto; Vault emite credenciales dinámicas de base de datos, tokens de cloud y certificados PKI; SOPS cifra la configuración no-secreta pero sensible (endpoints internos, IDs de proyecto) que debe versionarse. Cada herramienta cubre a las otras.
Comparativa: Vault vs SOPS vs systemd-creds
Dimensión
HashiCorp Vault
SOPS + age
systemd-creds
Caso de uso principal
Servidor central, secretos dinámicos
Secretos versionados en Git (GitOps)
Inyección local sellada al hardware
Rotación automática
Sí (DB, PKI, SSH, cloud)
No (manual)
No (secretos de configuración)
Dependencia de red
Alta (servidor HTTPS)
Ninguna (offline)
Ninguna (offline)
Sellado al hardware
Vía auto-unseal con KMS/HSM
Vía plugin YubiKey/TPM (opcional)
Sí, nativo con TPM 2.0
Auditoría
Log completo por operación
Historial Git
Journal de systemd
Curva de aprendizaje
Alta
Baja
Media
Radio de impacto si se compromete
Muy alto (raíz de confianza)
Alto (todos los ficheros cifrados)
Bajo (limitado al host)
HashiCorp Vault en producción
Vault 1.17 (LTS hasta 2027) sigue siendo el estándar para gestión centralizada. Su valor real no está en almacenar strings cifrados (cualquier base de datos hace eso), sino en emitir secretos dinámicos: credenciales de base de datos que existen durante 15 minutos y se destruyen automáticamente, certificados PKI de vida corta, y tokens cloud con IAM efímero. Consulta las notas de la versión oficial de Vault para detalles de compatibilidad.
Configuración mínima de producción con almacenamiento Raft integrado y auto-unseal contra AWS KMS:
Habilita el motor de base de datos dinámica para PostgreSQL:
# Habilitar el secrets engine de base de datos
vault secrets enable database
# Configurar conexión a PostgreSQL
vault write database/config/postgres-prod \
plugin_name=postgresql-database-plugin \
allowed_roles="app-readonly,app-readwrite" \
connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/appdb?sslmode=require" \
username="vault-admin" \
password="$(vault kv get -field=password secret/bootstrap/pgadmin)"
# Definir role con TTL de 15 minutos
vault write database/roles/app-readonly \
db_name=postgres-prod \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="15m" \
max_ttl="1h"
# La aplicación pide credenciales bajo demanda
vault read database/creds/app-readonly
AppRole para autenticación de máquinas
Los humanos entran con OIDC (Okta, Google Workspace). Las máquinas entran con AppRole: un role_id público y un secret_id de un solo uso. El patrón seguro es que el host obtiene su secret_id de un trusted broker (típicamente el orquestador de aprovisionamiento, como Terraform o Ansible con Vault Agent) y lo intercambia por un token de corta duración.
SOPS con age para GitOps
Mozilla SOPS 3.9 cifra ficheros YAML, JSON, ENV e INI a nivel de valor: las claves quedan en claro (para poder hacer diff en Git), los valores quedan cifrados. La combinación con age (proyecto oficial en GitHub) es la que recomiendo desde 2024: X25519 moderno, sintaxis de clave simple, sin la complejidad histórica de GPG. Si tu equipo aún usa gpg --gen-key para esto, en serio, ya es hora de migrar.
Instalación y flujo básico en un pipeline GitOps:
# Instalar age y sops (Debian/Ubuntu 2026)
sudo apt install age
curl -LO https://github.com/getsops/sops/releases/download/v3.9.1/sops_3.9.1_amd64.deb
sudo dpkg -i sops_3.9.1_amd64.deb
# Generar clave age del equipo (fuera del repo)
age-keygen -o ~/.config/sops/age/keys.txt
# Public key: age1qxk5v7t8s3r4d9m2n6p0q1w5e8y3u4i7o6l9k2j5h1g8f0d3s6a9x2c
# Fichero .sops.yaml en la raíz del repo
cat > .sops.yaml <<'EOF'
creation_rules:
- path_regex: secrets/prod/.*\.yaml$
age: age1qxk5v7t8s3r4d9m2n6p0q1w5e8y3u4i7o6l9k2j5h1g8f0d3s6a9x2c
- path_regex: secrets/staging/.*\.yaml$
age: age1zzhg7ekq8w2m3n0p9v6t4s1r5d8y3u4i7o6l9k2j5h1g8f0d3s6a9c
EOF
# Cifrar un fichero
sops --encrypt --in-place secrets/prod/database.yaml
# Editar (descifra en memoria, cifra al guardar)
sops secrets/prod/database.yaml
# Descifrar en el pipeline
sops --decrypt secrets/prod/database.yaml | kubectl apply -f -
SOPS con Flux o ArgoCD
Ambos controladores GitOps integran SOPS de forma nativa. Con Flux, se usa el Kustomize decryption provider; con ArgoCD, un plugin como argocd-vault-plugin o helm-secrets. La clave age privada se monta como un Secret de Kubernetes en el namespace del controlador, protegido por RBAC estricto y auditado con políticas de Kyverno para admisión de pods.
systemd-creds sellado al TPM 2.0
Introducido en systemd 250 y maduro desde la versión 256 (2025), systemd-creds es la respuesta nativa de Linux al problema del bootstrap secret. La idea: cifras un secreto en el host con una clave que solo existe dentro del TPM 2.0, y el servicio systemd lo descifra en el arranque, exponiéndolo al proceso vía descriptor de fichero. Si alguien roba el disco, no puede descifrar el secreto sin el TPM físico. Si alguien altera el firmware de arranque, el TPM se niega a descifrarlo (medición PCR).
Flujo completo para inyectar un token de Vault a un servicio de aplicación:
El secreto vive en /run/credentials/myapp.service/vault-token, un tmpfs privado del servicio, invisible para otros procesos gracias a PrivateMounts. Combinado con las directivas de hardening de servicios systemd, el radio de impacto de una RCE en myapp queda contenido al descriptor de fichero de esa credencial.
External Secrets Operator en Kubernetes
El anti-patrón clásico de Kubernetes es tener contraseñas en Secret objects cuyo YAML se versiona en Git. Recordatorio importante: un Secret de Kubernetes es base64, no cifrado. External Secrets Operator (ESO) 0.14, junto con Reloader, resuelve esto sincronizando desde Vault, AWS Secrets Manager, GCP Secret Manager o Azure Key Vault hacia Secret objects en el clúster, sin que ese secreto pase nunca por Git.
ESO añade metadatos que Reloader vigila: cuando el ExternalSecret cambia, Reloader reinicia el Deployment. Combinado con secretos dinámicos de Vault (TTL 24h), los pods rotan sus credenciales de base de datos automáticamente, sin intervención humana.
Rotación automática de secretos
Un secreto sin rotación es un secreto muerto. La pregunta operativa no es si se filtrará, sino cuándo, y qué haces cuando ocurre. La respuesta industrial madura es sencilla: rotación programada, revocación instantánea, y detección de filtración. Vault cubre las tres.
Ejemplo de rotación mensual de la clave raíz de un motor PKI intermedio:
# Programar rotación de credenciales root de la DB cada 30 días
vault write -force database/rotate-root/postgres-prod
# Rotación de la clave interna del motor de tránsito (TDE)
vault write -force transit/keys/app-encryption/rotate
# Revocar todos los leases de un role (uso en emergencia)
vault lease revoke -prefix database/creds/app-readwrite
Para SOPS, la rotación se llama re-encryption: se genera una nueva clave age, se actualiza .sops.yaml, y se ejecuta sops updatekeys secrets/prod/*.yaml para reencriptar cada fichero con la nueva clave sin cambiar el contenido. El compromiso de la clave antigua sigue exponiendo el historial Git; por eso la rotación de SOPS debe acompañarse de rotación del secreto subyacente (contraseña, token) siempre que sea posible.
Ventanas de exposición aceptables
Mi rúbrica personal, refinada tras varios post-mortems dolorosos: credenciales de base de datos con TTL de 1h, tokens de cloud con TTL de 15 minutos, certificados PKI internos con validez de 24h, certificados públicos con 90 días (Let's Encrypt), y contraseñas de servicio estáticas rotadas cada 90 días. Cualquier secreto con TTL mayor a un trimestre necesita justificación escrita.
Detección de secretos filtrados en Git
Toda la arquitectura anterior asume que los desarrolladores no cometen secretos. La realidad es que ocurre igualmente: con rotación de personal, con nuevos junior, con presión de deadline. La red de seguridad son escáneres de secretos pre-commit y en CI. Gitleaks, TruffleHog y GitHub secret scanning (para repos privados) son las tres opciones vigentes.
# Hook pre-commit local (.git/hooks/pre-commit)
#!/usr/bin/env bash
if ! command -v gitleaks >/dev/null; then
echo "gitleaks no instalado, omitiendo escaneo"
exit 0
fi
gitleaks protect --staged --redact --verbose
if [ $? -ne 0 ]; then
echo "Secreto detectado en el staging area. Aborta commit."
exit 1
fi
# En el pipeline CI (GitHub Actions)
- name: Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
with:
config-path: .gitleaks.toml
La configuración .gitleaks.toml permite añadir patrones custom para tokens internos (por ejemplo, tu prefijo de AppRole de Vault). Documentación completa en el repositorio oficial de Gitleaks.
Anti-patrones que veo repetirse
Cierro con la lista de errores que he visto llevar a incidentes reales. Primero, secretos en variables de entorno de contenedor persistidas en el manifiesto: cualquier persona con kubectl get pod -o yaml las lee. Segundo, una única clave age para toda la organización, imposible de rotar sin coordinación catastrófica. Tercero, Vault sin auditoría al backend: sin log inmutable de operaciones, no hay forma de investigar nada. Cuarto, backups sin cifrar del snapshot de Vault Raft, que es literalmente todos tus secretos en un solo fichero legible. Coordina con la firma con Cosign en la cadena de suministro para que estos backups también sean verificables.
Preguntas frecuentes
¿Cuál es la diferencia entre Vault y SOPS?
Vault es un servidor centralizado que emite secretos dinámicos con TTL, ideal para credenciales de base de datos y tokens cloud rotables. SOPS cifra ficheros estáticos versionados en Git, ideal para configuración GitOps y valores de Helm. Se complementan: Vault para lo dinámico, SOPS para lo versionado.
¿Es seguro guardar la clave age privada en el disco del servidor?
Solo si el disco está cifrado con LUKS2 sellado al TPM y los permisos del fichero son 600, con SELinux o AppArmor restringiendo qué procesos pueden leerlo. Para producción de alta seguridad, guarda la clave age en un YubiKey PIV o sella el fichero con systemd-creds.
¿Cómo rotar un secreto que ya se filtró en Git?
Rota el secreto subyacente en el origen inmediatamente (nueva contraseña de DB, revocar token API, regenerar clave). Después, audita los logs de acceso del servicio desde el timestamp del commit filtrado. Reescribir el historial Git es opcional y no elimina el riesgo: el secreto ya está comprometido en cualquier clon o fork.
¿Puedo usar systemd-creds sin TPM 2.0?
Sí, con --with-key=host el secreto se sella a una clave derivada del sistema pero sin garantía hardware; un atacante con acceso root al host activo puede descifrarlo. Sin TPM, el modelo de amenaza cubre solo robo de disco frío, no compromiso online. Para servidores en cloud sin TPM, considera Nitro Enclaves (AWS) o Confidential Computing (GCP/Azure).
¿Qué pasa si Vault se cae y mis pods necesitan arrancar?
Depende de tu configuración de ESO: si el Secret ya está sincronizado en el clúster, los pods arrancan con la copia local (potencialmente caducada). Si el Secret aún no existe, los pods quedan en estado Pending. Mitigaciones: cluster Vault en HA con al menos 3 nodos Raft, auto-unseal contra KMS externo, y monitoreo activo del endpoint /v1/sys/health.
¿SOPS es compatible con GPG legado o solo con age?
SOPS soporta GPG, age, AWS KMS, GCP KMS, Azure Key Vault y HashiCorp Vault Transit como proveedores de clave. GPG sigue funcionando por compatibilidad, pero para despliegues nuevos en 2026 la recomendación es age por su simplicidad y curva X25519 moderna, o KMS cloud si ya usas ese proveedor.
Cómo integrar Checkov, KICS y Trivy config en pipelines DevSecOps de 2026 para escanear Terraform y Kubernetes con SARIF, baselines y hooks pre-commit.
Stack práctico para asegurar la cadena de suministro de software en Linux 2026: firma keyless con Cosign, SBOMs con Syft, escaneo con Grype y verificación obligatoria en Kubernetes con Kyverno.
Guía práctica para construir un pipeline DevSecOps en Linux con GitHub Actions. Integra Semgrep, Gitleaks y Trivy para blindar tu flujo CI/CD con herramientas open source y código listo para copiar.