Spring Boot应用程序JHipster和Keycloak通过HTTPS之间的通信问题

问题描述

在我们的团队中,我们正在尝试在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/ 的任何内容。

解决方案

  1. 从您的浏览器中,获取有问题的根CA证书(您所称的词:„…网关试图与其连接的领域…“

    • 以您在问题中所说的方式(“ …作为X-509二进制编码的DER文件…”)提取出来
  2. 将证书添加到 您的应用程序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进行通信。