Secretos y datos confidenciales en Kubernetes: gestión segura con Secrets, Vault y sealed-secrets
Introducción: por qué los Kubernetes Secrets nativos no son seguros por defecto
Los Kubernetes Secrets son lo primero que le viene a la mente a un ingeniero DevOps cuando necesita pasar una contraseña de base de datos o una clave de API a un Pod. Sin embargo, la mayoría de los equipos subestiman los riesgos: por defecto, los secretos se almacenan en etcd en formato Base64 — esto no es cifrado, sino únicamente codificación. Cualquiera que obtenga acceso a una copia de seguridad de etcd o al objeto Secret mediante kubectl podrá leer los datos en texto plano de inmediato.
Las amenazas reales son las siguientes:
- Compromiso de etcd — un atacante con acceso a un snapshot de etcd obtiene todos los secretos del clúster.
- Permisos RBAC excesivos — los desarrolladores obtienen accidentalmente permisos de
get secretsen el namespace de producción. - Secretos en Git — un manifiesto con datos codificados se confirma en el repositorio.
- Ausencia de rotación — la misma contraseña se usa durante años.
En 2026, la gestión de secretos en Kubernetes no es una opción, sino un elemento obligatorio de una infraestructura segura. Analicemos los enfoques y las buenas prácticas.
Panorama de enfoques para la gestión de secretos
1. Kubernetes Secrets nativos
Mecanismo integrado, sencillo de usar, pero que requiere protección adicional:
- Habilitar el cifrado en reposo mediante
EncryptionConfigurationen el servidor de API. - Restringir RBAC: conjunto mínimo de permisos a nivel de namespace.
- Habilitar Audit Logging para rastrear el acceso.
2. HashiCorp Vault
Almacén de secretos completo con credenciales dinámicas, mecanismo de lease y auditoría detallada. Se integra con Kubernetes mediante Agent Injector (sidecar) o CSI Provider. Indicado para equipos grandes y altos requisitos de seguridad.
3. Sealed Secrets (Bitnami)
Controlador para Kubernetes que permite almacenar secretos cifrados directamente en Git de forma segura. El secreto se cifra con la clave pública del clúster y solo puede ser descifrado por el propio clúster. Ideal para el enfoque GitOps.
4. External Secrets Operator
Operador que sincroniza secretos desde almacenes externos (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Azure Key Vault) hacia Kubernetes Secrets nativos. Permite gestionar los secretos de forma centralizada fuera del clúster.
Práctica: configuración de Sealed Secrets en el clúster
Sealed Secrets es la opción óptima para equipos que practican GitOps. Veamos la instalación y el uso paso a paso.
Paso 1: Instalación del controlador
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets\nhelm repo update\nhelm install sealed-secrets sealed-secrets/sealed-secrets \\\n --namespace kube-system \\\n --set fullnameOverride=sealed-secrets-controllerPaso 2: Instalación de kubeseal CLI
# Linux\nwget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.26.0/kubeseal-0.26.0-linux-amd64.tar.gz\ntar xvf kubeseal-0.26.0-linux-amd64.tar.gz\nsudo mv kubeseal /usr/local/bin/Paso 3: Creación y cifrado del secreto
Primero creamos un manifiesto Secret normal y luego lo ciframos con kubeseal:
kubectl create secret generic db-credentials \\\n --from-literal=DB_PASSWORD=supersecret123 \\\n --from-literal=DB_USER=appuser \\\n --namespace=production \\\n --dry-run=client -o yaml | \\\n kubeseal --controller-name=sealed-secrets-controller \\\n --controller-namespace=kube-system \\\n --format yaml > sealed-db-credentials.yamlEl manifiesto resultante sealed-db-credentials.yaml tiene el siguiente aspecto:
apiVersion: bitnami.com/v1alpha1\nkind: SealedSecret\nmetadata:\n name: db-credentials\n namespace: production\nspec:\n encryptedData:\n DB_PASSWORD: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...\n DB_USER: AgAKIBDLs8yHFUVCG+p1k2M3N4...\n template:\n metadata:\n name: db-credentials\n namespace: production\n type: OpaqueEste archivo puede confirmarse de forma segura en Git — sin acceso a la clave privada del clúster es imposible descifrarlo. Aplicamos el manifiesto:
kubectl apply -f sealed-db-credentials.yamlEl controlador creará automáticamente un Secret nativo en el namespace production.
Integración de HashiCorp Vault con Kubernetes
Agent Injector
Vault Agent Injector funciona como un mutating admission webhook: al crear un Pod con las anotaciones correspondientes, se añade automáticamente un contenedor sidecar que se autentica en Vault y monta los secretos en el sistema de archivos del Pod.
Ejemplo de anotaciones para un Pod:
apiVersion: v1\nkind: Pod\nmetadata:\n name: go-app\n namespace: production\n annotations:\n vault.hashicorp.com/agent-inject: "true"\n vault.hashicorp.com/role: "go-app-role"\n vault.hashicorp.com/agent-inject-secret-config.env: "secret/data/production/go-app"\n vault.hashicorp.com/agent-inject-template-config.env: |\n {{- with secret "secret/data/production/go-app" -}}\n export DB_PASSWORD={{ .Data.data.db_password }}\n export API_KEY={{ .Data.data.api_key }}\n {{- end }}\nspec:\n serviceAccountName: go-app-sa\n containers:\n - name: go-app\n image: myregistry/go-app:latestCSI Provider
Vault CSI Provider monta los secretos como archivos normales a través del Kubernetes Secrets Store CSI Driver — sin contenedor sidecar, lo que reduce el overhead. Es adecuado cuando la aplicación ya sabe leer la configuración desde archivos.
apiVersion: secrets-store.csi.x-k8s.io/v1\nkind: SecretProviderClass\nmetadata:\n name: vault-db-credentials\n namespace: production\nspec:\n provider: vault\n parameters:\n vaultAddress: "https://vault.example.com"\n roleName: "go-app-role"\n objects: |\n - objectName: "db_password"\n secretPath: "secret/data/production/go-app"\n secretKey: "db_password"Configuración de Kubernetes Auth en Vault
# Habilitamos la autenticación de Kubernetes\nvault auth enable kubernetes\n\n# Configuramos la conexión al clúster\nvault write auth/kubernetes/config \\\n kubernetes_host="https://kubernetes.default.svc" \\\n kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \\\n token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token\n\n# Creamos el rol para la aplicación\nvault write auth/kubernetes/role/go-app-role \\\n bound_service_account_names=go-app-sa \\\n bound_service_account_namespaces=production \\\n policies=go-app-policy \\\n ttl=1hGestión de secretos en el pipeline de CI/CD
GitHub Actions
En GitHub Actions, los secretos se almacenan en la configuración del repositorio o de la organización y se pasan al workflow mediante variables de entorno. Nunca imprimas secretos en los logs.
name: Deploy to Kubernetes\non:\n push:\n branches: [main]\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - name: Configure kubectl\n uses: azure/k8s-set-context@v3\n with:\n kubeconfig: ${{ secrets.KUBECONFIG }}\n - name: Deploy application\n env:\n REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}\n run: |\n echo "$REGISTRY_TOKEN" | docker login registry.example.com -u ci --password-stdin\n kubectl apply -f k8s/GitLab CI
En GitLab CI, los secretos se definen como variables Protected y Masked en la configuración del proyecto. Para la integración con HashiCorp Vault, GitLab soporta un mecanismo JWT nativo:
deploy:production:\n stage: deploy\n id_tokens:\n VAULT_ID_TOKEN:\n aud: https://vault.example.com\n secrets:\n DB_PASSWORD:\n vault: production/go-app/db_password@secret\n file: false\n script:\n - kubectl create secret generic db-credentials \\\n --from-literal=DB_PASSWORD="$DB_PASSWORD" \\\n --namespace=production \\\n --dry-run=client -o yaml | kubectl apply -f -Este enfoque permite obtener secretos directamente desde Vault sin almacenarlos en GitLab, lo que respeta el principio de mínima confianza.
Rotación de secretos sin tiempo de inactividad
La rotación de secretos es un proceso crítico que a menudo se olvida hasta el primer incidente. Veamos la estrategia para aplicaciones en Go y PHP.
Estrategia de escritura doble
- En Vault se crea una nueva versión del secreto, mientras la anterior permanece activa.
- La aplicación se reinicia con el nuevo secreto (rolling update en Kubernetes).
- Tras un despliegue exitoso, la versión anterior se marca como obsoleta.
Aplicación Go: recarga en caliente de la configuración
Las aplicaciones Go pueden utilizar el SDK de Vault para actualizar credenciales dinámicamente sin necesidad de reinicio:
// Ejemplo simplificado de monitoreo de lease
func renewSecret(client *vault.Client, secret *vault.Secret) {
renewer, _ := client.NewLifetimeWatcher(&vault.LifetimeWatcherInput{
Secret: secret,
Increment: 3600,
})
go renewer.Start()
defer renewer.Stop()
for {
select {
case renewal := <-renewer.RenewCh():
log.Printf("Secret renewed: %v", renewal.Secret.LeaseDuration)
case err := <-renewer.DoneCh():
log.Printf("Renewal failed, fetching new secret: %v", err)
// Obtener nuevo secreto
return
}
}
}Aplicación PHP: rotación mediante Kubernetes rolling update
Las aplicaciones PHP, por lo general, no mantienen conexiones de larga duración, por lo que el rolling update estándar de Kubernetes resuelve la tarea sin tiempo de inactividad:
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-app
namespace: production
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
spec:
containers:
- name: php-app
image: myregistry/php-app:latest
envFrom:
- secretRef:
name: db-credentialsAl rotar el secreto, basta con actualizar el objeto Secret y ejecutar kubectl rollout restart deployment/php-app -n production.
Auditoría y monitoreo del acceso a los secretos
La seguridad sin monitoreo es una ilusión. Configure los siguientes mecanismos:
Kubernetes Audit Logging
Habilite la auditoría a nivel del servidor de API, registrando los accesos al recurso secrets:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
verbs: ["get", "list", "watch"]
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
verbs: ["create", "update", "patch", "delete"]Vault Audit Devices
En HashiCorp Vault, habilite el dispositivo de auditoría file o syslog:
vault audit enable file file_path=/var/log/vault/audit.logTodos los accesos a los secretos se registran indicando el cliente, la hora y el resultado de la operación. Integre los logs con ELK Stack o Grafana Loki para un análisis centralizado.
Alertas
Configure alertas para eventos anómalos: listado masivo de secretos, acceso desde namespaces inesperados, intentos de acceso con tokens revocados.
Checklist de seguridad para un clúster en producción
- Cifrado en reposo — habilitar
EncryptionConfigurationpara etcd con proveedor AES-CBC o KMS. - Minimalismo en RBAC — principio de mínimo privilegio: roles solo para los namespaces y recursos necesarios, sin wildcards.
- Sin secretos en Git en texto plano — usar Sealed Secrets o External Secrets Operator.
- Audit logs habilitados — mínimo nivel Metadata para los recursos de tipo secrets.
- Rotación configurada — rotación automática mediante Vault lease o un planificador externo.
- Network Policy — restringir el acceso de red a Vault y otros almacenes de secretos.
- Tokens de ServiceAccount con TTL limitado — usar Bound Service Account Tokens (no estáticos).
- Deshabilitar el montaje automático de ServiceAccount —
automountServiceAccountToken: falseen el Deployment si el token no es necesario. - Escaneo de imágenes — analizar las imágenes Docker en busca de secretos hardcodeados (truffleHog, gitleaks en CI/CD).
- Monitoreo de anomalías — alertas ante comportamientos inusuales al trabajar con secretos.
Conclusión
La gestión segura de secretos en Kubernetes es una tarea de múltiples capas que requiere elegir las herramientas correctas según el contexto. Para equipos pequeños con enfoque GitOps, Sealed Secrets cubre la mayoría de los riesgos. Para infraestructuras enterprise con requisitos de cumplimiento normativo, HashiCorp Vault ofrece control total: credenciales dinámicas, auditoría detallada y gestión centralizada de políticas. External Secrets Operator integra de forma flexible Kubernetes con los almacenes de secretos en la nube.
Independientemente de la herramienta elegida, la base de la seguridad se apoya en tres pilares: mínimos privilegios, auditoría de todos los accesos y rotación regular. Implemente estas prácticas hoy mismo — y su clúster estará preparado para los requisitos de seguridad de 2026.
Tecnologías
Etiquetas
Ruslan Ismailov
Desarrollador Senior Web / Backend. Desarrollador senior web/backend con 9 años de experiencia. Stack: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, microservicios, CI/CD. Más sobre mí →