Los equipos sacan los secretos del clúster hacia un store externo — y lo primero que crean es un secreto nuevo: el token de acceso a ese mismo store. Un token de Vault en un ConfigMap, una clave de AWS en una variable del operador — «un secreto para acceder a los secretos» devuelve el problema al punto de partida. Según el Verizon DBIR 2025, más del 60 % de los incidentes en la nube empiezan con credenciales filtradas. La combinación que funciona en 2026 cierra el circuito por completo: External Secrets Operator sincroniza los secretos desde el store externo y workload identity elimina la última clave estática — la que el operador usa para autenticarse ante el store.
Por qué un Secret nativo no es source of truth
Un Secret de Kubernetes se guarda en etcd como base64 — eso es codificación, no cifrado. Mientras el encryption at rest no esté activado explícitamente (--encryption-provider-config, con proveedor KMS en producción), leer etcd directamente — o uno de sus backups — se salta todo el RBAC. Pero incluso un Secret nativo cifrado es mala fuente primaria: no tiene versionado, ni auditoría, ni rotación automática, está atado a un clúster — multi-cluster se convierte en copiar y pegar — y meterlo en Git bajo GitOps está descartado: el plaintext vive para siempre en el historial de commits.
La conclusión: el secreto «verdadero» vive en un store externo. El default es Vault o su fork libre de BSL, OpenBao (compatible a nivel de API, bajo la Linux Foundation); las alternativas son AWS Secrets Manager, GCP Secret Manager y Azure Key Vault. Los stores gestionados esconden una trampa de precio: a $0.40 por secreto al mes, 500 secretos se convierten en unos $2.400 al año antes de contar las llamadas a la API — un Vault autogestionado puede salir más barato a escala.
ESO: una fachada CRD sobre más de 50 backends
External Secrets Operator (proyecto CNCF) es el pegamento universal entre el store y el clúster. Un ClusterSecretStore describe la conexión al backend; un ExternalSecret es la petición declarativa: «sincroniza esta ruta en un K8s Secret normal». La aplicación no sabe nada de Vault: lee el secreto a través del habitual valueFrom: secretKeyRef. Cambiar de backend significa editar el SecretStore, no las aplicaciones.
Sobre el par básico están ClusterExternalSecret (fan-out de un secreto hacia muchos namespaces en plataformas multi-tenant) y PushSecret (la dirección inversa: del clúster hacia el store). En Git solo vive el ExternalSecret — una referencia a una ruta, nunca el valor.
Workload identity: sin credenciales para las credenciales
Queda un secreto de bootstrap: ¿con qué se autentica el propio operador ante el store? La respuesta — con nada estático. El mecanismo es idéntico en todas las nubes: el pod recibe un projected ServiceAccount token, ligado por audience al endpoint OIDC de la nube, y el IAM lo valida directamente. En EKS eso es IRSA — la anotación eks.amazonaws.com/role-arn en el ServiceAccount, con una trust policy de IAM fijada al par exacto de namespace y serviceaccount — o el más moderno EKS Pod Identity. En GKE es Workload Identity, en AKS es Azure Workload Identity: el mismo patrón con otra anotación.
Para Vault, el mismo papel lo cumple el kubernetes auth method: el rol de Vault queda ligado en firme al nombre del ServiceAccount y al namespace (bound_service_account_names más bound_service_account_namespaces), y el token vive una hora. Comprometer un pod del namespace vecino no da nada. La única salvedad: el rol federado debe ser least-privilege. Concederle AdministratorAccess «para la demo» es volver a reunir todos los riesgos de la clave estática — solo que sin la clave.
La rotación es un proceso, no un evento
refreshInterval en el ExternalSecret fija cada cuánto se consulta el store: 1–5 minutos es rotación rápida a costa de carga en el backend; 1–24 horas trata bien al backend, pero el secreto envejece. Un valor cambiado en el store llega al K8s Secret automáticamente, pero los pods leen los secretos al arrancar — hace falta un reinicio. Ese hueco lo cierra stakater/reloader: la anotación reloader.stakater.com/auto en el Deployment, y el reinicio ocurre solo cada vez que el Secret cambia.
El escalón superior son los dynamic secrets de Vault: las credenciales de base de datos se generan por petición, cada una con su TTL y revocación automática. La rotación deja de ser un problema — cada instancia de la aplicación lleva sus propias credenciales efímeras.
Checklist y trampas
Vault KV v2: en remoteRef.key el segmento /data/ es obligatorio — por ejemplo secret/data/prod/app/db. Sin él, ESO reporta SecretSynced: True mientras el Secret queda vacío: los fallos más dañinos son los que parecen éxito.
creationPolicy: Owner en el target — de lo contrario habrá conflicto de ownership en un kubectl edit manual. Un refreshInterval largo para credenciales efímeras deja la rotación sin sentido. Un Secret compartido por veinte aplicaciones vuelve el RBAC grueso: revocar el acceso a un solo servicio se hace imposible.
Env vars contra ficheros: las variables de entorno se ven en /proc, en los crash dumps y en kubectl describe; la tendencia 2026 son los secretos montados como fichero vía CSI driver para lo más sensible. El encryption at rest en etcd es la capa base que ESO no sustituye: el secreto sincronizado sigue acabando en etcd.
El reparto final: el store responde «dónde viven los secretos», ESO «cómo llegan al pod» y workload identity «cómo funciona todo esto sin una sola clave de larga vida». Las capas no se quitan desde abajo: sin las dos primeras, a workload identity no le queda nada que proteger. Y cómo un secreto no acaba en el repositorio por el camino — del secret-scan a las políticas de admisión.