尝试从 Docker 访问 ES 实例时出现 SSLHandshakeException [英] SSLHandshakeException when trying to access ES instance from Docker

查看:59
本文介绍了尝试从 Docker 访问 ES 实例时出现 SSLHandshakeException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用高级 REST 客户端 6.7.2 访问 6.x ES 实例.我通过主机名 (https://****.azureedge.net)、用户名和密码.

当我的 Spring Boot 应用程序从我的开发环境 (IDE) 运行时,它从同一个 ES 获取数据没有问题,但是一旦我尝试从 Docker 容器(从我的开发机器或云中的 K8s 集群)运行它,就会抛出 SSLHandshakeException).

容器是用基础镜像制作的:FROM debian:stretch-slim &OpenJDK 11.0.2 带有 Spring Boot 必要的模块.

我使用 -Djavax.net.debug=all 进行了一些调试.事实证明,在 docker 镜像中运行时,只发生了通常 SSL 握手的几个第一步:

产生ClientHello握手消息写入:TLS13 握手,长度 = 2352原始写入原始读取 (0000: 15 03 03 00 02 02 28 ......( )阅读:TLSv1.2 警报,长度 = 2收到警报消息 (警报": {级别":致命",描述":握手失败"})

后跟 SSLHandshakeException:

javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure在 org.elasticsearch.client.RestClient$SyncResponseListener.get(RestClient.java:938)在 org.elasticsearch.client.RestClient.performRequest(RestClient.java:227)在 org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1764)在 org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1749)在 org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1708)在 org.elasticsearch.client.SecurityClient.getSslCertificates(SecurityClient.java:508)....引起:javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure在 java.base/sun.security.ssl.Alert.createSSLException(未知来源)在 java.base/sun.security.ssl.Alert.createSSLException(未知来源)在 java.base/sun.security.ssl.TransportContext.fatal(来源不明)在 java.base/sun.security.ssl.Alert$AlertConsumer.consume(来源不明)在 java.base/sun.security.ssl.TransportContext.dispatch(来源不明)在 java.base/sun.security.ssl.SSLTransport.decode(来源不明)在 java.base/sun.security.ssl.SSLEngineImpl.decode(来源不明)在 java.base/sun.security.ssl.SSLEngineImpl.readRecord(来源不明)在 java.base/sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source)在 java.base/sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source)在 java.base/javax.net.ssl.SSLEngine.unwrap(来源不明)在 org.apache.http.nio.reactor.ssl.SSLIOSession.doUnwrap(SSLIOSession.java:271)在 org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:316)在 org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:509)在 org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:120)在 org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)在 org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)在 org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)在 org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)在 org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)在 org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)

从我的本地环境运行时,握手看起来没有中断:

产生ClientHello握手消息写入:TLS13 握手,长度 = 460原始写入原始读取阅读:TLSv1.2 握手,长度 = 155消费 ServerHello服务器你好协商协议版本:TLSv1.3会话初始化:会话(1560119025211|TLS_AES_256_GCM_SHA384)写入:TLS13 change_cipher_spec,长度 = 1原始写入原始读取阅读:TLSv1.2 change_cipher_spec,长度 = 1使用 ChangeCipherSpec 消息原始读取阅读:TLSv1.2 application_data,长度 = 27...原始读取阅读:TLSv1.2 application_data,长度 = 8469使用服务器证书握手消息...//这是带有SHA256withRSA"、SHA256withRSA"、SHA1withRSA"签名算法的 3 个证书的列表找到受信任的证书 ⇢ SHA1withRSA...

在本地运行时,我注意到 CN=Microsoft IT TLS CA 2, OU=Microsoft IT, O=Microsoft Corporation, L=Redmond, ST=Washington, C=US,以及 CN=Baltimore Cyber​​Trust Root, OU=Cyber​​Trust, O=Baltimore, C=IE 作为发行者,也许这很重要,但我想考虑到 ES 主机地址(Azure)是可以预期的.

最后我想强调的是,我不需要做任何特别的事情就可以在我的 ma​​cOS Java 11.0.2 开发环境中进行这项工作.>

我已经尝试过,但这并没有改变任何东西:

  • 将基础 Docker 映像从超薄"更改为非超薄版本
  • 使用 OpenJDK 11.0.1 或 11.0.2
  • 将 JVM 在运行时中使用的证书从主机添加到 TrustStore.(我检查了 Docker 容器,确实还有一个证书,但考虑到 握手失败 何时发生,我认为这无关紧要)
  • 尝试使用以下命令强制执行应用程序:-Dcom.sun.net.ssl.enableECC=false"、-Djdk.tls.client.protocols=TLSv1.3"、-Dhttps.protocols=TLSv1.3"、没有帮助

有趣: 来自 Docker 镜像的 curl 与 BasicAuth 对话"与相同的 URL 没有问题(握手完成)&小查询返回结果.我猜 curl 和 JVM 在 Docker 中使用了不同来源的可信 CA,不同的握手算法等.

在此先感谢您的帮助

解决方案

TLDR: 为客户端实施 TLSv1.2 在应用程序中从 docker 内部完成握手

经过多次尝试/失败尝试后,我成功了.以下内容没有任何区别:

  • 使用非slim"的 debian 基础镜像代替slim"
  • 使用 OpenJDK 11.0.2 而不是 11.0.1
  • 在构建 docker 镜像时将主机的证书添加到 JVM TrustedStore,以便在容器启动时可用.
  • 强制com.sun.net.ssl.enableECC=false
  • https.protocols 和/或 jdk.tls.client.protocols
  • 强制执行 TLSv1.3
  • https.protocols 实施 TLSv1.2

修复与主机握手的问题是通过在 Dockerfile 中使用 -Djdk.tls.client.protocols=TLSv1.2 为客户端强制执行 TLSv1.2,因此应用程序运行在容器内使用此标志.这允许 SSL 握手完成,因为它应该可以正常工作.出于某种原因,如果不为客户端强制执行较低版本的协议,则有关协议版本的实际协商不起作用.来自本地与 docker 环境的日志没有显示任何差异,但这对 docker 有所帮助.

帮助我发现的是:

  • 设置javax.net.debug=ssl:handshake 或更详细的javax.net.debug=all 以便我可以看到握手尝试的详细信息
  • 确认至少有人"可以通过使用 curl 发送与应用程序尝试相同的请求来从 Docker 内部建立出站通信,这是有效的,因为 curl 以某种方式想出了如何与主机进行握手.
  • 纯属运气

感谢大家的支持 &想法

I am trying to access 6.x ES instance using High Level REST Client 6.7.2. Access to this ES instance is provided to me via hostname (https://****.azureedge.net), username & password.

My Spring Boot application is getting data from the same ES without issues when it runs from my dev environment (IDE), but throws SSLHandshakeException as soon as I try run it from Docker container (from my development machine or K8s cluster in cloud).

Container is made with base image: FROM debian:stretch-slim & OpenJDK 11.0.2 with Spring Boot necessary modules.

I made some progress debugging with -Djavax.net.debug=all. It turns out that while running in docker image just few first steps of usual SSL handshaking happen:

Produced ClientHello handshake message
WRITE: TLS13 handshake, length = 2352
Raw write
Raw read (0000: 15 03 03 00 02 02 28    ......(   )
READ: TLSv1.2 alert, length = 2
Received alert message (
  "Alert": {
    "level"      : "fatal",
    "description": "handshake_failure"
  }
)

followed by SSLHandshakeException:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at org.elasticsearch.client.RestClient$SyncResponseListener.get(RestClient.java:938)
at org.elasticsearch.client.RestClient.performRequest(RestClient.java:227)
at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1764)
at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1749)
at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1708)
at org.elasticsearch.client.SecurityClient.getSslCertificates(SecurityClient.java:508) 
....
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)
at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)
at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Unknown Source)
at java.base/sun.security.ssl.TransportContext.dispatch(Unknown Source)
at java.base/sun.security.ssl.SSLTransport.decode(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.decode(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.readRecord(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source)
at java.base/javax.net.ssl.SSLEngine.unwrap(Unknown Source)
at org.apache.http.nio.reactor.ssl.SSLIOSession.doUnwrap(SSLIOSession.java:271)
at org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:316)
at org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:509)
at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:120)
at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)

When running from my local environment handshake looks uninterrupted:

Produced ClientHello handshake message
WRITE: TLS13 handshake, length = 460
Raw write
Raw read
READ: TLSv1.2 handshake, length = 155
Consuming ServerHello
ServerHello
Negotiated protocol version: TLSv1.3
Session initialized:  Session(1560119025211|TLS_AES_256_GCM_SHA384)
WRITE: TLS13 change_cipher_spec, length = 1
Raw write
Raw read
READ: TLSv1.2 change_cipher_spec, length = 1
Consuming ChangeCipherSpec message
Raw read
READ: TLSv1.2 application_data, length = 27
...
Raw read
READ: TLSv1.2 application_data, length = 8469
Consuming server Certificate handshake message
... // here is the list of 3 certificates with "SHA256withRSA", "SHA256withRSA", "SHA1withRSA" signature algorithms
Found trusted certificate ⇢ SHA1withRSA
...

While running locally I noticed CN=Microsoft IT TLS CA 2, OU=Microsoft IT, O=Microsoft Corporation, L=Redmond, ST=Washington, C=US, as well as CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE as issuers, maybe this is important, but I guess it is expected considering ES host address (Azure).

At the end I wanted to emphasise that I didn't need to do anything special to make this work in my macOS Java 11.0.2 development environment.

I already tried following, but that didn't change anything:

  • Changing base Docker image from "slim" to non slim version
  • Using OpenJDK 11.0.1 or 11.0.2
  • Added cert from host to TrustStore that JVM is using in a runtime. (I checked in Docker container that there is indeed one more cert, but considering when handshake failure happens I imagine this is irrelevant)
  • tried to enforce App with: "-Dcom.sun.net.ssl.enableECC=false", "-Djdk.tls.client.protocols=TLSv1.3", "-Dhttps.protocols=TLSv1.3", didn't help

Interesting: curl from Docker image with BasicAuth "talks" with same URL without issues (handshake completes) & small query returns results. I guess that curl and JVM are using different sources of trusted CAs inside the Docker, different algorithms for handshaking etc..

Thanks in advance for any help

解决方案

TLDR: enforcing TLSv1.2 for client in the app made handshaking complete from inside the docker

After a lot of try/fail attempts I made it work. Following things didn't make any difference:

  • using non "slim" debian base image insead of "slim"
  • using OpenJDK 11.0.2 instead of 11.0.1
  • adding host's certificate to JVM TrustedStore while building docker image so it is available when container starts.
  • enforcing com.sun.net.ssl.enableECC=false
  • enforcing TLSv1.3 for https.protocols and/or jdk.tls.client.protocols
  • enforcing TLSv1.2 for https.protocols

What fixed handshake with host, was enforcing TLSv1.2 for client by using -Djdk.tls.client.protocols=TLSv1.2 in Dockerfile, so app runs with this flag inside container. This allowed SSL handshake to complete as it should work anyways. For some reason actual negotiation about protocol version didn't work without enforcing lower version of protocol for client. Logs from local vs docker environment doesn't show any difference but this helped in docker.

What helped me to find out was:

  • setting of javax.net.debug=ssl:handshake or even more detailed javax.net.debug=all so I could see details of handshake attempts
  • confirming that "at least someone" can establish outbound communication from inside the docker by using curl to send same request as app is trying, which worked because curl somehow figured out how to proceed handshaking with host.
  • pure luck

Thanks everyone for support & ideas

这篇关于尝试从 Docker 访问 ES 实例时出现 SSLHandshakeException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆