Un Secret de Kubernetes es base64, y todo el mundo lo aprende tarde

Sacar los secretos de los manifiestos significa un operador que sincroniza desde el almacén de tu nube, un controlador que los monta, o los dos. Lo que decide cuál es qué hace tu aplicación cuando el valor cambia.

Alguien ejecuta una orden para ver un Secret en formato YAML, pasa el valor por un decodificador de base64, y la contraseña de la base de datos aparece en pantalla. Esto sorprende a la gente, y la sorpresa revela el malentendido: un Secret de Kubernetes no está cifrado, está codificado. La distinción importa porque todo el modelo de seguridad se sigue de ella.

La codificación protege contra mostrar datos binarios por accidente. No protege contra nada más. Quien pueda leer Secrets en un namespace puede leer todas las credenciales que haya en él, y en un clúster por defecto los valores están en etcd en texto plano.

Las tres cosas que hay que arreglar

Cifrado en reposo en etcd. La mayoría de los proveedores gestionados cifran ya el contenido de etcd por defecto o lo ofrecen como un ajuste respaldado por una clave que tú controlas. Comprueba el tuyo; es una casilla y elimina el caso en que un disco o una copia de seguridad exponen todos los secretos del clúster.

Quién puede leer Secrets. Es el control que más importa y el que la gente se salta. El permiso de leer Secrets en un namespace es el permiso de leer todas las credenciales que contiene. Concédelo por cuenta de servicio y para los secretos concretos que hagan falta, nunca como lectura general a nivel de namespace. Recuerda que el permiso de crear pods es en la práctica el permiso de leer cualquier secreto que pueda montar una cuenta de servicio de ese namespace, que es el razonamiento de varios equipos en un clúster.

De dónde viene el valor. Un secreto subido a git es una filtración desde el momento en que se empuja, y reescribir el historial no lo despublica. Es lo que resuelven los patrones de operador y de controlador.

External Secrets Operator, que sincroniza

El operador vigila un recurso personalizado que describe dónde vive un secreto en el gestor de secretos de tu nube, lo recupera y crea con él un Secret normal de Kubernetes. Las aplicaciones lo consumen como un Secret corriente, sin cambios de código.

La virtud es que para la carga no cambia nada. Todas las aplicaciones, todos los charts y todas las librerías que esperan un Secret siguen funcionando, lo que hace que la adopción sea incremental en vez de una migración.

El precio es que el secreto sí existe en etcd, así que te estás apoyando en los permisos y el cifrado de arriba en vez de eliminar la exposición. Estás resolviendo el problema de git y el de la rotación, no el de etcd.

Dos funciones que merece usar a conciencia. Un almacén de secretos con alcance de clúster permite al equipo de plataforma definir la conexión con el almacén una vez, para que los equipos de aplicación la referencien sin tener las credenciales de acceso. Y el empuje de secretos funciona en la otra dirección, escribiendo un valor generado hacia el almacén, lo que es útil cuando el clúster crea la credencial.

Fija el intervalo de refresco conscientemente. Demasiado frecuente y estás pagando por llamada a la API de tu gestor de secretos a un ritmo que se acumula, que es uno de los costes sorpresa de Vault o el gestor de secretos de tu nube. Demasiado espaciado y una rotación tarda horas en propagarse.

El controlador de almacenamiento, que monta

El controlador de almacenamiento de secretos toma el otro enfoque: monta los secretos directamente en el sistema de ficheros del pod como un volumen, recuperados al arrancar el pod y refrescados con un intervalo, sin que exista ningún Secret de Kubernetes.

La virtud es que el valor nunca está en etcd y nunca es visible para quien tenga lectura en el namespace. Para una credencial genuinamente sensible esta es la posición más fuerte.

El precio es que la aplicación tiene que leer de un fichero en vez de de una variable de entorno, lo que es un cambio de código en casi todas, y las herramientas que esperan un Secret no funcionan. El controlador puede además crear un Secret opcionalmente, lo que restaura la compatibilidad y renuncia al beneficio principal, así que elige a conciencia en vez de activarlo por defecto.

El reparto práctico que recomendamos: el operador para el grueso de las credenciales, porque la adopción es gratis, y el controlador de almacenamiento para el conjunto pequeño donde la exposición en etcd sea genuinamente inaceptable.

La identidad de carga de trabajo es el requisito previo

Los dos patrones necesitan que el clúster se autentique contra el gestor de secretos, y la forma incorrecta de hacerlo es una credencial de nube estática guardada en un Secret, que es el problema que estabas resolviendo.

Todos los servicios gestionados de Kubernetes admiten ya federar una cuenta de servicio de Kubernetes con una identidad de la nube, de modo que el pod recibe un token de vida corta y no existe ninguna credencial estática. Configúralo por carga de trabajo y no una identidad para todo el clúster, porque una identidad única con acceso de lectura a todos los secretos recrea el radio de impacto que intentabas reducir.

Es el mismo mecanismo que sacar las claves de larga vida de la integración continua, aplicado dentro del clúster.

La rotación es donde esto se paga, o no

Sincronizar un valor nuevo en un Secret no reinicia nada. Si tu aplicación leyó el valor al arrancar y lo metió en una cadena de conexión, seguirá usando el antiguo hasta que reinicie, y entonces fallará si la credencial antigua fue revocada.

Tres formas de manejarlo, de menor a mayor calidad. Un controlador que vigila el Secret y dispara un reinicio progresivo del despliegue, que es simple y provoca un reinicio. Que la aplicación vuelva a leer de forma programada o ante un fallo de autenticación, que es la respuesta correcta. O credenciales dinámicas de vida corta, donde el problema entero desaparece porque la credencial nunca fue de larga vida.

Elijas lo que elijas, esta es la parte que decide si tu programa de rotación funciona siquiera, y es trabajo de aplicación y no de plataforma, que es por lo que se atasca. El argumento completo está en la rotación que nadie hace.

GitOps, donde el manifiesto referencia pero no contiene

El patrón que hace funcionar esto con un despliegue dirigido por git es que el repositorio contenga la referencia y nunca el valor.

Un recurso de secreto externo que nombra una clave del almacén es seguro de subir, revisable en un pull request y no produce ninguna filtración. El modelo de reconciliación de Argo CD en producción se aplica entonces a los secretos igual que a todo lo demás.

El enfoque alternativo, cifrar los valores en el repositorio con un patrón de secretos sellados, también funciona y es una elección razonable para un equipo pequeño sin gestor de secretos en la nube. Su debilidad es que rotar significa volver a cifrar y subir, y el historial cifrado sigue siendo público dentro del repositorio, así que una clave comprometida expone todos los valores pasados.

Lo que se olvida

  • Los secretos aparecen en más sitios que el Secret. Las variables de entorno se ven en la descripción de un pod, los valores acaban en registros, y un volcado de memoria puede contenerlos.
  • Los secretos de descarga de imágenes también son secretos, y suelen ser la credencial más compartida de un clúster.
  • Una sincronización que falla es silenciosa. Si el operador no puede alcanzar el almacén, el Secret existente se queda como está y parece que va bien. Alerta ante fallos de reconciliación.
  • Borrar el recurso personalizado puede borrar el Secret, según la política de borrado. Fíjala a conciencia.
  • El almacén se convierte en dependencia dura del arranque de los pods. Si no es alcanzable durante un reinicio masivo, no arranca nada. Ten claro cómo se ve eso antes de que pase.

Qué hacer esta semana

Elige un namespace y lista quién puede leer Secrets en él, después decodifica uno y mira qué contiene. Si la respuesta es una contraseña de base de datos de producción legible por todo el que tenga acceso al namespace, ese es el hallazgo, y apretar los permisos es una victoria más rápida que instalar ningún operador. Comprobamos exactamente esto en un proyecto de seguridad.

ConsultorIA

¿Quieres esto en tu nube?

El diagnóstico de diez días en solo lectura es gratuito, y con Skyline puedes ver tu entorno en un mapa antes de escribirnos.

Artículos relacionados