Docker和保护密码

时间:2014-03-26 04:42:34

标签: security build docker

我最近一直在尝试使用Docker来构建一些可以解决的服务,并且一直在唠叨我的一直是将密码放入Dockerfile中。我是一名开发人员,因此在源代码中存储密码就像是在打脸。这应该是一个问题吗?关于如何在Dockerfiles中处理密码有什么好的约定吗?

13 个答案:

答案 0 :(得分:76)

绝对是一个值得关注的问题。 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。)

我首选的模式是使用包装器脚本作为ENTRYPOINTCMD。包装器脚本可以在运行时首先将秘密从外部位置导入容器,然后执行应用程序,提供机密。具体的机制根据您的运行时环境而有所不同。在AWS中,您可以结合使用IAM角色Key Management Service和S3来将加密的密钥存储在S3存储桶中。像HashiCorp Vaultcredstash之类的东西是另一种选择。

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

你可能会发现shykes comments on config in containers很有用。

答案 1 :(得分:61)

我们的团队避免在凭据库中放置凭据,因此这意味着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

答案 2 :(得分:32)

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

kubernetesDCOS

中存在类似功能

答案 3 :(得分:8)

您永远不应该向容器添加凭据,除非您可以将信用卡广播给任何可以下载图像的人。特别是,执行ADD creds和更晚RUN rm creds是不安全的,因为creds文件保留在中间文件系统层的最终图像中。任何有权访问图像的人都可以轻松提取图像。

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

我希望Docker添加一些功能来简化这一切!

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

答案 4 :(得分:5)

使用环境变量的另一种方法是使用卷来使容器中的目录可以在容器中访问,而环境变量可能会变得很混乱。

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

例如:

$ 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

许多程序可以从单独的文件中读取其凭据,因此您可以通过这种方式将程序指向其中一个文件。

答案 5 :(得分:4)

使用 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

希望它有所帮助!再见。

答案 6 :(得分:1)

我的方法似乎有效,但可能很幼稚。告诉我为什么这是错的。

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

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

要更轻松地管理秘密数据,请将其放入由docker运行--env-file开关加载的文件中。当然,保密文件。 .gitignore等。

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

答案 7 :(得分:1)

仅运行时解决方案

docker-compose还提供非群集模式解决方案(自v1.11起: Secrets using bind mounts)。

秘密作为文件挂载在/run/secrets/下面的docker-compose。这解决了运行时(运行容器)的问题,但不是在构建时(构建映像),因为/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

进一步阅读:

答案 8 :(得分:1)

issue 13490 "Secrets: write-up best practices, do's and don'ts, roadmap"刚刚于2020年9月从Sebastiaan van Stijn获得了新的更新:

使用buildkit作为构建器时,现在可以构建时间秘密了;请参阅来自Build secrets and SSH forwarding in Docker 18.09的博客文章“ Tõnis Tiigi”,2018年11月。

文档已更新:“ Build images with BuildKit

用于机密的RUN --mount选项将很快逐渐采用默认的(稳定的)Dockerfile语法。

最后一部分是新的(2020年9月)

新的Docker Build机密信息

用于docker构建的新--secret标志允许用户以安全的方式传递将在Dockerfile中使用的秘密信息,以构建docker映像,而最终不会存储在最终映像中。

id是要传递到docker build --secret中的标识符。
该标识符与RUN --mount标识符相关联,以在Dockerfile中使用。
Docker不使用将秘密信息保存在Dockerfile之外的文件名,因为这可能是敏感信息。

dst将密钥文件重命名为Dockerfile RUN命令中要使用的特定文件。

例如,将秘密信息存储在文本文件中:

$ echo 'WARMACHINEROX' > mysecret.txt

并且使用指定使用BuildKit前端docker / dockerfile:1.0-experimental的Dockerfile,可以访问该机密。

例如:

# syntax = docker/dockerfile:1.0-experimental
FROM alpine

# shows secret from default secret location:
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret

# shows secret from custom secret location:
RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar

此Dockerfile仅用于证明可以访问该机密。如您所见,机密信息显示在构建输出中。构建的最终映像将没有秘密文件:

$ docker build --no-cache --progress=plain --secret id=mysecret,src=mysecret.txt .
...
#8 [2/3] RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
#8       digest: sha256:5d8cbaeb66183993700828632bfbde246cae8feded11aad40e524f54ce7438d6
#8         name: "[2/3] RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret"
#8      started: 2018-08-31 21:03:30.703550864 +0000 UTC
#8 1.081 WARMACHINEROX
#8    completed: 2018-08-31 21:03:32.051053831 +0000 UTC
#8     duration: 1.347502967s
#9 [3/3] RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar
#9       digest: sha256:6c7ebda4599ec6acb40358017e51ccb4c5471dc434573b9b7188143757459efa
#9         name: "[3/3] RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar"
#9      started: 2018-08-31 21:03:32.052880985 +0000 UTC
#9 1.216 WARMACHINEROX
#9    completed: 2018-08-31 21:03:33.523282118 +0000 UTC
#9     duration: 1.470401133s
...

答案 9 :(得分:0)

虽然我完全同意没有简单的解决方案。仍然存在单点故障。 dockerfile,etcd等等。 Apcera有一个看似sidekick的计划 - 双重身份验证。换句话说,除非有Apcera配置规则,否则两个容器无法通话。在他们的演示中,uid / pwd处于清晰状态,在管理员配置链接之前无法重复使用。然而,为了实现这一点,它可能意味着修补Docker或至少是网络插件(如果有这样的东西)。

答案 10 :(得分:0)

有一个新的docker命令[1]用于&#34;秘密&#34;管理,但这只适用于群集群。

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/

答案 11 :(得分:0)

12-Factor app methodology告诉我,任何配置都应存储在环境变量中。

Docker compose可以在配置中执行variable substitution,因此可以用来将密码从主机传递到docker。

答案 12 :(得分:0)

像这样的东西会起作用,我想它是否是bash shell。

读取-sp“ db_password:”密码| docker run -itd --name <容器名称> --build-arg mysql_db_password = $ db_password alpine / bin / bash

简单地静默阅读它,并将其作为参数传递给Docker映像。您需要在Dockerfile中将变量接受为ARG。