使用 Zulu jdk 11 从 Alpine Linux Image 调用 GRPC 服务时出现 SSL 连接问题
Posted
技术标签:
【中文标题】使用 Zulu jdk 11 从 Alpine Linux Image 调用 GRPC 服务时出现 SSL 连接问题【英文标题】:SSL connection issue when calling GRPC service from Alpine Linux Image using Zulu jdk 11 【发布时间】:2020-01-27 06:25:55 【问题描述】:我有一个使用 Netty 调用 GRPC 服务的 Spring Boot 应用程序。当我在本地机器(没有 JCE 的 MacOS 和 zulu jdk)上运行我的代码时,我能够连接到 GRPC 服务。
注意:我们使用的是Oracle JDK 1.8 编译的GRPC 客户端jar
当我构建一个 docker 镜像(Alpine Linux with zulu jdk without JCE)我得到以下错误
javax.net.ssl|ERROR|37|C7-services-1|2019-09-26 21:18:10.622 GMT|TransportContext.java:312|Fatal (HANDSHAKE_FAILURE): Couldn't kickstart handshaking (
"throwable" :
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
at java.base/sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:169)
at java.base/sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:98)
at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:216)
at java.base/sun.security.ssl.SSLEngineImpl.beginHandshake(SSLEngineImpl.java:103)
at io.netty.handler.ssl.JdkSslEngine.beginHandshake(JdkSslEngine.java:155)
at io.netty.handler.ssl.SslHandler.handshake(SslHandler.java:1967)
at io.netty.handler.ssl.SslHandler.startHandshakeProcessing(SslHandler.java:1886)
at io.netty.handler.ssl.SslHandler.channelActive(SslHandler.java:2021)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:225)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:211)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:204)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1396)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:225)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:211)
at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:906)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise(AbstractNioChannel.java:311)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:341)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:670)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:617)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:534)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at java.base/java.lang.Thread.run(Thread.java:834)
)
我看到在我的本地以下密码套件存在
* TLS_AES_128_GCM_SHA256
* TLS_AES_256_GCM_SHA384
* TLS_DHE_DSS_WITH_AES_128_CBC_SHA
* TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
* TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
* TLS_DHE_DSS_WITH_AES_256_CBC_SHA
* TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
* TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
* TLS_DHE_RSA_WITH_AES_128_CBC_SHA
* TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
* TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
* TLS_DHE_RSA_WITH_AES_256_CBC_SHA
* TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
* TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
* TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
* TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
* TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
* TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
* TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
* TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
* TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
* TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
* TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
* TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
* TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
* TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
* TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
* TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
* TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV
* TLS_RSA_WITH_AES_128_CBC_SHA
* TLS_RSA_WITH_AES_128_CBC_SHA256
* TLS_RSA_WITH_AES_128_GCM_SHA256
* TLS_RSA_WITH_AES_256_CBC_SHA
* TLS_RSA_WITH_AES_256_CBC_SHA256
* TLS_RSA_WITH_AES_256_GCM_SHA384
在图片中我看到的很少
* TLS_AES_128_GCM_SHA256
* TLS_AES_256_GCM_SHA384
* TLS_DHE_DSS_WITH_AES_128_CBC_SHA
* TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
* TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
* TLS_DHE_DSS_WITH_AES_256_CBC_SHA
* TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
* TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
* TLS_DHE_RSA_WITH_AES_128_CBC_SHA
* TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
* TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
* TLS_DHE_RSA_WITH_AES_256_CBC_SHA
* TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
* TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV
* TLS_RSA_WITH_AES_128_CBC_SHA
* TLS_RSA_WITH_AES_128_CBC_SHA256
* TLS_RSA_WITH_AES_128_GCM_SHA256
* TLS_RSA_WITH_AES_256_CBC_SHA
* TLS_RSA_WITH_AES_256_CBC_SHA256
* TLS_RSA_WITH_AES_256_GCM_SHA384
注意:顺便说一句,在我的本地它选择 TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
我将我的 java 版本降级为 Oracle JDK 1.8,并使用 JDK 1.8 构建了映像。现在我能够通过 SSL 握手解决问题,因为我可以看到图像中现在可用的所有密码。但是我最终遇到了一个非常 Alpine Linux 的问题。
Caused by: java.lang.IllegalStateException: Could not find TLS ALPN provider; no working netty-tcnative, Conscrypt, or Jetty NPN/ALPN available
at io.grpc.netty.GrpcSslContexts.defaultSslProvider(GrpcSslContexts.java:258) ~[grpc-netty-1.16.1.jar:1.16.1]
at io.grpc.netty.GrpcSslContexts.configure(GrpcSslContexts.java:171) ~[grpc-netty-1.16.1.jar:1.16.1]
at io.grpc.netty.GrpcSslContexts.forClient(GrpcSslContexts.java:120) ~[grpc-netty-1.16.1.jar:1.16.1]
at com.samsclub.list.core.client.C7ProductCatalogProvider.createChannel(C7ProductCatalogProvider.java:104) ~[sams-list-core-0.0.10-SNAPSHOT.jar:0.0.10-SNAPSHOT]
at com.samsclub.list.core.client.C7ProductCatalogProvider.init(C7ProductCatalogProvider.java:59) ~[sams-list-core-0.0.10-SNAPSHOT.jar:0.0.10-SNAPSHOT]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_212]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_212]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_212]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_212]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:363) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMet
看到这个https://github.com/grpc/grpc-java/issues/3336
我真的很想上zulu 11,想调用这个GRPC服务。我们应该只获取使用 JDK 11 编译的 GRPC 客户端 jar 吗?
我的 Docker Alpine 镜像配置
/app # cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.10.0
PRETTY_NAME="Alpine Linux v3.10"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
/app # java -version
openjdk version "11.0.4" 2019-07-16 LTS
OpenJDK Runtime Environment Zulu11.33+15-CA (build 11.0.4+11-LTS)
OpenJDK 64-Bit Server VM Zulu11.33+15-CA (build 11.0.4+11-LTS, mixed mode)
【问题讨论】:
【参考方案1】:你说没有JCE的zulu jdk是什么意思?
官方 zulu jdk 提供了所有必需的加密库和提供程序。如果您手动排除某些提供程序或库,您将错过相应的功能。
例如 SunEC 加密提供商负责 ECDSA 和 ECDH 算法的实现。从提供者列表中排除 SunEC 或禁用/删除 jdk.crypto.ec 模块,您将错过所有 TLS_ECDH_ECDSA_* TLS_ECDHE_ECDSA_* 密码套件。这可能是您的 TLS 握手失败的原因。
【讨论】:
JCE(Java Cryptography 扩展)默认情况下不附带任何 jdk 下载。您需要单独下载。我没有手动删除任何东西。 BTE 我们将删除模块“jdk.crypto.ec 模块”【参考方案2】:使用 Zulu 11 jdk 的清洁 alpine docker 映像显示如下:
$ docker run alpn_zulu more /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.10.2
PRETTY_NAME="Alpine Linux v3.10"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
$ docker run alpn_zulu java -version
openjdk version "11.0.4" 2019-07-16 LTS
OpenJDK Runtime Environment Zulu11.33+15-CA (build 11.0.4+11-LTS)
OpenJDK 64-Bit Server VM Zulu11.33+15-CA (build 11.0.4+11-LTS, mixed mode)
$ docker run alpn_zulu jrunscript -e "java.util.Arrays.asList(javax.net.ssl.SSLServerSocketFactory.getDefault().getSupportedCipherSuites()).stream().forEach(println)"
Warning: Nashorn engine is planned to be removed from a future JDK release
TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
【讨论】:
更新了我的问题,从 docker 镜像获取 linux 和 java 配置。 你能运行“jrunscript -e”java.util.Arrays.asList(javax.net.ssl.SSLServerSocketFactory.getDefault().getSupportedCipherSuites()).stream().forEach(println)” "和"jrunscript -e "java.util.Arrays.asList(javax.net.ssl.SSLServerSocketFactory.getDefault().getSupportedCipherSuites()).stream().forEach(println)"" 高山图像和本地命令机器(使用zulu11)并进行比较。如果不同,则说明您在 java.security 文件中有不同的安全配置以上是关于使用 Zulu jdk 11 从 Alpine Linux Image 调用 GRPC 服务时出现 SSL 连接问题的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Alpine linux docker 镜像中安装 oracle jdk11?
是啥让 Maven 想要使用 Zulu Java 11 的 openjfx 而不是 Zulu Java 8 用于 Spring Boot 项目?
从“openjdk:8-jdk-alpine”为 Spring Boot 应用程序构建 docker 映像时无法运行“RUN ./mvnw dependency:go-offline -B”