在Python Web Developer课程开始之前准备了文章的翻译。
构建Docker映像时,您可能需要一些秘密,例如私有软件包存储库的密码。您不希望这个秘密出现在图像中,因为这样,任何有权访问该图像的人都可以访问您的私有存储库。注意:如果您认为“为什么不只使用环境变量?”,则在创建映像时将其用于运行时的机密。本文重点介绍在使用Docker文件创建映像时使用的构建机密。
较新版本的Docker使用实验性BuildKit服务维护秘密,在Docker Compose 1.25及更高版本中,您已经可以使用BuildKit创建映像。不幸的是,截至2020年3月,仍在开发安全处理Compose机密的功能。那么现在该怎么办?在今天的文章中,我将展示如何使用相同的Dockerfile安全地创建秘密映像,而又不会失去使用Docker Compose进行快速开发的好处。使用dockerfile的两个选项
在Docker Compose中使用相同的Dockerfile进行生产和本地开发非常方便。通常,您将Dockerfile与Compose的构建功能一起使用:version: "3.7"
services:
yourapp:
build:
context: "."
然后,您可以执行以下操作:$ docker-compose up
使用此命令,您可以(重新)组装映像,然后运行它。为了在生产中使用,您可以收集图像并通过push发送:$ docker build -t myimage .
$ docker push myimage
一切顺利。但是,如果您需要秘密构建该怎么办?第一次尝试(不安全)
假设您有一个需要秘密构建的脚本,例如,从私有DevPI存储库中下载Python软件包。为简单起见,我们将在帮助下简单地推导出秘密,use-secret.sh
以表明我们拥有该秘密。
set -euo pipefail
echo "Secret is: $THEPASSWORD"
您可以简单地使用Docker构建参数传递秘密,因为它在Docker Compose中得到了广泛支持。注意:超出我们讨论的范围,我想说的是,在本文中使用Docker文件不是最佳实践,但是,过于复杂可能会干扰传达本文的主要含义。
因此,如果要在使用Docker的生产环境中运行Python应用程序,可以采用以下两种好的方法:FROM python:3.8-slim-buster
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
f88b19ca8e65 About a minute ago /bin/sh -c
...
有权访问该图像的任何人都可以识别您的密码。那该怎么办呢?BuildKit的秘密(部分解决方案)
BuildKit是用于创建Docker映像的新的(仍在试验中)解决方案,除其他外,它还增加了在组装过程中安全使用机密的支持。Docker Compose自v1.25起提供了BuildKit支持。但是有一个问题:Docker Compose仍然不支持BuildKit secrets功能。现在对此工作正在进行中,但是截至2020年3月,还没有现成的解决方案,更不用说一个稳定的版本了。因此,我们将结合这两种方法:- Docker Compose将继续使用构建参数来传递秘密。
- 对于使用docker build构建的生产映像,我们使用BuildKit传递秘密。
这样,我们可以使用相同的Dockerfile在本地和生产环境中工作。BuildKit可以按以下方式使用机密:带有机密的文件在执行RUN命令时会挂载在一个临时目录中,例如/var/secrets/thepassword
。由于它是在执行RUN命令期间安装的,因此不会将其添加到最终映像中。我们将修改文件use_secret.sh
以检查是否存在这样的临时文件。如果存在,它将使用其环境变量设置$THEPASSWORD
。如果文件不存在,那么我们将返回到环境变量。也就是说,$THEPASSWORD
可以使用BuildKit或build参数安装它:
set -euo pipefail
if [ -f /run/secrets/thepassword ]; then
export THEPASSWORD=$(cat /run/secrets/thepassword)
fi
echo "Secret is: $THEPASSWORD"
然后,我们将修改Dockerfile以添加BuildKit并安装秘密:
FROM python:3.8-slim-buster
ARG THEPASSWORD
COPY use_secret.sh .
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需要使用Docker的CLI版本以及BuildKit。我们还将把机密写入文件:$ export DOCKER_BUILDKIT=1
$ export COMPOSE_DOCKER_CLI_BUILD=1
$ echo 's3krit' > /tmp/mypassword
对于Compose,我们使用build参数:$ docker-compose build --progress=plain \
--no-cache 2>&1 | grep Secret
请注意,--no-cache
如果您自己执行上述所有操作,则必须了解该映像将真正重建。在现实生活中,可以省略此参数。2>&1
重定向stderr
到stdout
正确的操作grep
。当我们准备在生产环境上进行构建时,我们使用具有BuildKit秘密功能的docker build:$ docker build --no-cache -t myimage \
--secret id=thepassword,src=/tmp/mypassword \
--progress=plain . 2>&1 | grep Secret
安全吗?
让我们确保秘密不可见:$ 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 .
...
万岁!我们使用Compose和将秘密传递给了相同的Dockerfile docker build
,在后一种情况下并未从程序集中揭示秘密。
了解有关该课程的更多信息。