Docker y seguridad de contraseñas

162

He estado experimentando con Docker recientemente en la construcción de algunos servicios para jugar y una cosa que me molesta constantemente ha sido poner contraseñas en un Dockerfile. Soy un desarrollador, por lo que almacenar contraseñas en la fuente se siente como un golpe en la cara. ¿Debería esto ser una preocupación? ¿Hay alguna buena convención sobre cómo manejar las contraseñas en Dockerfiles?

anthonator
fuente
77
Hay un problema abierto sobre Github que solicita mejores prácticas con respecto a Docker y sus secretos, el problema está aquí: github.com/docker/docker/issues/13490
Luís Bianchin

Respuestas:

93

Definitivamente es una preocupación. Los archivos Docker se registran comúnmente en repositorios y se comparten con otras personas. Una alternativa es proporcionar las credenciales (nombres de usuario, contraseñas, tokens, cualquier cosa sensible) como variables de entorno en tiempo de ejecución . Esto es posible a través del -eargumento (para --env-filevariables individuales en la CLI) o argumento (para múltiples variables en un archivo) docker run. Lea esto para usar el medio ambiente con docker-compose.

El uso --env-filees definitivamente una opción más segura ya que esto protege contra los secretos que aparecen psen los registros si se usa set -x.

Sin embargo, los entornos tampoco son particularmente seguros. Son visibles a través de docker inspecty, por lo tanto, están disponibles para cualquier usuario que pueda ejecutardocker comandos. (Por supuesto, cualquier usuario al que tenga acceso dockeren el host también tiene root de todos modos).

Mi patrón preferido es usar un script de envoltura como ENTRYPOINTo CMD. El script de envoltura puede importar primero secretos desde una ubicación externa al contenedor en tiempo de ejecución, luego ejecutar la aplicación, proporcionando los secretos. La mecánica exacta de esto varía según el entorno de tiempo de ejecución. En AWS, puede usar una combinación de roles de IAM, el Servicio de administración de claves y S3 para almacenar secretos cifrados en un depósito de S3. Algo como HashiCorp Vault o credstash es otra opción.

AFAIK no existe un patrón óptimo para usar datos confidenciales como parte del proceso de compilación. De hecho, tengo una pregunta SO sobre este tema. Puede usar docker-squash para eliminar capas de una imagen. Pero no hay funcionalidad nativa en Docker para este propósito.

Puede encontrar útiles los comentarios de shykes sobre config en contenedores .

Ben Whaley
fuente
Como se señaló en otros comentarios, habrá 2 capas (después de AGREGAR y después de la primera EJECUCIÓN) que contienen el .configarchivo.
Petr Gladkikh
1
Yer, las variables env parecen ser el mejor camino a seguir. He estado mirando esto en el contexto del desarrollo de TDDing Dockerfile.
gnoll110
55
Me preocupa que si su contraseña es una variable env, aparezca en docker inspect.
delgado
Una instalación predeterminada de Docker (en Linux) requiere privilegios de sudoer para ejecutarse docker inspect. Si el atacante ya puede sudo, quitar su contraseña de la inspección de la ventana acoplable probablemente sea bastante baja en su lista de cosas que ahora pueden salir mal. Este detalle en particular me parece un riesgo aceptable.
GrandOpener 01 de
77
@GrandOpener Eso solo se aplica a la situación en la que hay un atacante que usa su sistema. Si empujo una imagen de Docker a un repositorio, y es extraída por otra persona, no me importa si tienen sudo en su propio sistema, pero definitivamente me importa si ven secretos en el entorno que ya no se supone que estén allí.
vee_ess
75

Nuestro equipo evita poner credenciales en los repositorios, lo que significa que no están permitidos Dockerfile. Nuestra mejor práctica dentro de las aplicaciones es usar créditos de variables de entorno.

Resolvemos esto usando docker-compose .

Dentro docker-compose.yml, puede especificar un archivo que contenga las variables de entorno para el contenedor:

 env_file:
- .env

Asegúrese de agregar .enva .gitignore, luego configure las credenciales dentro del .envarchivo como:

SOME_USERNAME=myUser
SOME_PWD_VAR=myPwd

Almacene el .envarchivo localmente o en un lugar seguro donde el resto del equipo pueda tomarlo.

Ver: https://docs.docker.com/compose/environment-variables/#/the-env-file

el otro lado
fuente
15
También puede hacerlo sin un archivo .env, si lo desea. Simplemente use la propiedad del entorno en su archivo docker-compose.yml. "Las variables de entorno con solo una clave se resuelven a sus valores en la máquina en la que se ejecuta Compose, lo que puede ser útil para valores secretos o específicos del host".
D. Visser
1
dale a este hombre una galleta! :) sí, esta es una muy buena práctica. Solo quiero agregar que docs.docker.com/compose/env-file debería funcionar automáticamente, pero en Docker compose la versión 2 parece que necesita declararlo como se describe en esta respuesta.
equivalente8
55
El equipo de Docker desaconseja el uso de variables de entorno, ya que se puede ver env var a través de / proc / <pid> / environmental y docker inspeccionar. Solo ofusca la forma de obtener las credenciales para un atacante que ha obtenido acceso de root. Por supuesto, las credenciales nunca deben ser rastreadas por el CVS. Supongo que la única forma de evitar que un usuario root obtenga crédito es leer las credenciales desde la aplicación web (con la esperanza de que no actualice su archivo de entorno de proceso) desde un archivo cifrado, el proceso de descifrado solicita una contraseña de forma segura. Creo que lo intentaré con una tumba: github.com/dyne/Tomb
pawamoy
.gitignorepara que el .envarchivo con información confidencial no se registre en GitHub. Estoy bastante seguro de que esto no funcionará si lo añade.dockerignore
theUtherSide
hola @ theUtherSide, gracias por tu respuesta, tuve una pregunta, cuando no registro el .envarchivo y realizo una implementación en un servidor en las instalaciones, ¿sugieres crear el .envarchivo nuevamente en el servidor manualmente?
opensource-developer
37

Docker ahora (versión 1.13 o 17.06 y superior) tiene soporte para administrar información secreta. Aquí hay una descripción general y documentación más detallada

Existe alguna característica similar en kubernetes y DCOS

Heather QC
fuente
Algunos comandos útiles de los enlaces anteriores docker secret create:: crear un secreto docker secret inspect: mostrar información detallada sobre un secreto docker secret ls: ver todos los secretos docker secret rm: eliminar un --secretindicador secreto específico para docker service create: crear un secreto durante la creación del servicio --secret-addy--secret-rm banderas para docker service update: actualizar el valor de un secreto o eliminar un secreto durante la tarea de actualización del servicio. Los secretos de Docker están protegidos en reposo en los nodos de administrador y aprovisionados a los nodos de trabajo cuando se inicia el contenedor.
PJ
77
Sí, necesita configurar un enjambre para usar los secretos de Docker
Heather QC
11
Este es un buen comienzo para una respuesta, pero necesita mucha más información de la que está vinculada para aparecer en la respuesta misma.
Jeff Lambert
77
No estoy seguro de que esta sea la respuesta aceptada si solo funciona con enjambres. Muchas personas no están usando enjambres, pero aún necesitan pasar secretos.
John Y
9

Nunca debe agregar credenciales a un contenedor a menos que esté bien transmitiendo los créditos a quien pueda descargar la imagen. En particular, hacer y ADD credsmás tarde RUN rm credsno es seguro porque el archivo creds permanece en la imagen final en una capa intermedia del sistema de archivos. Es fácil para cualquier persona con acceso a la imagen extraerla.

La solución típica que he visto cuando necesitas créditos para pagar dependencias y es usar un contenedor para construir otro. Es decir, normalmente tiene un entorno de compilación en su contenedor base y necesita invocarlo para construir su contenedor de aplicaciones. Entonces, la solución simple es agregar el origen de su aplicación y luego RUNlos comandos de compilación. Esto es inseguro si necesitas credibilidad en eso RUN. En cambio, lo que debe hacer es colocar su fuente en un directorio local, ejecutar (como en docker run) el contenedor para realizar el paso de compilación con el directorio de origen local montado como volumen y las credenciales inyectadas o montadas como otro volumen. Una vez que se completa el paso de compilación, construye su contenedor final simplemente ADDusando el directorio de origen local que ahora contiene los artefactos construidos.

¡Espero que Docker agregue algunas funciones para simplificar todo esto!

Actualización: parece que el método en el futuro será tener compilaciones anidadas. En resumen, el dockerfile describiría un primer contenedor que se usa para construir el entorno de tiempo de ejecución y luego una segunda construcción de contenedor anidado que puede ensamblar todas las piezas en el contenedor final. De esta manera, el material de tiempo de construcción no está en el segundo contenedor. Se trata de una aplicación Java donde necesita el JDK para compilar la aplicación pero solo el JRE para ejecutarla. Se están discutiendo una serie de propuestas, lo mejor es comenzar desde https://github.com/docker/docker/issues/7115 y seguir algunos de los enlaces para propuestas alternativas.

TvE
fuente
7

Una alternativa al uso de variables de entorno, que pueden ser complicadas si tiene muchas de ellas, es usar volúmenes para hacer que un directorio en el host sea accesible en el contenedor.

Si coloca todas sus credenciales como archivos en esa carpeta, entonces el contenedor puede leer los archivos y usarlos como desee.

Por ejemplo:

$ echo "secret" > /root/configs/password.txt
$ docker run -v /root/configs:/cfg ...

In the Docker container:

# echo Password is `cat /cfg/password.txt`
Password is secret

Muchos programas pueden leer sus credenciales desde un archivo separado, por lo que de esta manera solo puede apuntar el programa a uno de los archivos.

Malvinoso
fuente
5

solución de tiempo de ejecución solamente

docker-compose también proporciona una solución de modo sin enjambre (desde v1.11: Secretos que utilizan montajes de enlace ).

Los secretos se montan como archivos a continuación /run/secrets/por docker-compose. Esto resuelve el problema en tiempo de ejecución (ejecutando el contenedor), pero no en tiempo de compilación (compilando la imagen), porque /run/secrets/no está montado en tiempo de compilación. Además, este comportamiento depende de ejecutar el contenedor con docker-compose.


Ejemplo:

Dockerfile

FROM alpine
RUN cat /run/secrets/password
CMD sleep inifinity

docker-compose.yml

version: '3.1'
services:
  app:
    build: .
    secrets:
      - password

secrets:
  password:
    file: password.txt

Para construir, ejecute:

docker-compose up -d

Otras lecturas:

Murmel
fuente
2

Con Docker v1.9 puede usar la instrucción ARG para buscar argumentos pasados ​​por línea de comando a la imagen en la acción de compilación . Simplemente use --build-arg bandera . Por lo tanto, puede evitar mantener una contraseña explícita (u otra información sensible) en el Dockerfile y pasarlas sobre la marcha.

fuente: https://docs.docker.com/engine/reference/commandline/build/ http://docs.docker.com/engine/reference/builder/#arg

Ejemplo:

Dockerfile

FROM busybox
ARG user
RUN echo "user is $user"

compilar comando de imagen

docker build --build-arg user=capuccino -t test_arguments -f path/to/dockerfile .

durante la construcción imprimir

$ docker build --build-arg user=capuccino -t test_arguments -f ./test_args.Dockerfile .

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM busybox
 ---> c51f86c28340
Step 2 : ARG user
 ---> Running in 43a4aa0e421d
 ---> f0359070fc8f
Removing intermediate container 43a4aa0e421d
Step 3 : RUN echo "user is $user"
 ---> Running in 4360fb10d46a
**user is capuccino**
 ---> 1408147c1cb9
Removing intermediate container 4360fb10d46a
Successfully built 1408147c1cb9

¡Espero eso ayude! Adiós.

NickGnd
fuente
26
De acuerdo con documentos de Docker ARG : "No se recomienda a las variables en tiempo de creación utilizan para pasar secretos como llaves de GitHub, credenciales de usuario, etc"
Lie Ryan
3
Simplemente preguntándome por qué Docker no recomienda usar --build-arg var=secretpara pasar una clave privada SSH a una imagen, no hay justificación documentada. ¿Alguien puede explicarlo?
Henk Wiersema
2
@HenkWiersema La información del proceso, los registros y el historial de comandos no son seguros. La información del proceso está disponible públicamente y eso incluye todos los parámetros de la línea de comandos. Con frecuencia, estas llamadas terminan en registros que también pueden ser públicos. No es raro que un atacante inspeccione información sobre procesos en ejecución y rastree archivos de registro públicos en busca de secretos. Incluso cuando no es público, podría almacenarse en su historial de comandos, lo que facilitaría que alguien obtenga secretos a través de una cuenta no administrativa.
tu-Reinstate Monica-dor duh
2
¿Cuál es la forma recomendada de proporcionar las credenciales que se necesitan en el momento de la compilación? Por ejemplo, ¿una imagen que necesita acceso aws s3 para obtener un gran conjunto de datos que residirá dentro de la imagen?
ely
3
Me imagino que la razón por la que no se recomienda es porque docker historyexpone build-arg/ ARGvariables. Uno puede extraer cualquier imagen, inspeccionarla y ver los secretos pasados ​​durante la construcción como un parámetro build-arg/ ARG.
vee_ess
2

Mi enfoque parece funcionar, pero probablemente sea ingenuo. Dime por qué está mal.

El subcomando de historial expone los ARG establecidos durante la construcción de Docker, por lo que no hay que ir allí. Sin embargo, cuando se ejecuta un contenedor, las variables de entorno proporcionadas en el comando de ejecución están disponibles para el contenedor, pero no forman parte de la imagen.

Entonces, en el Dockerfile, realice una configuración que no implique datos secretos. Establecer un CMD de algo así /root/finish.sh. En el comando de ejecución, use variables de entorno para enviar datos secretos al contenedor. finish.shutiliza las variables esencialmente para terminar las tareas de compilación.

Para facilitar la administración de los datos secretos, colóquelos en un archivo cargado por Docker Run con el --env-fileinterruptor. Por supuesto, mantenga el archivo en secreto. .gitignorey tal.

Para mí, finish.shejecuta un programa Python. Comprueba para asegurarse de que no se ha ejecutado antes, luego finaliza la configuración (por ejemplo, copia el nombre de la base de datos en Django settings.py).

Kieran Mathieson
fuente
2

Hay un nuevo comando docker para la gestión de "secretos". Pero eso solo funciona para grupos de enjambre.

docker service create
--name my-iis
--publish target=8000,port=8000
--secret src=homepage,target="\inetpub\wwwroot\index.html"
microsoft/iis:nanoserver 
José Ibáñez
fuente
-2

Si bien estoy totalmente de acuerdo, no hay una solución simple. Sigue habiendo un solo punto de falla. O el dockerfile, etcd, y así sucesivamente. Apcera tiene un plan que parece un compinche: autenticación dual. En otras palabras, dos contenedores no pueden hablar a menos que haya una regla de configuración de Apcera. En su demostración, el uid / pwd estaba limpio y no podía reutilizarse hasta que el administrador configurara el enlace. Para que esto funcione, sin embargo, probablemente significó parchear Docker o al menos el complemento de red (si existe).

Ricardo
fuente
2
¿Hay alguna respuesta a la pregunta que se hace?
Abhijit Sarkar