Secretos seguros al construir en Docker Compose

Se preparó una traducción del artículo antes del inicio del curso Python Web Developer .




Cuando crea una imagen de Docker, es posible que necesite secretos, como una contraseña para un repositorio de paquetes privado. No desea que este secreto termine en la imagen, porque cualquiera que tenga acceso a la imagen tendrá acceso a su repositorio privado.
Nota : Si piensa “¿Por qué no solo usar variables de entorno?”, Que se usan para secretos en tiempo de ejecución al crear una imagen. Este artículo se centra en los secretos de compilación que se utilizan al crear una imagen con un archivo Docker.
Las versiones más recientes de Docker mantienen secretos usando el servicio experimental BuildKit , y en Docker Compose 1.25 y posterior, ya puede crear imágenes usando BuildKit. Desafortunadamente, a partir de marzo de 2020, la capacidad de trabajar de manera segura con los secretos de Compose aún está en desarrollo .

Entonces, ¿qué hacer ahora?

En el artículo de hoy, mostraré cómo puede usar el mismo Dockerfile para crear imágenes secretas de forma segura sin perder los beneficios del rápido desarrollo con Docker Compose.

Dos opciones para usar su dockerfile


Es muy conveniente utilizar el mismo Dockerfile para la producción y el desarrollo local con Docker Compose. Por lo general, utiliza el Dockerfile junto con la función de compilación de Compose:

version: "3.7"
services:
  yourapp:
    build:
      context: "."	

Entonces puedes hacer:

$ docker-compose up

Con este comando, puede (re) ensamblar la imagen y luego ejecutarla.
Para usar en producción, recolecta la imagen y la envía con push :

$ docker build -t myimage .
$ docker push myimage

Y mientras todo va bien. Pero, ¿y si necesitas una construcción secreta?

Primer intento (inseguro)


Suponga que tiene un script que necesita una compilación secreta, por ejemplo, para descargar un paquete de Python desde un repositorio privado de DevPI . Por simplicidad, simplemente derivaremos el secreto con la ayuda use-secret.shpara demostrar que lo tenemos.

#!/bin/bash
set -euo pipefail

echo "Secret is: $THEPASSWORD"

Simplemente puede transmitir el secreto utilizando los argumentos de compilación de Docker, ya que son compatibles en todas partes, incluso en Docker Compose.
Nota : yendo más allá del alcance de nuestra discusión, quiero decir que el uso de archivos Docker en este artículo no es la mejor práctica, sin embargo, la complejidad excesiva puede interferir con la transmisión del significado principal del artículo.
Por lo tanto, si desea ejecutar su aplicación Python en producción con Docker, aquí hay dos buenas maneras de hacerlo:


FROM python:3.8-slim-buster
# Using ARG for build secrets is INSECURE!
ARG THEPASSWORD
COPY use_secret.sh .
RUN ./use_secret.sh

Podemos escribir docker-compose.yml, que se transmitirá en secreto:

version: "3.7"
services:
  yourapp:
    build:
      context: "."
      args:
        THEPASSWORD: "s3kr!t"

Para el trabajo local, puede ejecutar o crear una imagen con Compose:

$ docker-compose build | grep Secret
Secret is: s3kr!t

Y todo está bien.

Y también podemos ensamblar la imagen usando Docker, en preparación para moverla al registro de imágenes:

$ docker build -t myimage --build-arg THEPASSWORD=s3krit . | grep Secret
Secret is: s3krit

Hacerlo no es seguro: nunca lo hagas . Si decidimos mirar las capas de la imagen, ¡veremos el secreto en ella!

$ docker history myimage
IMAGE               CREATED              CREATED BY                                      SIZE
c224231ec30b        47 seconds ago       |1 THEPASSWORD=s3krit /bin/sh -c ./use_secre…   0B
6aef62acf0db        48 seconds ago       /bin/sh -c #(nop) COPY file:7aa28bbe6595e0d5…   62B
f88b19ca8e65        About a minute ago   /bin/sh -c #(nop)  ARG THEPASSWORD              0B
...

Cualquiera que obtenga acceso a esta imagen reconocerá su contraseña. ¿Qué se puede hacer entonces?

BuildKit Secrets (Solución parcial)


BuildKit es una solución nueva (y aún experimental) para crear imágenes Docker, que, entre otras cosas, agrega soporte para el uso seguro de secretos durante el ensamblaje . Docker Compose tiene soporte BuildKit desde v1.25.

Pero hay un problema: Docker Compose aún no admite la funcionalidad de secretos de BuildKit. Ahora se está trabajando en esto, pero a partir de marzo de 2020, no hay soluciones preparadas, sin mencionar una versión estable.

Por lo tanto, vamos a combinar estos dos enfoques:

  • Docker Compose continuará usando argumentos de construcción para pasar secretos;
  • Para una imagen de producción creada con Docker Build, utilizamos BuildKit para transmitir secretos.

De esta manera podemos usar el mismo Dockerfile para trabajar localmente y en producción.
BuildKit funciona con secretos de la siguiente manera: el archivo con secretos se monta en un directorio temporal mientras se ejecuta el comando EJECUTAR, por ejemplo, en /var/secrets/thepassword. Como se monta durante la ejecución del comando EJECUTAR, no se agregará a la imagen final.

Modificaremos el archivo use_secret.shpara verificar si existe dicho archivo temporal. Si existe, utiliza su configuración de variable de entorno $THEPASSWORD. Si el archivo no existe, volveremos a la variable de entorno. Es decir, se $THEPASSWORDpuede instalar usando BuildKit o construir argumentos:

#!/bin/bash
set -euo pipefail
if [ -f /run/secrets/thepassword ]; then
   export THEPASSWORD=$(cat /run/secrets/thepassword)
fi

echo "Secret is: $THEPASSWORD"

Luego modificaremos el Dockerfile para agregar BuildKit y montar el secreto:

# syntax = docker/dockerfile:1.0-experimental
FROM python:3.8-slim-buster
# Only use the build arg for local development:
ARG THEPASSWORD
COPY use_secret.sh .
# Mount the secret to /run/secrets:
RUN --mount=type=secret,id=thepassword ./use_secret.sh

docker-compose.ymlNo cambiamos el archivo :

version: "3.7"
services:
  yourapp:
    build:
      context: "."
      args:
        THEPASSWORD: "s3kr!t"

Ahora debe definir dos variables de entorno, una de las cuales le indicará a Docker que necesita usar BuildKit, la segunda que Compose necesita usar la versión CLI de Docker y, por lo tanto, BuildKit. También escribiremos el secreto en el archivo:

$ export DOCKER_BUILDKIT=1
$ export COMPOSE_DOCKER_CLI_BUILD=1
$ echo 's3krit' > /tmp/mypassword

Con Compose, usamos los argumentos de construcción:

$ docker-compose build --progress=plain \
    --no-cache 2>&1 | grep Secret
#12 0.347 Secret is: s3kr!t

Tenga en cuenta que es --no-cachenecesario comprender que la imagen realmente se reconstruirá si usted mismo ejecuta todo lo anterior. En la vida real, este parámetro puede omitirse. 2>&1redirigir stderra stdoutpara el correcto funcionamiento grep.

Cuando estamos listos para construir en producción, usamos Docker Build con la funcionalidad de secretos de BuildKit:

$ docker build --no-cache -t myimage \
    --secret id=thepassword,src=/tmp/mypassword \
    --progress=plain . 2>&1 | grep Secret
#12 0.359 Secret is: s3krit

¿Es seguro?


Asegurémonos de que el secreto no sea visible:

$ docker history myimage
IMAGE               CREATED             CREATED BY                                      SIZE
a77f3c32b723        25 seconds ago      RUN |1 THEPASSWORD= /bin/sh -c ./use_secret.…   0B
<missing>           25 seconds ago      COPY use_secret.sh . # buildkit                 160B
...

¡Hurra! Pasamos el secreto al mismo Dockerfile usando Compose y docker build, y en el último caso, no revelamos el secreto de la asamblea.



Aprende más sobre el curso.



All Articles