Безопасная работа с секретами при сборке в Docker Compose

—

от автора

Перевод статьи подготовлен в преддверии старта курса «Web-разработчик на Python».


Когда вы собираете Docker-образ вам могут понадобиться секреты, например, пароль к приватному репозиторию пакетов. Вы не хотите, чтобы этот секрет в конечном итоге оказался в образе, потому что тогда любой, кто получит доступ к образу, получит доступ и к вашу приватному репозиторию.

Примечание: Если вы думаете «Почему бы просто не использовать переменные среды?», которые используются для секретов в рантайме при создании образа. Эта статья посвящена секретам сборки, которые используются при создании образа с помощью Docker-файла.

Более новые версии Docker поддерживают секреты с помощью экспериментального сервиса BuildKit, а в Docker Compose 1.25 и более поздних версиях уже можно создавать образы с помощью BuildKit. К сожалению, по состоянию на март 2020 года, возможность безопасной работы с секретами из Compose все еще находится в процессе разработки.

Так что же теперь делать?

В сегодняшней статье я покажу, как можно использовать один и тот же Dockerfile для безопасного создания образов с секретами, но при этом не упускать преимущества быстрой разработки с помощью Docker Compose.

Два варианта использования вашего Dockerfile

Очень удобно использовать один и тот же Dockerfile для продакшена и для локальной разработки с Docker Compose. Обычно вы используете Dockerfile вместе с функцией сборки из Compose:

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

Затем вы можете сделать:

$ docker-compose up

С помощью этой команды вы можете (пере)собрать образ, а затем запустить его.
Для использования на продакшене, вы собираете образ и отправляете его с push:

$ docker build -t myimage . $ docker push myimage

И пока все идет хорошо. Но что делать, если вам нужна сборка с секретом?

Первая попытка (небезопасная)

Допустим, у вас есть скрипт, которому нужна сборка с секретом, например, чтобы загрузить пакет Python из частного репозитория DevPI. Для простоты, мы будем просто выводить секрет с помощью use-secret.sh, чтобы показать, что он у нас есть.

#!/bin/bash set -euo pipefail  echo "Secret is: $THEPASSWORD"

Можно простодушно передать секрет с помощью аргументов сборки Docker, поскольку они поддерживаются везде, в том числе и в Docker Compose.

Примечание: Выходя за пределы нашей дискуссии, хочу сказать, что использование Docker-файлов в этой статье – не лучшая практика, однако излишняя сложность может помешать донести основной смысл статьи.

Поэтому, если вы захотите запустить свое приложение на Python на продакшене с Docker, вот два хороших способа это сделать:

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

Мы можем написать docker-compose.yml, который будет передан в секрете:

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

Для локальной работы можно запустить или собрать образ с помощью Compose:

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

И все хорошо.

А еще мы можем собрать образ с помощью Docker, в качестве подготовки к перемещению его в реестр образов:

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

Так делать небезопасно: никогда так не делайте. Если мы решим просмотреть слои образа, то увидим секрет, лежащий в нем!

$ 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 ...

Любой, кто получит доступ к этому образу, узнает ваш пароль. Что в таком случае можно сделать?

Секреты BuildKit (частичное решение проблемы)

BuildKit – это новое (и все еще экспериментальное) решение для создания Docker-образов, которое помимо всего прочего добавляет поддержку безопасного использования секретов при сборке. В Docker Compose есть поддержка BuildKit с v1.25.

Но есть одна проблема: Docker Compose до сих пор не поддерживает функционал секретов BuildKit. Сейчас ведется работа над этим, однако по состоянию на март 2020 года, готовых решений нет, не говоря уже о стабильном релизе.

Поэтому мы собираемся объединить два этих подхода:

  • Docker Compose продолжит пользоваться аргументами при сборке, чтобы передавать секреты;
  • Для образа на продакшене, собранного с помощью docker build, мы используем BuildKit для передачи секретов.

Так мы сможем использовать один и тот же Dockerfile для работы локально и на продакшене.
BuildKit работает с секретами следующим образом: файл с секретами монтируется во временную директорию, пока выполняется команда RUN, например, в /var/secrets/thepassword. Поскольку он монтируется в течение выполнения команды RUN, он не будет добавлен в конечный образ.

Мы изменим файл use_secret.sh, чтобы он проверял, существует ли такой временный файл. Если существует, он использует его установки переменной среды $THEPASSWORD. Если файла не существует, то мы вернемся к переменной среды. То есть $THEPASSWORD может быть установлена с помощью BuildKit или аргументов сборки:

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

Затем мы изменим Dockerfile, чтобы добавить BuildKit и смонтировать секрет:

# 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.yml мы не меняем:

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

Теперь нужно определить две переменные среды, одна из которых будет говорить Docker, что нужно использовать BuildKit, вторая, что Compose нужно использовать CLI-версию Docker и, следовательно, BuildKit. Также мы запишем секрет в файл:

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

С Compose используем аргументы для сборки:

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

Обратите внимание, что --no-cache нужен, чтобы понять, что образ действительно пересоберется, если вы сами запустите все вышеприведенное. В реальной жизни этот параметр можно опустить. 2>&1 перенаправит stderr на stdout для корректной работы grep.

Когда мы будем готовы к сборке на продакшен, мы используем docker build с функционалом секретов из 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

Безопасно ли это?

Давайте убедимся, что секрет не виден:

$ 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 ...

Ура! Мы передали секрет в один и тот же Dockerfile, используя Compose и docker build, и в последнем случае не раскрыли секрет из сборки.


Узнать подробнее о курсе.


ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/501580/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *