尝试从 Docker 访问 ES 实例时出现 SSLHandshakeException
Posted
技术标签:
【中文标题】尝试从 Docker 访问 ES 实例时出现 SSLHandshakeException【英文标题】:SSLHandshakeException when trying to access ES instance from Docker 【发布时间】:2019-10-24 15:45:22 【问题描述】:我正在尝试使用高级 REST 客户端 6.7.2 访问 6.x ES 实例。 通过主机名 (https://****.azureedge.net)、用户名和密码向我提供对此 ES 实例的访问权限。
我的 Spring Boot 应用程序从我的开发环境 (IDE) 运行时从同一个 ES 获取数据没有问题,但是当我尝试从 Docker 容器(从我的开发机器或云中的 K8s 集群)运行它时抛出 SSLHandshakeException )。
容器由基础镜像组成:FROM debian:stretch-slim
& OpenJDK 11.0.2
带有 Spring Boot 必要的模块。
我使用-Djavax.net.debug=all
进行了一些调试。事实证明,在 docker 映像中运行时,通常会发生 SSL 握手的几个第一步:
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"
)
后跟 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)
从我的本地环境运行时,握手看起来没有中断:
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
...
在本地运行时,我注意到 CN=Microsoft IT TLS CA 2, OU=Microsoft IT, O=Microsoft Corporation, L=Redmond, ST=Washington, C=US
和 CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE
作为发行者,这可能很重要,但我想考虑到 ES 主机地址 (Azure),这是可以预期的。
最后我想强调的是,我不需要做任何特别的事情来使这项工作在我的 macOS Java 11.0.2 开发环境中工作。 p>
我已经尝试过关注,但这并没有改变任何东西:
将基础 Docker 映像从“slim”更改为非 slim 版本 使用 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 来源、不同的握手算法等。
提前感谢您的帮助
【问题讨论】:
替代纤薄的基础图像,尝试使用完整图像,看看是否有帮助 不幸的是没有改变任何东西:相同握手步骤后的相同异常 你能不能做一个curl -v <url>
看看你是否在 docker 容器中遇到同样的握手错误
curl 到 ES 实例和与 Docker 内的 BasicAuth 完全相同的 URL 工作。我了解到 docker 内部的 curl 使用 /etc/ssl/certs
和 JVM /JAVA_HOME/lib/security/cacerts
作为可信 CA 的来源(如果我错了请纠正我),所以不确定这个小进步是否真的有用。
是的,看看这对rabexc.org/posts/certificates-not-working-java是否有帮助,这对unix.stackexchange.com/questions/456475/…也有帮助
【参考方案1】:
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 以某种方式弄清楚了如何与主机进行握手。
纯属运气
感谢大家的支持和想法
【讨论】:
以上是关于尝试从 Docker 访问 ES 实例时出现 SSLHandshakeException的主要内容,如果未能解决你的问题,请参考以下文章
尝试将docker容器连接到mongodb时出现异常打开套接字异常
尝试从主机连接到 mysql docker 容器时出现“读取初始通信数据包”错误
从 ASP.NET 访问“用户实例”数据库时出现用户权限错误
尝试在 Windows 中访问 apache vhost 时出现禁止错误
尝试从 docker 上发布的 Neo4jRepository 检索数据时出现 Neo4jSession 错误[ONLY DOCKER ISSUE]