【问题标题】:Docker RUN with keytool import to Java truststore successful but fails at the same time during image build?Docker RUN 与 keytool 导入到 Java 信任库成功但在映像构建期间同时失败?
【发布时间】:2023-08-06 08:47:01
【问题描述】:

我必须将自定义根证书添加到 Docker 环境中的 Java 信任库。所以我在我的 dockerfile 中添加了以下命令:

RUN $JAVA_HOME/bin/keytool -import -file /opt/custom/certs/mycert.pem -alias mycert -keystore $JAVA_HOME/jre/lib/security/cacerts -trustcacerts -storepass changeit -noprompt

我在构建 docker 镜像时得到以下输出:

Step 10/10 : RUN $JAVA_HOME/bin/keytool -import -file /opt/custom/certs/mycert.pem -alias mycert -keystore $JAVA_HOME/jre/lib/security/cacerts -trustcacerts -storepass changeit -noprompt
 ---> Running in cbc2a547797e
Certificate was added to keystore
keytool error: java.io.FileNotFoundException: /opt/java/openjdk/jre/lib/security/cacerts (No such file or directory)
The command '/bin/sh -c $JAVA_HOME/bin/keytool -import -file /opt/custom/certs/mycert.pem -alias mycert -keystore $JAVA_HOME/jre/lib/security/cacerts -trustcacerts -storepass changeit -noprompt' returned a non-zero code: 1

我对以下事实感到困惑:

  • 输出Certificate was added to keystore 似乎表明keytool 已成功执行
  • 同时,我得到keytool error 和一个非零返回码,所以没有成功
  • 声称不存在的文件实际上存在(可能是访问问题?)

我检查过的内容:

  • %JAVA_HOME 似乎可用,因为错误消息显示正确的路径
  • 当我在没有上述RUN 命令的情况下构建映像时,然后在 docker 容器内发出完全相同的命令,它可以完美运行
  • 我使用/bin/sh 作为外壳检查了相同的内容,以确保它不是外壳 - 工作
  • 不依赖于当前目录,因为所有路径都是绝对路径

现在我不知道如何追踪这个问题了。

【问题讨论】:

  • 我想我将不得不结束这个问题。在执行上述 RUN 命令之前,我的 docker build 中似乎存在问题。现在检查这个......

标签: docker keytool cacerts


【解决方案1】:

这可能是权限问题,我猜是您使用的基本映像从 root 更改了用户,您需要成为 root 才能访问该文件。 您应该能够执行以下操作:

USER root
RUN $JAVA_HOME/bin/keytool -import -file /opt/custom/certs/mycert.pem -alias mycert -keystore $JAVA_HOME/jre/lib/security/cacerts -trustcacerts -storepass changeit -noprompt
USER originaluser

您可以通过以下方式找到原始用户:

docker history yourbaseimagename:tag

【讨论】:

  • 感谢您的回答。但是,我尝试在RUN 之前插入USER root,但这并没有改变结果。我还可以提一下,问题 RUN 之前的 RUN 做了一些apt-get install(并且它是成功的),所以我很确定它具有 root 访问权限。你能详细说明docker history吗?我试过这个,但我没有看到任何用户信息。
  • dockerfile 中的 RUN whoami 在构建时返回 root
【解决方案2】:

原来问题是我的错™️

有几件事让我感到困惑:

  1. keytool 显示 Certificate was added to keystore,即使这实际上失败了——愚蠢
  2. 我检查了该命令是否在 docker 容器中工作,但我错过了我正在以不同方式安装 Java 的另一个版本的映像中进行测试
  3. Java 密钥库并不总是位于同一路径中 - 它可能是 $JAVA_PATH/lib/security/cacerts 或可能是 $JAVA_PATH/jre/lib/security/cacerts - 显然取决于是否安装了 JRE 或 JDK

所以我的解决方案是写一个 bash 脚本:

  1. 是 Java 版本 9 还是更高版本?然后使用 -cacerts 选项导入,该选项将自动处理密钥库位置
  2. Else:$JAVA_HOME/jre 目录是否存在?然后使用-keystore $JAVA_PATH/jre/lib/security/cacerts
  3. 否则:使用-keystore $JAVA_PATH/lib/security/cacerts

【讨论】: