2014-03-26 203 views
85

最近我一直在试验Docker构建一些服务来玩玩,而一件让我唠叨的事情就是将密码放在Dockerfile中。我是一名开发人员,因此在源代码中存储密码看起来就像是一张脸。这应该是一个问题吗?是否有任何关于如何在Dockerfiles中处理密码的良好惯例?Docker和安全密码

+6

有Github上一个开放的问题,请求有关泊坞窗和秘密最佳实践,问题就在这里:https://github.com/ docker/docker/issues/13490 –

回答

50

绝对值得关注。 Dockerfiles通常检入存储库并与其他人共享。另一种方法是提供任何凭据(用户名,密码,令牌,任何敏感内容)as environment variables at runtime。这可以通过参数-e(对于CLI中的各个变量)或--env-file参数(对于文件中的多个变量)至docker run来实现。

使用--env-file绝对是一个更安全的选择,因为这可以防止没有显示在ps或日志如果使用set -x的秘密。

但是,环境变量也不是特别安全。它们通过docker inspect可见,因此它们可供任何可以运行docker命令的用户使用。 (当然,可以访问docker在主机上也has root反正。任何用户)

我的优选模式是用一个包装脚本作为ENTRYPOINTCMD。包装脚本可以首先将外部位置的秘密在运行时导入容器,然后执行应用程序,提供秘密。其确切的机制取决于您的运行时环境。在AWS中,您可以使用IAM角色组合Key Management Service和S3将加密的秘密存储在S3存储桶中。类似HashiCorp Vaultcredstash是另一种选择。

AFAIK没有使用敏感数据作为构建过程的一部分的最佳模式。事实上,我在这个主题上有一个SO question。您可以使用docker-squash从图像中移除图层。但是Docker中没有用于此目的的本地功能。

您可能会觉得shykes comments on config in containers有用。

+0

正如其他注释中所指出的那样,在包含'.config'文件的地方会有2层(在ADD和第一次运行之后)。 –

+1

你看,env变量似乎是最好的选择。我一直在TDDing Dockerfile开发的背景下看这个。 – gnoll110

+3

我担心如果你的密码是一个env变量,它会出现在'docker inspect'中。 – slim

8

除非您可以向任何可以下载图像的人广播信誉,否则不应该向容器添加凭证。特别是,ADD creds和更高版本RUN rm creds不安全,因为creds文件保留在中间文件系统层的最终映像中。任何有权访问该映像的人都很容易提取它。

我看到的典型解决方案是,您需要对结帐依赖关系进行信任,并且这样使用一个容器来构建另一个容器。也就是说,通常你的基本容器中有一些构建环境,你需要调用它来构建你的应用容器。所以简单的解决方案是添加你的应用程序源码,然后添加构建命令。这是不安全的,如果你需要在RUN的信用。相反,您所做的是将源代码放入本地目录,运行(如docker run)容器,执行构建步骤,将本地源目录安装为卷,并将信任注入或挂载为另一个卷。构建步骤完成后,您只需通过ADD即可构建最终容器,其中包含现在包含构建工件的本地源目录。

我希望Docker增加一些功能来简化所有这些!

更新:看起来像前进的方法将有嵌套的构建。简而言之,dockerfile将描述用于构建运行时环境的第一个容器,然后描述第二个嵌套容器构建,可以将所有构件组装到最终容器中。这样构建时间的东西不在第二个容器中。这是一个Java应用程序,您需要JDK来构建应用程序,但只有JR​​E才能运行它。有许多建议正在讨论中,最好从https://github.com/docker/docker/issues/7115开始,并按照其他建议的一些链接进行。

0

虽然我完全同意没有简单的解决方案。继续存在单点故障。 dockerfile,etcd等等。 Apcera的计划看起来像搭档 - 双重身份验证。换句话说,除非有Apcera配置规则,否则两个容器不能通话。在他们的演示中,uid/pwd清晰并且在管理员配置链接之前无法重用。然而,为了这个工作,这可能意味着修补Docker或者至少网络插件(如果有这样的事情)。

46

我们的团队避免将凭据放入存储库,这意味着他们不允许在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

+8

如果您愿意,您也可以在没有.env文件的情况下执行此操作。只需在docker-compose.yml文件中使用[环境属性](https://docs.docker.com/compose/compose-file/#environment)即可。 _“只有一个密钥的环境变量会在计算机上解析为它们的值,因此Compose正在运行,这对秘密或主机特定的值有帮助。”_ –

+1

给这个人一个cookie! :)是的,这是非常好的做法,我只是想补充一点,https://docs.docker.com/compose/env-file/这应该会自动工作,但在泊坞窗撰写版本2似乎你需要声明它描述这个答案。 – equivalent8

+0

Docker团队本身不鼓励使用环境变量,因为可以通过/ proc//environ和docker检查来看env var。它只是混淆了获取获得root权限的攻击者证书的方式。当然,证书不应该由CVS跟踪。我想阻止root用户获得信任的唯一方法是从加密文件中读取Web应用程序中的凭据(希望它不更新其proc environ文件),解密过程会安全地询问密码。我想我会试着用一个坟墓:https://github.com/dyne/Tomb – Pawamoy

3

随着泊坞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 

希望它能帮助!再见。

+21

根据[Docker的ARG文档](https://docs.docker.com/engine/reference/builder/#arg):“它是不建议使用构建时变量来传递秘密,如github密钥,用户凭据等“ –

+3

只是想知道为什么Docker不愿意使用'--build-arg var = secret'来将SSH私钥传递到映像中,所以没有理由记录。任何人都可以解释吗? –

+1

@HenkWiersema过程信息,日志和命令历史记录是不安全的。过程信息可以公开获得,包括所有的命令行参数。通常这些电话最终会在可以公开的日志中出现。攻击者检查正在运行的进程的信息并拖放公用日志文件以获取机密信息并不罕见。即使它不是公开的,它可以存储在您的命令历史记录中,这可以让人们通过非管理帐户轻松获取秘密。 – tudor

1

作为使用环境变量的一种替代方法,如果您拥有大量环境变量,可能会变得混乱,那就是使用卷在容器中访问主机上的目录。

如果您将所有凭据作为文件放在该文件夹中,则容器可以读取文件并按需要使用它们。

例如:

$ 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 

许多程序可以从一个单独的文件中读取他们的凭据,所以这样你可以指向程序文件之一。

16

Docker现在(版本1.13或17.06及更高版本)支持管理机密信息。下面是一个overview和更详细的documentation

类似的功能存在于kubernetesDCOS

+3

这应该是现在接受的答案。 –

+0

上面链接的一些有用的命令:'docker secret create':创建一个秘密'docker secret inspect':显示关于秘密的详细信息'docker secret ls':查看所有秘密'docker secret rm':删除特定秘密' Docker服务创建的秘密标志:在服务创建时创建一个秘密''docker服务更新'的秘密 - 添加'和'--secret-rm'标志:更新秘密的值或者移除一个秘密在服务更新任务期间。在容器启动过程中,Docker机密在经理节点上受到保护,并提供给工作节点。 –

+2

这只适用于码头群,对不对? – sauerburger

1

我的做法似乎工作,但可能是幼稚的。告诉我为什么这是错的。

docker build期间设置的ARGs被history子命令公开,所以不要去那里。但是,在运行容器时,运行命令中给出的环境变量对容器可用,但不是图像的一部分。

因此,在Dockerfile中,不要涉及秘密数据的设置。设置类似/root/finish.sh的CMD。在运行命令中,使用环境变量将秘密数据发送到容器中。 finish.sh本质上使用变量来完成构建任务。

要使管理秘密数据变得更容易,请将其放入由docker与--env-file开关一起运行的文件中。当然,保持文件的秘密。 .gitignore等等。

对我来说,finish.sh运行一个Python程序。它检查以确保它之前没有运行,然后完成设置(例如,将数据库名称复制到Django的settings.py中)。

0

有一个新的docker命令[1]用于“秘密”管理,但只适用于swarm群集。

docker service create 
--name my-iis 
--publish target=8000,port=8000 
--secret src=homepage,target="\inetpub\wwwroot\index.html" 
microsoft/iis:nanoserver 

[1] https://docs.docker.com/engine/swarm/secrets/

0

运行时仅溶液

搬运工-撰写还提供了一种非群模式溶液(自1.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 

延伸阅读: