我最近一直在尝试使用Docker构建一些服务来解决这个问题,并且一直在唠叨我的一直是将密码放在Dockerfile中。我是一名开发人员,因此在源代码中存储密码感觉就像一拳。这应该是一个问题吗?关于如何在Dockerfiles中处理密码有什么好的约定吗?
绝对是一个值得关注的问题。 Dockerfiles通常签入存储库并与其他人共享。另一种方法是提供任何凭据(用户名,密码,令牌,任何敏感的)as environment variables at runtime。这可以通过-e
参数(对于CLI上的单个变量)或--env-file
参数(对于文件中的多个变量)到docker run
来实现。阅读this以使用docker-compose环境。
使用--env-file
绝对是一个更安全的选择,因为这可以防止ps
或日志中出现的秘密,如果使用set -x
。
然而,env vars也不是特别安全。它们通过docker inspect
可见,因此任何可以运行docker
命令的用户都可以使用它们。 (当然,任何能够在主机上访问docker
的用户还是has root。)
我首选的模式是使用包装脚本作为ENTRYPOINT
或CMD
。包装器脚本可以在运行时首先将秘密从外部位置导入容器,然后执行应用程序,提供机密。具体的机制根据您的运行时环境而有所不同。在AWS中,您可以结合使用IAM角色(Key Management Service和S3)将加密的密钥存储在S3存储桶中。像HashiCorp Vault或credstash这样的东西是另一种选择。
AFAIK没有使用敏感数据作为构建过程的一部分的最佳模式。事实上,我有一个关于这个主题的SO question。您可以使用docker-squash从图像中删除图层。但是为了这个目的,Docker中没有本机功能。
你可能会发现shykes comments on config in containers很有用。
12-Factor app methodology告诉我,任何配置都应存储在环境变量中。
Docker compose可以在配置中执行variable substitution,因此可以用来将密码从主机传递到docker。
虽然我完全同意没有简单的解决方案。仍然存在单点故障。 dockerfile,etcd等等。 Apcera有一个看似sidekick的计划 - 双重身份验证。换句话说,除非有Apcera配置规则,否则两个容器无法通话。在他们的演示中,uid / pwd处于清晰状态,在管理员配置链接之前无法重复使用。然而,为了实现这一点,它可能意味着修补Docker或至少是网络插件(如果有这样的东西)。
我们的团队避免将凭证放在存储库中,这意味着他们不被允许进入Dockerfile
。我们在应用程序中的最佳实践是使用来自环境变量的信用。
我们使用docker-compose
来解决这个问题。
在docker-compose.yml
中,您可以指定包含容器的环境变量的文件:
env_file:
- .env
确保将.env
添加到.gitignore
,然后在.env
文件中设置凭据,如:
SOME_USERNAME=myUser
SOME_PWD_VAR=myPwd
将.env
文件存储在本地或存储在团队其他成员可以抓取的安全位置。
见:https://docs.docker.com/compose/environment-variables/#/the-env-file
Docker现在(版本1.13或17.06及更高版本)支持管理机密信息。这是一个overview和更详细的documentation
类似的特征存在于kubernetes和DCOS
您永远不应该向容器添加凭据,除非您可以向任何可以下载图像的人广播信用。特别是,做和ADD creds
以及后来的RUN rm creds
不安全,因为creds文件保留在中间文件系统层的最终图像中。任何有权访问图像的人都可以轻松提取图像。
我看到的典型解决方案,当你需要信誉来检查依赖关系时,就是使用一个容器来构建另一个容器。即,通常在基础容器中有一些构建环境,您需要调用它来构建应用程序容器。因此,简单的解决方案是添加您的应用程序源,然后RUN
构建命令。如果您需要RUN
中的信用,这是不安全的。相反,你所做的是将你的源放入一个本地目录,运行(如在docker run
中)容器来执行构建步骤,将本地源目录作为卷挂载,并将creds注入或挂载为另一个卷。一旦构建步骤完成,您只需通过ADD
ing本地源目录(现在包含构建的工件)来构建最终容器。
我希望Docker增加一些功能来简化这一切!
更新:看起来前进的方法将是嵌套构建。简而言之,dockerfile将描述用于构建运行时环境的第一个容器,然后描述可以将所有部分组装到最终容器中的第二个嵌套容器构建。这样构建时间的东西不在第二个容器中。这是一个Java应用程序,您需要JDK来构建应用程序,但只需要JRE来运行它。有很多提案正在讨论中,最好从https://github.com/docker/docker/issues/7115开始,并按照一些替代提案的链接。
使用环境变量的替代方法是,如果您拥有大量环境变量,可能会变得混乱,就是使用卷来在容器中访问主机上的目录。
如果您将所有凭据作为文件放在该文件夹中,则容器可以读取文件并随意使用它们。
例如:
$ 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
许多程序可以从单独的文件中读取其凭据,因此您可以通过这种方式将程序指向其中一个文件。
使用Docker v1.9,您可以使用ARG指令将命令行传递的参数提取到构建操作上的映像。只需使用--build-arg标志即可。因此,您可以避免在Dockerfile上保留明确的密码(或其他敏感信息)并动态传递它们。
来源:https://docs.docker.com/engine/reference/commandline/build/ http://docs.docker.com/engine/reference/builder/#arg
例:
Dockerfile
FROM busybox
ARG user
RUN echo "user is $user"
构建图像命令
docker build --build-arg user=capuccino -t test_arguments -f path/to/dockerfile .
在打印期间打印
$ 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
希望能帮助到你!再见。
运行时唯一的解决方案
docker-compose还提供非群模式解决方案(自v1.11:Secrets using bind mounts)。
这些秘密通过docker-compose安装在/run/secrets/
下面的文件中。这解决了运行时(运行容器)的问题,但不是在构建时(构建映像),因为/run/secrets/
没有在构建时安装。此外,此行为取决于使用docker-compose运行容器。
例:
Dockerfile
FROM alpine
RUN cat /run/secrets/password
CMD sleep inifinity
泊坞窗,compose.yml
version: '3.1'
services:
app:
build: .
secrets:
- password
secrets:
password:
file: password.txt
要构建,执行:
docker-compose up -d
进一步阅读:
我的方法似乎有效,但可能是天真的。告诉我为什么这是错的。
在docker build期间设置的ARG由history子命令公开,所以不要去那里。但是,在运行容器时,run命令中给出的环境变量可用于容器,但不是映像的一部分。
因此,在Dockerfile中,进行不涉及秘密数据的设置。设置像/root/finish.sh
这样的CMD。在run命令中,使用环境变量将秘密数据发送到容器中。 finish.sh
主要使用变量来完成构建任务。
要更轻松地管理秘密数据,请将其放入由docker运行的--env-file
开关加载的文件中。当然,保密文件。 .gitignore
等。
对我来说,finish.sh
运行Python程序。它检查以确保它之前没有运行,然后完成设置(例如,将数据库名称复制到Django的settings.py
中)。
有一个新的docker命令[1]用于“秘密”管理,但这只适用于群集群。
docker service create
--name my-iis
--publish target=8000,port=8000
--secret src=homepage,target="\inetpub\wwwroot\index.html"
microsoft/iis:nanoserver