Cómo NO guardar secrets en Docker Containers

¿Pueden tus secrets verse comprometidos al hacer una implementación en un docker container? ¿Están lo suficientemente seguros de miradas que no deseás?

¿A qué me refiero con “secret”?

Contraseñas, API Tokens, certificados… Lo que se te ocurra. Cualquier información que tu código necesita para acceder a lo que sea que necesite acceder. — Encantadora redundancia :) — .

Quizás tu webserver necesita la contraseña de una base de datos para acceder a alguna tabla y trabajar con ella. Y nosotros queremos asegurarnos de que le podemos dar esa información al código sin que nadie más lo pueda ver. — ¡Lo siento, eso es privado muchachos! — .

Y como nota al margen… particularmente me gusta encriptar mis secrets.

¿Que por qué me gusta mantenerlos encriptados? Pues, si alguien se las ingeniara para acceder a tu clúster u obtuviera acceso a algunas de tus máquinas o consiguiera acceso a alguno de tus containers… Si el secret se encontrara guardado en texto plano, el trabajo estaría hecho. En caso contrario, si el secret se encuentra encriptado, ese alguien aún tendría que tomarse el trabajo de desencriptarlo. - No queremos dejársela fácil, ¿o sí?

Ahora pensemos en los aspectos prácticos de cómo en realidad obtenemos un secret en un contenedor en ejecución …

Comenzaré con algunas formas realmente malas.

#1.
Dejar los secrets en algún archivo de tu código y commitearlo. Probablemente la mitad de ustedes, mis queridos lectores, digan: “Por supuesto que eso es una estupidez”, y la otra mitad diga: “Mmm, sí, no, nunca he hecho eso. No, no me mires”. :B

En caso de que no sea tan obvio, si dejas tus secrets en tu código fuente, permites que todo aquel que tenga acceso a tu código, pueda tomar control de ellos.

¿Open source? -> Eso es realmente una mala idea. No sabes con quién te puedes encontrar del otro lado de Internet.

¿Closed source? -> No necesariamente quieres que todos los desarrolladores puedan leer tus credenciales.

Otra consecuencia, es que te fuerza a actualizar el código CADA VEZ que necesitas modificar o crear un nuevo secret. Y esa puede no ser la mejor de las ideas.

<< Si eres la persona de seguridad que quiere despertar a un desarrollador para decirle: “Hola, son las 3:00 de la mañana, pero necesito que implementes un nuevo secret”. Entonces no vas a ser muy popular. Así que NO pongas tus secrets en el código fuente. >>

Si los secrets no van a estar en la imagen del contenedor, tendremos que introducirlos durante el tiempo de ejecución. Y hay dos formas para hacer esto: mediante variables de entorno o montando un volumen.

#2.
Mediante variables de entorno.

Entonces, síganme, vamos a crear un container…

>> docker run -it — rm — name ubuntu -e MYSECRET=admin123 ubuntu /bin/bash

Como no tenía la última versión de la imagen de Ubuntu localmente en mi máquina, se descargó automáticamente para que la utilizáramos.

>> env | grep MYSECRET

Obviamente, como habría de esperarse, el valor de nuestro secret se encuentra entre las variables de entorno de nuestro container. Cualquier persona que pueda ejecutar un exec en nuestro container, será capaz de leer cada secret guardado en la memoria.

En caso de que no lo sepas, también es posible acceder a las variables de entorno de otras formas. — ¡Oh, maravillosa tecnología! — Entonces alguien podría… por ejemplo, inspeccionar ese container…

>> docker inspect ubuntu -f “{{json .Config.Env}}”

Y ahora podemos ver que cualquiera que pueda ejecutar un docker inspect — posiblemente de forma remota, no necesariamente desde la misma máquina — podrá ver el entorno muy fácilmente.

Además… Permítanme enviar este container a dormir por un ratito. Así podemos encontrar el process-id dentro del mismo.

>> sleep 100 (dentro del container)

>> pidof sleep (fuera del container)

Si bien necesito ser sudo o root para hacer esto, dentro de la carpeta /proc hay un montón de información interesante sobre los procesos en ejecución… ¡incluyendo sus variables de entorno!

>> sudo cat /proc/<pid>/environ | tr “\0” “\n” (Fuera del Container)

Y justo arriba de todo, se encuentra mi secret super mega secreto. XD

Pero quizás la razón más convincente para no utilizar este mecanismo es que los entornos se registran mucho. Muy a menudo, cuando algo sale mal, se vuelca el entorno en algún archivo de registro que ahora contiene todos tus secrets claramente accesibles para cualquiera que pueda leer esos logs. Entonces, por esta razón, podemos utilizar una alternativa…

#3. Montar un volumen.

“Pero Oriana… Yo te escuché decir que NO escriba mis secrets en el disco… que debo tenerlos encriptados en la memoria”.

¡Y sí, así es! Pero esos volúmenes son archivos de sistema temporarios. Y los archivos de sistema, por más temporarios que sean, se ven muy parecidos a los reales. Tienen cosas parecidas a archivos y directorios, pero, en realidad, están guardados solamente en la memoria, no en el disco. — ¡Punto a favor para los volúmenes! -

Y… ¿Cómo modifica esto la forma en la que los espías pueden acceder a nuestros secrets?

  • Como vimos más arriba, el comando docker inspect ya no sirve. Si lo ejecutara, podría ver que de hecho hay un volumen montado, pero no obtendría mágicamente y de forma legible toda la información dentro del volumen. Entonces ya no sería tan fácil obtener los secrets de esta forma. — ¡Yuppy! —
  • ¡Pero ojo! Cualquiera que pueda ejecutar un exec dentro de tu container, por supuesto, sigue teniendo la capacidad de leer las variables de entorno. — Oh no :( —
  • La carpeta /proc seguiría siendo un riesgo — nuevamente, solo para las personas que pudieron acceder a tu host, entonces probablemente también tengas otros temas de los cuales preocuparte — .
  • Otra cosa que desaparece es el problema del loggeo. Es poco probable que veas registros de rutina de todos los archivos con su contenido si algo falla. :)

Así que tener un volumen montado que contenga tus contraseñas y credenciales suena como una forma un poco más segura de hacerlo que una variable de entorno. Pero aún existen riesgos de cualquier manera.

Ya para ir cerrando, diría que cuando estés pensando en secrets, como todo en seguridad, no hay una solución perfecta. Todo lo que podemos hacer es reducir el riesgo y priorizar las necesidades del sistema. ¡Hasta la próxima!

Por Oriana Olivetti, security developer.

Turning good ideas into outstanding software.

Turning good ideas into outstanding software.