为什么不建议在 DOCKERFILE 中使用 ARG 来传递秘密?

Why is ARG in a DOCKERFILE not recommended for passing secrets?

http://docs.docker.com/engine/reference/builder/#arg中,建议不通过ARGS传递秘密。

Note: It is not recommended to use build-time variables for passing secrets like github keys, user credentials etc.

在什么情况下通过危险的构建时变量传递秘密?

2018 年 8 月更新:

你现在有 docker build --secret id=mysecret,src=/secret/file.
参见“”。

2017 年 1 月更新:

Docker (swarm) 1.13 有 docker secret.

然而,如commented by Steve Hoffman (bacoboy):

[...]The secret command only helps swarm users is not a more general solution (like they did with attaching persistent volumes).
How you manage your secrets (what they are and who has access to them) is very system dependent and depends on which bits of paid and/or OSS you cobble together to make your "platform".
With Docker the company moving into providing a platform, I'm not surprised that their first implementation is swarm based just as Hashicorp is integrating Vault into Atlas -- it makes sense.

Really how the secrets are passed falls outside the space of docker run.
AWS does this kind of thing with roles and policies to grant/deny permissions plus an SDK.
Chef does it using encrypted databags and crypto "bootstrapping" to auth.
K8S has their own version of what just got released in 1.13.
I'm sure mesos will add a similar implementation in time.

These implementations seem to fall into 2 camps.

  • pass the secret via volume mount that the "platform" provides or (chef/docker secret/k8s
  • pass credentials to talk to an external service to get things at boot (iam/credstash/etc)

原始答案:2015 年 11 月

这是在 commit 54240f8 (docker 1.9, Nov 2015), from PR 15182

中介绍的

The build environment is prepended to the intermediate continer's command string for aiding cache lookups.
It also helps with build traceability. But this also makes the feature less secure from point of view of passing build time secrets.

issue 13490重申:

Build-time environment variables: The build-time environment variables were not designed to handle secrets. By lack of other options, people are planning to use them for this. To prevent giving the impression that they are suitable for secrets, it's been decided to deliberately not encrypt those variables in the process.

9176 comments所述:

env variables are the wrong way to pass secrets around. We shouldn't be trying to reinvent the wheel and provide a crippled security distribution mechanism right out of the box.

When you store your secret keys in the environment, you are prone to accidentally expose them -- exactly what we want to avoid:

  • Given that the environment is implicitly available to the process, it's incredibly hard, if not impossible, to track access and how the contents get exposed
  • It is incredibly common having applications grabbing the whole environment and print it out, since it can be useful for debugging, or even send it as part of an error report. So many secrets get leaked to PagerDuty that they have a well-greased internal process to scrub them from their infrastructure.
  • Environment variables are passed down to child processes, which allows unintended access and breaks the principle of least privilege. Imagine that as part of your application you call to third-party tool to perform some action, all of a sudden that third-party tool has access to your environment, and god knows what it will do with it.
  • It is very common for applications that crash to store the environment variables in log-files for later debugging. This means secrets in plain-text on disk.
  • Putting secrets in env variables quickly turns into tribal knowledge. New engineers don't know they are there, and are not aware they should be careful when handling environment variables (filtering them to sub-processes, etc).

Overall, secrets in env variables break the principle of least surprise, are a bad practice and will lead to the eventual leak of secrets.

原因很简单,只要在图像上 运行 history,任何拥有图像的人都可以看到秘密的价值。

取此示例docker 文件:

FROM alpine

ARG secret

RUN echo "${secret}"

(简单明了,只是为了说明如何使用秘密。)

然后我们构建它$ docker build --build-arg secret=S3CR3T - < Dockerfile

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM alpine
 ---> 13e1761bf172
Step 2 : ARG secret
 ---> Running in 695b7a931445
 ---> 5414c15a1cb6
Removing intermediate container 695b7a931445
Step 3 : RUN echo "${secret}"
 ---> Running in c90cf0d1414b
s3cr3t
 ---> f2bcff49ac09
Removing intermediate container c90cf0d1414b
Successfully built f2bcff49ac09

以及如何让 "secret" 退出的示例(在第一行查找 |1 secret=):

$ docker history f2bcff49ac09
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
f2bcff49ac09        8 seconds ago       |1 secret=S3CR3T /bin/sh -c echo "${secret}"    0 B
5414c15a1cb6        8 seconds ago       /bin/sh -c #(nop) ARG secret                    0 B
13e1761bf172        6 months ago        /bin/sh -c #(nop) ADD file:614a9122187935fccf   4.797 MB

如果您在本地构建映像或从注册表中提取映像,就会出现这种情况。

如果您的目标是将构建时的秘密排除在 运行 容器之外,那么使用 ARG 确实可以帮助您 - 考虑一下:

$ docker run --rm -ti f2bcff49ac09 sh
/ # env
HOSTNAME=7bc772fd0f56
SHLVL=1
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
$ # Note no secret in the above output

我认为新的 (17.05) docker 功能多阶段构建 (https://docs.docker.com/engine/userguide/eng-image/multistage-build/) 减轻了这些(有效的)关于简单使用 --build-arg 的担忧。

FROM mybuildertools
ADD my-git-creds /root/.ssh
RUN git clone git@bitbucket.org:example/foo /src
FROM mybuildertools
COPY --from=0 /src /src
RUN ...build /src with no git credentials ending up in final image...

不幸的是,除非您有 "my-git-creds" 目录,否则似乎没有允许后续重建的简单方法(例如更改 Dockerfile 中的构建步骤)。

我写了 https://github.com/abourget/secrets-bridge 来解决构建时的秘密问题。

它创建了一个一次性配置,您可以将其作为构建参数传递,在构建过程中,它将连接到主机并获取机密,使用它们,然后您可以终止主机桥。即使 build-args 保存在某个地方,它们也会在服务器退出时变得无用。

服务器支持 SSH 代理转发,通过 TLS websocket 通信建立隧道。它也适用于 Windows!

希望对您有所帮助。