elasticsearch,ssl-certificate,Spring Boot,Docker,Ssl,elasticsearch,Ssl Certificate" /> elasticsearch,ssl-certificate,Spring Boot,Docker,Ssl,elasticsearch,Ssl Certificate" />

Spring boot 尝试从Docker访问ES实例时发生SSLHandshakeException

Spring boot 尝试从Docker访问ES实例时发生SSLHandshakeException,spring-boot,docker,ssl,elasticsearch,ssl-certificate,Spring Boot,Docker,Ssl,elasticsearch,Ssl Certificate,我正在尝试使用高级REST客户端6.7.2访问6.x ES实例。 我可以通过主机名(https://**.azureedge.net)、用户名和密码访问此ES实例 我的Spring Boot应用程序在从我的开发环境(IDE)运行时从相同的ES获取数据,没有问题,但在我尝试从Docker容器(从我的开发机器或云中的K8s集群)运行它时,就会抛出SSLHandshakeException 容器由基本图像制成:来自debian:stretch slim&OpenJDK 11.0.2以及Spring B

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

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

容器由基本图像制成:
来自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
...
在本地运行时,我注意到作为发行人,这可能很重要,但我想考虑到ES主机地址(Azure),这是应该的

最后我想强调的是,在我的macOSJava11.0.2开发环境中,我不需要做任何特殊的工作

我已经试过了,但没有改变任何事情:

  • 将基本Docker映像从“slim”更改为非slim版本
  • 使用OpenJDK 11.0.1或11.0.2
  • 将主机证书添加到JVM在运行时使用的信任库中。(我在Docker容器中检查,确实还有一个证书,但考虑到握手失败时发生的情况,我认为这与此无关)
  • 尝试使用“-Dcom.sun.net.ssl.enableECC=false”、“-Djdk.tls.client.protocols=TLSv1.3”、“-Dhttps.protocols=TLSv1.3”强制应用程序,但没有帮助
有趣:从Docker图像中卷曲,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
  • 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
    ,以便查看握手尝试的详细信息
  • 确认“至少有人”可以在docker内部建立出站通信,方法是使用curl发送与应用程序尝试相同的请求,这是有效的,因为curl不知怎么搞清楚了如何与主机进行握手
  • 纯粹的运气

感谢大家的支持和想法

而不是苗条的基本形象,尝试使用完整图像,看看这是否有帮助,但不幸的是没有改变任何东西:在相同的握手步骤后出现相同的异常扫描您执行
curl-v
,看看您是否在docker容器URL到ES实例中遇到相同的握手错误&docker容器URL与docker中的BasicAuth完全相同。我了解到curl inside docker使用
/etc/ssl/certs
和JVM
/JAVA\u HOME/lib/security/cacerts
作为可信CA的源(如果我错了,请纠正我),所以不确定这一小进步是否真的有用。是的,看看这是否有帮助,这也是