DockerFile:运行指令传递参数

huangapple go评论75阅读模式
英文:

DockerFile : RUN Pass arguments

问题

我们需要更改位于我们的 Docker 镜像内的密钥库(Keystore)的 JKS 密码。

在 DockerFile 中,以下命令是有效的:

RUN [\
  "/usr/lib/jvm/java-11-openjdk-amd64/bin/keytool",\
  "-storepasswd",\
  "-storepass",\
  "changeit",\
  "-new",\
  "NEW_JKS_PASSWORD",\
  "-cacerts"\
]

然而,修改后的密码(第7行)仍然是一个硬编码的字符串。
我们希望能够从环境变量中读取它并在命令中进行设置。
我应该如何使用环境变量来实现相同的功能?

英文:

We need to change the JKS password for the Keystore inside our docker image.

The following command in DockerFile works

1. RUN [\
2.  "/usr/lib/jvm/java-11-openjdk-amd64/bin/keytool",\
3.  "-storepasswd",\
4.  "-storepass",\
5.  "changeit",\
6.  "-new",\
7.  "NEW_JKS_PASSWORD",\
8.  "-cacerts"\
9.  ]

However, the changed password (line number 7) is still a hard-coded string.
We would like to read it from the environment and set in the command.
How can I use environment variables to achieve the same?

答案1

得分: 2

以下是您要翻译的内容:

将 Dockerfile 视为应用程序的 Java 源代码,构建后的镜像类似于一个 jar 文件。在 RUN 命令中执行的任何操作都将在运行容器时固定,就像 jar 文件一样,如果您知道如何熟练使用主机工具,从镜像中提取内容(在这种情况下是相应的秘密)会非常容易。

在这里最简单的做法是删除这个 RUN 指令。取而代之,在主机系统上运行 keytool 命令,然后在运行容器时使用 docker run -v 绑定挂载选项将文件注入容器。

# 从镜像中提取现有的 cacerts 文件
docker run --rm yourimage \
  cat /usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts \
  > cacerts
# 更改其密码
keytool \
  -keystore cacerts \
  -storepass changeit \
  -storepasswd -new NEW_JKS_PASSWD
# 运行容器,用本地的文件替换 cacerts 文件
docker run \
  -v $PWD/cacerts:/usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts \
  ...
  yourimage

如果您真的想在容器内执行此操作,您需要使用一个入口点脚本来更改密码。使用 ENTRYPOINTCMD 结合的典型模式是将 ENTRYPOINT 设为一个包装脚本,该脚本进行一些必要的首次设置,然后运行 shell 的 exec "$@" 命令以作为主要容器进程运行 CMD(其余的命令行参数)。

在这种情况下,请记住,任何具有 Docker 访问权限的人都可以使用 docker inspect 命令查找容器的环境变量和其他启动选项,而任何具有系统 root 访问权限的人都可以找到任何进程的环境变量,因此以这种方式传递密码存在相当大的安全风险。

入口点脚本可能如下所示:

#!/bin/sh

# 如果提供了密码,则设置密码
if [ -n "$NEW_JKS_PASSWORD" ]; then
  /usr/lib/jvm/java-11-openjdk-amd64/bin/keytool \
    -storepasswd \
    -storepass changeit \
    -new "$NEW_JKS_PASSWORD" \
    -cacerts
  # 防止密码泄漏到主进程中
  # (`docker inspect` 等仍将显示它)
  unset NEW_JKS_PASSWORD
fi

# 运行主要容器进程
exec "$@"

在您的 Dockerfile 中,您可以将此脚本设为入口点,将运行应用程序的任何 java 命令设为命令:

FROM openjdk:11
COPY target/app.jar entrypoint.sh /
# 必须使用 JSON 数组形式
ENTRYPOINT ["/entrypoint.sh"]
# 可以是 shell 形式或 JSON 数组形式
CMD ["java", "-jar", "/app.jar"]
英文:

Think of the Dockerfile like the Java source code to your application, and the built image like a jar file. Anything you do in a RUN command will be fixed when you run container, and like a jar file, if you know how to use the host tools well it is very easy to extract the contents of the image (and in this case the corresponding secret).

The easiest thing to do here is to remove this RUN directive. Instead, run the keytool command on the host system, and then use a docker run -v bind mount option to inject the file into the container when you run it.

<!-- language: lang-sh -->

# Get the existing cacerts file out of the image
docker run --rm yourimage \
  cat /usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts \
  &gt; cacerts
# Change its password
keytool \
  -keystore cacerts \
  -storepass changeit \
  -storepasswd -new NEW_JKS_PASSWD
# Run the container, replacing the cacerts file with the local one
docker run \
  -v $PWD/cacerts:/usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts \
  ...
  yourimage

If you really want to do this in the container, you need to use an entrypoint script to change the password. A typical pattern for using ENTRYPOINT and CMD together is to have the ENTRYPOINT be a wrapper script that does some required first-time setup, then runs the shell exec &quot;$@&quot; command to run the CMD (the rest of the command-line arguments) as the main container process.

In this context, remember that anyone who has Docker access can docker inspect the container to find the environment variables and other options it was started with, and anyone who has root access to the system can find the environment variables of any process, so there is a not insignificant security risk of passing a password this way.

The entrypoint script would look something like:

<!-- language: lang-sh -->

#!/bin/sh

# Set the password if it&#39;s provided
if [ -n &quot;$NEW_JKS_PASSWORD&quot; ]; then
  /usr/lib/jvm/java-11-openjdk-amd64/bin/keytool \
    -storepasswd \
    -storepass changeit \
    -new &quot;$NEW_JKS_PASSWORD&quot; \
    -cacerts
  # Prevent the password from leaking into the main process
  # (`docker inspect` _etc._ will still show it)
  unset NEW_JKS_PASSWORD
fi

# Run the main container process
exec &quot;$@&quot;

In your Dockerfile, you'd make this script be the entrypoint, and whatever java command runs the application be the command:

FROM openjdk:11
COPY target/app.jar entrypoint.sh /
# MUST be JSON-array form
ENTRYPOINT [&quot;/entrypoint.sh&quot;]
# Can be either shell or JSON-array form
CMD [&quot;java&quot;, &quot;-jar&quot;, &quot;/app.jar&quot;]

答案2

得分: 1

你可以使用 ARG 来配合 RUN 命令的 shell 形式:

ARG jks_password
RUN "/usr/lib/jvm/java-11-openjdk-amd64/bin/keytool -storepasswd -storepass …"

然后,在构建时使用 docker build --build-arg jks_password=XYZ .

但是,这种方法有一个注意事项,请参阅 Docker 文档中的警告:

警告:
不建议使用构建时变量传递诸如 GitHub 密钥、用户凭据等敏感信息。构建时变量的值对于使用 docker history 命令查看镜像的任何用户都是可见的。
请参阅“使用 BuildKit 构建镜像”部分,了解在构建镜像时安全使用密钥的方法。

相反,应该使用 --secret 标志

RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret

以及相应的 Docker 构建参数:--secret id=mysecret,src=mysecret.txt .

英文:

You can use ARG and then the shell form of the RUN command:

ARG jks_password
RUN &quot;/usr/lib/jvm/java-11-openjdk-amd64/bin/keytool -storepasswd -storepass …&quot;

then, when building use docker build --build-arg jks_password=XYZ .

BUT this comes with a caveat, see the warning in the Docker docs:

> Warning:
> It is not recommended to use build-time variables for passing secrets like github keys, user credentials etc. Build-time variable values are visible to any user of the image with the docker history command.
> Refer to the “build images with BuildKit” section to learn about secure ways to use secrets when building images.

Instead, the --secret flag should be used:

RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret

And the corresponding docker build arguments: --secret id=mysecret,src=mysecret.txt .

huangapple
  • 本文由 发表于 2020年10月5日 22:49:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/64210973.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定