将 Dockerfile 视为应用程序的 Java 源代码,将构建的映像视为 jar 文件。当你运行容器时,你在RUN 命令中所做的任何事情都会被修复,就像一个 jar 文件一样,如果你知道如何很好地使用主机工具,那么很容易提取图像的内容(在这种情况下是对应的秘密)。
这里最简单的做法是删除这个RUN 指令。而是在主机系统上运行keytool 命令,然后在运行时使用docker run -v 绑定挂载选项将文件注入容器。
# Get the existing cacerts file out of the image
docker run --rm yourimage \
cat /usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts \
> 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
如果您真的想在容器中执行此操作,则需要使用入口点脚本来更改密码。一起使用ENTRYPOINT 和CMD 的典型模式是让ENTRYPOINT 成为一个包装脚本,执行一些必要的首次设置,然后运行shell exec "$@" 命令来运行CMD(其余的的命令行参数)作为主容器进程。
在这种情况下,请记住,任何拥有 Docker 访问权限的人都可以docker inspect 找到容器启动时使用的环境变量和其他选项,任何拥有系统 root 访问权限的人都可以找到任何进程的环境变量,因此以这种方式传递密码存在不小的安全风险。
入口点脚本如下所示:
#!/bin/sh
# Set the password if it's provided
if [ -n "$NEW_JKS_PASSWORD" ]; then
/usr/lib/jvm/java-11-openjdk-amd64/bin/keytool \
-storepasswd \
-storepass changeit \
-new "$NEW_JKS_PASSWORD" \
-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 "$@"
在您的 Dockerfile 中,您可以将此脚本设置为入口点,而无论 java 命令运行应用程序的任何命令都是命令:
FROM openjdk:11
COPY target/app.jar entrypoint.sh /
# MUST be JSON-array form
ENTRYPOINT ["/entrypoint.sh"]
# Can be either shell or JSON-array form
CMD ["java", "-jar", "/app.jar"]