Blog

Gestión de Secrets en Kubernetes: Cómo Proteger los Datos Sensibles

Gestión de Secrets en Kubernetes bien hecha: cifrado en reposo en etcd, RBAC, secrets externos, rotación, GitOps y escaneo shift-left.

Bruno Baldo·Sep 14, 2026·11 min de lectura·Revisado por Rainforest Technologies
La gestión de Secrets en Kubernetes es uno de esos temas que parece resuelto el primer día y que, en silencio, se convierte en un pasivo hacia el día cien. Creas un Secret, lo referencias desde un Deployment y la aplicación arranca. Funciona, así que parece seguro. La verdad incómoda es que un Secret estándar de Kubernetes, por sí solo, no protege casi nada, y es justamente en la brecha entre "funciona" y "está protegido" donde ocurren los incidentes reales. Este análisis a fondo forma parte de nuestra guía más amplia de seguridad en Kubernetes. Aquí nos centramos en una sola cosa y la hacemos a conciencia: cómo almacenar, distribuir y proteger datos sensibles como contraseñas de bases de datos, tokens de API, claves TLS y credenciales de nube a lo largo de toda la vida de un clúster. Qué son los Secrets de Kubernetes y la gran salvedad Un Secret de Kubernetes es un objeto de API diseñado para almacenar pequeñas cantidades de datos sensibles, de modo que no tengan que quedar incrustados en la spec de un Pod o en una imagen de contenedor. En comparación con poner una contraseña directamente en un manifiesto de Deployment, eso es genuinamente un avance: el valor vive en su propio objeto, puede montarse o inyectarse bajo demanda y puede regirse por controles de acceso. Aquí está la salvedad que sorprende a muchos equipos: de forma predeterminada, los datos de un Secret solo están codificados en base64, no cifrados. Base64 es una codificación, no un cifrado. No tiene clave y no ofrece confidencialidad alguna. Cualquier persona que pueda leer el objeto Secret puede decodificarlo con un solo comando: kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d Peor aún, los valores de los Secrets se almacenan en etcd, el datastore de respaldo del clúster. Si etcd no está cifrado, entonces un archivo de backup, un snapshot, un disco robado o el acceso directo a etcd exponen todos los secrets del clúster en un formato trivial de leer. Así que el modelo mental que conviene adoptar es simple: un Secret es un contenedor cómodo para datos sensibles, no una garantía de que los datos estén seguros. Todo lo que sigue trata de cerrar esa brecha. Cifrado en reposo: EncryptionConfiguration y un proveedor KMS El primer control que hay que añadir es el cifrado en reposo en etcd. Kubernetes admite cifrar los recursos Secret antes de que se escriban en etcd, lo cual se configura mediante un archivo EncryptionConfiguration que el API server carga a través de --encryption-provider-config. Puedes cifrar con una clave gestionada localmente, pero la opción más robusta y operativamente más sólida es un proveedor KMS. Con KMS, las propias claves de cifrado de datos quedan envueltas por una clave alojada en un servicio externo de gestión de claves, de modo que la clave en bruto nunca reside en un archivo de configuración del control plane, y puedes rotarla y auditarla de forma independiente. Una configuración respaldada por KMS se ve, a grandes rasgos, así: apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources:   - resources:       - secrets     providers:       - kms:           apiVersion: v2           name: my-kms-provider           endpoint: unix:///var/run/kmsplugin/socket.sock       - identity: {} Dos apuntes prácticos. Primero, el orden importa: el proveedor que aparece primero en la lista se usa para cifrar las nuevas escrituras, mientras que todos los proveedores listados pueden descifrar, que es exactamente cómo se migra de identity (texto plano) al cifrado y, más adelante, se rotan las claves. Segundo, habilitar el cifrado solo afecta a las escrituras futuras. Para cifrar los secrets que ya existen, fuerza una reescritura después del cambio: kubectl get secrets --all-namespaces -o json | kubectl replace -f - El cifrado en reposo es el control que convierte "cualquiera con un snapshot de etcd es dueño de tus credenciales" en "el atacante también necesita la clave de KMS". Eso supone un aumento considerable del costo para el atacante. Quién puede leer los secrets: RBAC y el peligro de get/list amplios El cifrado protege los datos en reposo, pero dentro de un clúster en ejecución la vía de exposición más común es la autorización. Si un usuario, una service account o una carga de trabajo puede llamar a la API y leer un Secret, el cifrado en reposo no los detiene, porque el API server descifra a la salida. Por eso el RBAC sobre los secrets merece un escrutinio especial. Un role que concede get, list o watch sobre el recurso secrets es, en efecto, un role capaz de leer credenciales. Concedido de forma amplia, a nivel de clúster o con un comodín, se convierte en una vía hacia todos los secrets del clúster. Trata estos verbos como de alto privilegio y acota su alcance con rigor: apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata:   namespace: payments   name: read-payments-db-secret rules:   - apiGroups: [""]     resources: ["secrets"]     verbs: ["get"]     resourceNames: ["payments-db"] Observa el campo resourceNames, que restringe la concesión a un único Secret con nombre en lugar de a todos los secrets del namespace. Prefiere Roles con alcance de namespace en lugar de ClusterRoles para el acceso a los secrets, evita list y watch a menos que una carga de trabajo realmente necesite enumerar secrets, y recuerda que la service account de un Pod hereda todo lo que esa cuenta puede hacer. Para un tratamiento completo del diseño de roles con privilegio mínimo, consulta nuestra guía sobre RBAC en Kubernetes. Cómo evitar las filtraciones: imágenes, variables de entorno y logs Algunas de las peores exposiciones de secrets nunca llegan a tocar etcd. Se filtran porque el secret se copió en algún lugar de forma descuidada. No incrustes secrets en las imágenes de contenedor. Una credencial añadida durante un build permanece en las capas de la imagen y viaja a cada registry y nodo que la descarga. Las capas de imagen no son un lugar seguro para nada sensible. Ten cuidado con las variables de entorno. Inyectar un Secret como variable de entorno es cómodo, pero las variables de entorno tienen la costumbre de terminar en crash dumps, rastreadores de errores, la salida de kubectl describe para la spec del Pod y en procesos hijos. Siempre que puedas, prefiere montar los secrets como archivos a través de un volumen. Los archivos montados se leen bajo demanda, pueden actualizarse en el sitio y es menos probable que se capturen de forma incidental. Cuidado con los logs. Los logs de la aplicación, los traces de depuración y los banners de arranque que muestran la configuración son un canal clásico de filtración. Un secret que está cifrado de forma segura en etcd no vale nada como protección si la aplicación lo imprime en stdout durante el arranque. Montar un secret como archivo es sencillo: volumes:   - name: db-creds     secret:       secretName: payments-db containers:   - name: api     volumeMounts:       - name: db-creds         mountPath: /etc/secrets         readOnly: true Stores de secrets externos y el Secrets Store CSI Driver Para muchos equipos, la respuesta correcta a largo plazo es dejar de tratar el clúster como la fuente de verdad de los secrets y, en su lugar, mantenerlos en un gestor de secrets externo dedicado, respaldado por un KMS, con sus propias políticas de acceso, versionado y registro de auditoría. El clúster entonces obtiene los secrets en tiempo de ejecución en lugar de almacenarlos de forma permanente. El patrón común y neutral respecto al proveedor para esto es el Secrets Store CSI Driver. Monta secrets de un store externo directamente en un Pod como un volumen, de modo que el valor sensible se entrega a la carga de trabajo que lo necesita sin necesariamente persistir como un Secret nativo de Kubernetes. Un objeto SecretProviderClass describe qué secrets externos obtener, y el Pod lo referencia como un volumen CSI: apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata:   name: payments-db spec:   provider:   parameters:     objects: |       - objectName: "payments-db-password"         objectType: "secret" volumes:   - name: secrets-store     csi:       driver: secrets-store.csi.x-k8s.io       readOnly: true       volumeAttributes:         secretProviderClass: payments-db Los beneficios son una política centralizada, un único lugar para rotar y revocar, y un registro de auditoría que vive fuera del clúster. Otro patrón relacionado usa un operador que sincroniza desde un store externo hacia Secrets nativos, lo cual es más fácil de adoptar pero reintroduce la copia dentro del clúster, así que combínalo con cifrado en reposo y RBAC estricto. Rotación y credenciales de corta duración Los secrets estáticos que nunca cambian son un riesgo permanente: cuanto más tiempo vive una credencial, a más lugares se filtra y mayor es el radio de impacto cuando esto ocurre. Una buena gestión de secrets presupone la rotación desde el principio. Dos hábitos complementarios ayudan aquí. Primero, rota con regularidad y haz de la rotación una operación rutinaria y automatizada, en lugar de una carrera manual después de un incidente. Los stores de secrets externos facilitan mucho esto, porque la rotación ocurre en un solo lugar y las cargas de trabajo toman el nuevo valor en su siguiente montaje o actualización. Segundo, prefiere credenciales de corta duración, emitidas de forma dinámica siempre que la plataforma lo permita, como credenciales de base de datos que se generan bajo demanda y expiran en cuestión de horas, o el acceso a la nube concedido mediante federación de identidad de carga de trabajo en lugar de claves estáticas de larga duración. Una credencial que expira por sí sola es una credencial que no tienes que acordarte de revocar. Secrets sellados y cifrados en Git para GitOps GitOps es un modelo operativo maravilloso, y choca de frente con los secrets: quieres todo tu estado deseado bajo control de versiones, pero nunca debes commitear secrets en texto plano en un repositorio. El historial de Git es duradero y está ampliamente replicado, así que un secret commiteado una vez está, en la práctica, filtrado para siempre, incluso después de un commit posterior de "corrección". La solución es commitear únicamente material secreto cifrado. Dos enfoques consolidados y neutrales respecto al proveedor: Sealed secrets, donde un controlador en el clúster guarda una clave privada y tú commiteas un objeto cifrado con clave pública que solo ese controlador puede descifrar. El objeto cifrado es seguro para almacenar en Git; el texto plano nunca sale de tu máquina ni del clúster. Cifrado a nivel de archivo de los manifiestos de secrets usando una herramienta de cifrado con claves alojadas en un KMS, de modo que los valores del YAML commiteado sean texto cifrado y solo se descifren en el pipeline de entrega o mediante un operador dentro del clúster. En cualquier caso, la regla es la misma: el repositorio contiene texto cifrado, y la clave de descifrado vive en algún lugar que el repositorio no puede alcanzar. Nunca dejes que un secret en texto plano caiga en un commit, en el diff de un pull request o en un log de CI. Auditoría del acceso a los secrets No puedes proteger lo que no puedes ver. El audit log de Kubernetes registra las solicitudes al API server, incluidas las lecturas de objetos Secret, y es una de las señales de seguridad más valiosas y menos aprovechadas de un clúster. Configura una política de auditoría que capture el acceso al recurso secrets, envía esos logs a un sistema que de verdad revises y genera alertas ante los patrones anómalos: una service account inesperada leyendo secrets, un list repentino en todos los namespaces o el acceso desde una carga de trabajo que no tiene por qué tocar credenciales. La auditoría cumple dos propósitos. En el día a día, es detección. Después de un incidente, es el registro que te dice exactamente qué secrets fueron tocados y, por lo tanto, cuáles hay que rotar. Ambos son mucho mejores que adivinar. Shift left: detecta los problemas de secrets antes de que lleguen a producción Todo lo anterior es un control de tiempo de ejecución. Sin embargo, el lugar más barato para corregir un problema de secrets es antes de que llegue a un clúster, que es el corazón del desarrollo seguro de software. Dos prácticas se amortizan rápidamente: Detecta los secrets incrustados en el código en el CI. Escanea el código fuente, los manifiestos y el historial de commits en busca de tokens, claves y contraseñas para que una credencial se detecte en el pull request en lugar de después de haber sido desplegada y cacheada en una docena de lugares. Escanea los manifiestos y la IaC. El escaneo de infraestructura como código automatizado señala las configuraciones incorrectas que hemos comentado: etcd sin cifrar, RBAC excesivamente amplio sobre los secrets, secrets inyectados como variables de entorno y Deployments que referencian datos sensibles de forma insegura, todo antes de que se apliquen. El shift-left transforma la gestión de secrets de un ejercicio de apagar incendios en un guardrail que se ejecuta en cada cambio. Cómo ayuda Rainforest Rainforest está diseñada para detectar exactamente estos problemas de forma temprana. Su secret scanning rastrea tu código y tu configuración en busca de credenciales incrustadas en el código y secrets commiteados por accidente, para que un token filtrado se señale en el pull request en lugar de descubrirse en el análisis de un incidente. Su IaC scanning inspecciona tus manifiestos de Kubernetes y definiciones de infraestructura en busca de las configuraciones incorrectas que socavan la gestión de secrets: falta de cifrado en reposo, RBAC demasiado permisivo y un manejo inseguro de secrets en las specs de los Pods. Juntos te ofrecen una red de seguridad shift-left que complementa los controles de tiempo de ejecución de esta guía, todo desde una única plataforma de application security testing. Si quieres ver cómo se ve esto frente a tus propios manifiestos y repositorios, agenda una demo y lo repasaremos contigo.

Preguntas frecuentes

¿Los secrets de Kubernetes están cifrados de forma predeterminada?

No. De forma predeterminada, los datos de un Secret de Kubernetes solo están codificados en base64, que es una codificación sin clave y sin confidencialidad, y se almacenan en etcd. Cualquier persona que pueda leer el objeto Secret o acceder al datastore etcd, incluido un backup o un snapshot, puede recuperar el texto plano. Para cifrar realmente los secrets debes habilitar el cifrado en reposo y controlar el acceso con RBAC.

¿Cómo cifro los secrets de Kubernetes en reposo?

Configura un EncryptionConfiguration y apunta el API server hacia él con --encryption-provider-config. La opción más robusta es un proveedor KMS, que envuelve las claves de cifrado de datos con una clave alojada en un servicio externo de gestión de claves, de modo que la clave en bruto nunca se almacena en el control plane. Recuerda que el cifrado solo se aplica a las nuevas escrituras, así que reescribe después los secrets existentes para cifrarlos.

¿Debo almacenar secrets en Git?

Nunca almacenes secrets en texto plano en Git. El historial de Git es duradero y está ampliamente replicado, así que un secret commiteado una vez está, en la práctica, filtrado de forma permanente. Para GitOps, commitea únicamente material secreto cifrado, usando sealed secrets o cifrado a nivel de archivo respaldado por un KMS, y mantén la clave de descifrado en algún lugar que el repositorio no pueda alcanzar.

¿Cómo limito quién puede leer los secrets?

Usa un RBAC estricto. Trata get, list y watch sobre el recurso secrets como un acceso de alto privilegio a credenciales. Prefiere Roles con alcance de namespace en lugar de ClusterRoles, usa resourceNames para acotar una concesión a secrets con nombre específicos, evita list y watch a menos que sean realmente necesarios, y recuerda que un Pod hereda los permisos de su service account.

¿Qué es el Secrets Store CSI Driver?

Es un patrón de Kubernetes neutral respecto al proveedor para montar secrets de un gestor de secrets externo directamente en un Pod como un volumen, de modo que el valor sensible se entregue en tiempo de ejecución sin necesariamente persistir como un Secret nativo de Kubernetes. Un SecretProviderClass describe qué secrets externos obtener, lo que te brinda política centralizada, rotación y auditoría fuera del clúster.

Bruno Baldo

Escrito por

Bruno Baldo

CMO

Um pouco de marketing e um pouco de curiosidade e temos a receita pra criar um apaixonado por cyber!

Siga leyendo