https://habr.com/ru/company/otus/blog/501580/- Блог компании OTUS. Онлайн-образование
- Разработка веб-сайтов
- Python
- Программирование
Перевод статьи подготовлен в преддверии старта курса «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
, и в последнем случае не раскрыли секрет из сборки.
Узнать подробнее о курсе.