External Secrets Operator: Gestión Segura de Secretos en Kubernetes
Uno de los problemas más persistentes en la gestión de clústeres de Kubernetes es el tratamiento de información sensible: contraseñas de bases de datos, claves de API, certificados TLS y tokens de acceso. Los Secrets nativos de Kubernetes resuelven parte del problema, pero tienen una debilidad fundamental: están codificados en Base64, no cifrados, lo que los hace potencialmente vulnerables si alguien accede al repositorio Git o a la API de Kubernetes sin los permisos adecuados.
External Secrets Operator (ESO) es la solución estándar de la industria para resolver este problema. Permite sincronizar secretos desde proveedores externos (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, Google Secret Manager, etc.) directamente a objetos Secret nativos de Kubernetes, de forma automática y segura.
El problema con los Secrets nativos de Kubernetes
Cuando defines un Secret de Kubernetes de forma estándar, el valor se almacena en etcd codificado en Base64:
apiVersion: v1
kind: Secret
metadata:
name: mi-secreto
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # "password123" en Base64 — ¡no es cifrado!
Esto presenta varios problemas críticos:
- No es cifrado real: Base64 es una codificación reversible trivialmente. Cualquier persona con acceso al objeto puede decodificarlo.
- Riesgo en Git: Si commitas tus manifiestos en un repositorio, el secreto queda expuesto en el historial para siempre.
- Rotación manual: Actualizar credenciales requiere modificar archivos YAML manualmente y redesplegar.
- Sin auditoría: No hay registro de quién leyó el secreto ni cuándo.
External Secrets Operator resuelve todos estos problemas delegando el almacenamiento real de los secretos a sistemas diseñados específicamente para ello.
Arquitectura de External Secrets Operator
ESO funciona mediante tres recursos personalizados (CRDs) principales:
┌─────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ ExternalSecret │─────▶│ SecretStore / │ │
│ │ (qué secreto │ │ ClusterSecretStore │ │
│ │ quiero y cómo │ │ (cómo conectar al │ │
│ │ mapearlo) │ │ proveedor externo) │ │
│ └────────┬─────────┘ └──────────┬───────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ Secret nativo │ │ External Secrets │ │
│ │ de Kubernetes │◀─────│ Operator (ESO) │ │
│ │ (auto-generado) │ │ (controlador) │ │
│ └──────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────┐
│ Proveedor Externo │
│ (AWS SM / Vault / │
│ Azure KV / GCP SM) │
└───────────────────────────┘
SecretStore: Define cómo conectarse al proveedor de secretos externo (credenciales, región, endpoint). Es namespaced (solo aplica a un namespace).ClusterSecretStore: Igual queSecretStorepero con alcance a todo el clúster.ExternalSecret: Define qué secretos obtener del proveedor y cómo mapearlos a unSecretde Kubernetes.
El operador vigila estos recursos y sincroniza automáticamente los secretos según el intervalo configurado.
Instalación con Helm
La forma más rápida de instalar ESO es mediante su chart oficial de Helm:
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets \
external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace \
--set installCRDs=true
Verifica que los pods estén corriendo correctamente:
kubectl get pods -n external-secrets
# NAME READY STATUS RESTARTS AGE
# external-secrets-xxxxxxxxx-xxxxx 1/1 Running 0 60s
# external-secrets-cert-controller-xxxxxx-xxxxx 1/1 Running 0 60s
# external-secrets-webhook-xxxxxxxxx-xxxxx 1/1 Running 0 60s
Ejemplo práctico: AWS Secrets Manager
1. Configurar el SecretStore
Primero, crea un Secret de Kubernetes con las credenciales de AWS (o usa IRSA si estás en EKS):
apiVersion: v1
kind: Secret
metadata:
name: awssm-credentials
namespace: produccion
type: Opaque
stringData:
access-key: "AKIAIOSFODNN7EXAMPLE"
secret-access-key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
A continuación, define el SecretStore que apunta a AWS Secrets Manager:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
namespace: produccion
spec:
provider:
aws:
service: SecretsManager
region: eu-west-1
auth:
secretRef:
accessKeyIDSecretRef:
name: awssm-credentials
key: access-key
secretAccessKeySecretRef:
name: awssm-credentials
key: secret-access-key
2. Crear el ExternalSecret
Define qué secreto obtener de AWS y cómo exponerlo en Kubernetes:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: mi-base-de-datos
namespace: produccion
spec:
refreshInterval: "1h" # Sincronizar cada hora
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: db-credentials # Nombre del Secret de K8s que se creará
creationPolicy: Owner
data:
- secretKey: username # Clave en el Secret de K8s
remoteRef:
key: produccion/mi-app/db # Nombre del secreto en AWS Secrets Manager
property: username # Campo dentro del secreto JSON
- secretKey: password
remoteRef:
key: produccion/mi-app/db
property: password
ESO creará automáticamente un Secret de Kubernetes llamado db-credentials en el namespace produccion con los valores obtenidos de AWS.
3. Usar el secreto en tu Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: mi-aplicacion
namespace: produccion
spec:
replicas: 2
selector:
matchLabels:
app: mi-aplicacion
template:
metadata:
labels:
app: mi-aplicacion
spec:
containers:
- name: app
image: mi-imagen:latest
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials # Secret gestionado por ESO
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
Ejemplo con HashiCorp Vault
Si tu organización usa HashiCorp Vault, la configuración del SecretStore es igualmente sencilla:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: produccion
spec:
provider:
vault:
server: "https://vault.mi-empresa.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "mi-app-role"
serviceAccountRef:
name: "mi-app-sa"
Con autenticación Kubernetes nativa, Vault verifica la identidad del Pod mediante su ServiceAccount, eliminando la necesidad de gestionar credenciales estáticas de Vault.
Sincronización completa con dataFrom
En lugar de mapear campo a campo, puedes sincronizar todos los campos de un secreto externo de una sola vez usando dataFrom:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: config-completa
namespace: produccion
spec:
refreshInterval: "30m"
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: app-config-completa
dataFrom:
- extract:
key: produccion/mi-app/config # Todos los campos de este secreto JSON
Si el secreto en AWS contiene {"DB_HOST": "...", "DB_PORT": "5432", "API_KEY": "..."}, el Secret de Kubernetes tendrá automáticamente los tres campos.
Buenas prácticas de seguridad
- Usa
ClusterSecretStorecon precaución: Permite a cualquier namespace obtener secretos. Restringe qué namespaces pueden usarlo mediantenamespaceSelector. - Configura
refreshIntervalapropiado: Para credenciales críticas, intervalos cortos (15m-1h) garantizan que las rotaciones se propaguen rápidamente. - Monitoriza el estado de los ExternalSecrets:
kubectl get externalsecret -n produccion # NAME STORE REFRESH INTERVAL STATUS READY # mi-base-de-datos aws-secrets-manager 1h Valid True - Usa IRSA en EKS (IAM Roles for Service Accounts): Evita almacenar credenciales de AWS en Kubernetes. El operador obtiene permisos directamente a través de la identidad del Pod.
- Combina con GitOps: Los manifiestos de
ExternalSecretson seguros para versionar en Git — no contienen valores sensibles, solo referencias a ellos.
Conclusión
External Secrets Operator transforma radicalmente la gestión de secretos en Kubernetes. En lugar de luchar contra las limitaciones de los Secrets nativos, permite aprovechar la potencia y seguridad de plataformas especializadas como AWS Secrets Manager, HashiCorp Vault o Azure Key Vault, con integración perfecta en el ecosistema Kubernetes.
La combinación de ESO + GitOps (con ArgoCD o Flux) representa la arquitectura de referencia para equipos que buscan un control total sobre su infraestructura manteniendo los más altos estándares de seguridad: los manifiestos en Git nunca contienen secretos reales, y los secretos en el clúster se actualizan automáticamente cuando rotan en el proveedor externo.