问题描述
在我们的团队中,我们正在尝试在OpenShift(4.2)上部署基于JHipster(6.8.0)的微服务堆栈。
当网关启动并尝试通过HTTPS与Keycloak通信时(确切地说,使用基于Keycloak的Red Hat Single Sign On 7.3),我们遇到了一个问题。
这里是引发的异常:
javax.net.ssl.SSLHandshakeException: PKIX path building Failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
我们认为这是因为我们的网关不信任Keycloak的证书。确实,这是使用组织证书。 我们已经登录到网关尝试连接的域上的Keycloak管理界面。并且我们已将证书提取为X.509二进制编码的DER文件;借助Chrome浏览器功能。
我们首先尝试将这些证书简单地添加到网关容器的etc/ssl/certs/java/cacerts
文件夹中。为此,我们在项目臂存储库src/main/jib/etc/ssl/certs/java/cacerts
中创建了这些文件夹,并将证书复制到该文件夹。
我们已经使用Maven和jib:dockerBuild
选项生成了网关Docker映像。我们将其推送到Docker注册表中,并将其部署到OpenShift。
在OpenShift窗格中检查后,证书在etc/ssl/certs/java/cacerts
中的位置很合适。但是我们仍然会收到与以前相同的错误。
然后,我们尝试使用信任库。因此,我们使用以下命令为每个证书创建了一个证书:
keytool -import -file path/to/certificate.cer -alias certificatealias -keystore applicationTrustStore.jks
由于此命令,我们检查是否已正确添加所有证书:
keytool -list -v -keystore applicationTrustStore.jks
然后,我们将此applicationTrustStore.jks文件添加到项目的src/main/jib/etc/ssl/certs/java/cacerts
文件夹中,并将其添加到pom.xml中:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib-maven-plugin.version}</version>
<configuration>
<from>
<image>adoptopenjdk:11-jre-hotspot</image>
</from>
<to>
<image>application:latest</image>
</to>
<container>
…
<jvmFlags>
<jvmFlag>-Djavax.net.ssl.trustStore=etc/ssl/certs/java/cacerts/applicationTrustStore.jks</jvmFlag>
<jvmFlag>-Djavax.net.ssl.trustStoreType=jks</jvmFlag>
<jvmFlag>-Djavax.net.ssl.trustStorePassword=password</jvmFlag>
</jvmFlags>
</container>
…
</configuration>
</plugin>
我们再一次没有失败就生成并重新部署到OpenShift;还是完全一样的问题。
我们当然缺少明显的东西,但是我们不能指责它。
解决方法
TL; DR —尝试将证书添加到应用程序的Docker映像所在的JRE的 cacerts
密钥库中
漫长的答案
啊,是的!旧的 …PKIX path building failed…
问题。到过那里。而且OpenShift还涉及启动。
在这一点上可能遇到了大约十二次相同的错误,我敢打赌,到目前为止您的努力没有效果的原因是, javax.net.ssl.SSLHandshakeException
是来自运行您的应用程序的JRE;不是来自 /etc/ssl/certs/java/
的任何内容。
解决方案
-
从您的浏览器中,获取有问题的根CA证书(您所称的词:„…网关试图与其连接的领域…“ )
- 以您在问题中所说的方式(“ …作为X-509二进制编码的DER文件…”)提取出来
-
将证书添加到 您的应用程序Docker映像所基于的JRE的
cacerts
密钥库中 (<image>adoptopenjdk:11-jre-hotspot</image>
)- 一个示例Dockerfile 为您提供您需要做什么的想法: 1
FROM adoptopenjdk:11-jre-hotspot
COPY stackexchange.cer /tmp/cert/certificate.cer
RUN keytool -noprompt -import -alias deduper.answer -storepass changeit -keystore /opt/java/openjdk/lib/security/cacerts -file /tmp/cert/certificate.cer
CMD ["keytool","-list","-keystore","/opt/java/openjdk/lib/security/cacerts","-alias","deduper.answer","-storepass","changeit" ]
您可以从Docker Hub the image that Dockerfile builds…
中拉取$ docker pull deduper/ajrarn.soq.pkix.fix
运行它以查看是否添加了具有别名的证书... 2
$ docker run -it deduper/ajrarn.soq.pkix.fix
Warning: use -cacerts option to access cacerts keystore
deduper.answer,Oct 10,2020,trustedCertEntry,Certificate fingerprint (SHA-256): E5:81:5A:DF:11:A9:0C:CC:51:8F:6A:99:D2:6C:67:16:29:D6:68:E1:EA:C2:C0:A7:E7:9B:84:09:AF:9C:29:14
如果甚至那都不能解决您的问题,那么我建议的下一件事情就是更改此内容……
…
<jvmFlag>-Djavax.net.ssl.trustStore=etc/ssl/certs/java/cacerts/applicationTrustStore.jks</jvmFlag>
…
对此...
…
<jvmFlag>-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts/applicationTrustStore.jks</jvmFlag>
…
如果您没有发现差异,则可以忽略以下内容中的前导斜线: /etc/…
1 无论您如何使用示例中的说明构建图像,都必须适应Dockerfile。
2 我为此实验使用了Stack Overflow证书。
默认情况下,adoptopenjdk:11-jre-hotspot
中的JRE从不读取/etc/ssl/certs/java/cacerts
。默认情况下,大多数JRE实际上都读<JRE>/lib/security/cacerts
(除非您设置了-Djavax.net.ssl.trustStore
)。
仅仅是在许多Linux发行版中,<JRE>/lib/security/cacerts
经常是/etc/ssl/certs/java/cacerts
的符号链接。例如,
# ls -l /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts
lrwxrwxrwx 1 root root 27 Jan 1 1970 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts -> /etc/ssl/certs/java/cacerts
在这种情况下,可以将cacerts
放在/etc/ssl/certs/java
处。但是,在adoptopenjdk:11-jre-hotspot
中,<JRE>/lib/security/cacerts
不是符号链接,如下所示:
# ls -l /opt/java/openjdk/lib/security/cacerts
-rw-r--r-- 1 root root 101001 Jul 15 09:07 /opt/java/openjdk/lib/security/cacerts
如@deduper所述,我将文件放入/opt/java/openjdk/lib/security/
。
如果要设置-Djavax.net.ssl.trustStore
来指定其他位置,我认为该路径应该是@deduper指出的绝对路径。
感谢@deduper和@Chanseok的帮助,我能够解决此问题。
如果某些JHipster / Openshift用户在此处走动,则几乎没有解释。
首先,我尝试使用keytool命令在 / opt / java / openjdk / lib / security / cacerts 中导入组织证书。因此,我在src / main / jib文件夹中创建了tmp / cert子文件夹,将组织证书放入其中,并像这样更新src / main / jib / entrypoint.sh文件:
#!/bin/sh
echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP}
exec keytool -noprompt -import -alias alias -storepass changeit -keystore /opt/java/openjdk/lib/security/cacerts -file /tmp/cert/organization.cer
exec java ${JAVA_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "com.your.company.App" "$@"
它似乎在本地工作,但是在Openshift上启动它时,调用keytool时我得到了权限被拒绝,因为默认情况下Openshift不使用root用户。 因此,我终于删除了修改,并返回到原始entrypoint.sh文件。
我使用的解决方法是:
- 从 adoptopenjdk:11-jre-hotspot 中提取证书文件(使用docker cp命令,更多信息:Copying files from Docker container to host) 在我的工作站上使用keytool命令在证书中
- 添加证书
- 在src / main / jib中创建这些子文件夹:opt / java / openjdk / lib / security
- 在其中复制证书文件
当使用臂架创建Docker映像时,opt / java / openjdk / lib / security中的 cacerts 包含您的组织证书,并且在Openshift上启动时,可以与Keycloak进行通信。