Search code examples
javasslkotlinreverse-proxyelasticsearch-high-level-restclient

Acessing elasctiDB behind reverse proxy


I have an RestHighLevelClient to connect to a remote elasticDB behind a reversed proxy which strips the ssl.

        val restClientBuilder: RestClientBuilder = RestClient
                .builder(HttpHost(hostname, port, scheme))

        if(username.isNotEmpty()){
            val credentialsProvider: CredentialsProvider = BasicCredentialsProvider()
            credentialsProvider.setCredentials(AuthScope.ANY, UsernamePasswordCredentials(username, password))
            restClientBuilder.setHttpClientConfigCallback { h: HttpAsyncClientBuilder -> h.setDefaultCredentialsProvider(credentialsProvider).setSSLHostnameVerifier { _, _ -> true }.setSSLContext(SSLContext.getDefault()) }
        }

client = RestHighLevelClient(restClientBuilder)

When I call

client.index(indexRequest, RequestOptions.DEFAULT)

I get an handshake_failure Exception

javax.net.ssl.SSLException: Received fatal alert: handshake_failure

As described here and here I checked the logs and found the ClientHello

*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1307888763 bytes = { 137, 187, 7, 199, 3, 82, 79, 49, 175, 115, 114, 160, 22, 241, 240, 60, 237, 136, 239, 232, 108, 236, 249, 79, 139, 90, 119, 136 }
Session ID:  {95, 24, 10, 176, 230, 223, 52, 137, 2, 146, 170, 240, 61, 10, 177, 36, 223, 167, 33, 19, 225, 19, 213, 54, 22, 220, 239, 77, 199, 139, 52, 23}
Cipher Suites: [Unknown 0x13:0x1, Unknown 0x13:0x2, Unknown 0x13:0x3, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, Unknown 0xcc:0xa9, Unknown 0xcc:0xa8, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA]
Compression Methods:  { 0 }
Extension server_name, server_name: [type=host_name (0), value=localhost]
Unsupported extension type_23, data: 
Extension renegotiation_info, renegotiated_connection: <empty>
Extension elliptic_curves, curve names: {unknown curve 29, secp256r1, secp384r1}
Extension ec_point_formats, formats: [uncompressed]
Unsupported extension type_35, data: 
Extension signature_algorithms, signature_algorithms: SHA256withECDSA, Unknown (hash:0x8, signature:0x4), SHA256withRSA, SHA384withECDSA, Unknown (hash:0x8, signature:0x5), SHA384withRSA, Unknown (hash:0x8, signature:0x6), SHA512withRSA, SHA1withRSA
Unsupported extension type_51, data: 00:24:00:1d:00:20:6a:06:37:c2:47:97:e9:87:59:c6:0e:8f:9a:7f:12:81:18:20:18:49:77:b3:d4:11:71:37:0e:13:3d:de:6f:10
Unsupported extension type_45, data: 01:01
Unsupported extension type_43, data: 08:03:04:03:03:03:02:03:01
***

and the ServerHello

*** ServerHello, TLSv1.2
RandomCookie:  GMT: 1595412045 bytes = { 85, 81, 154, 66, 129, 137, 233, 228, 170, 170, 0, 28, 94, 221, 152, 50, 25, 2, 31, 115, 127, 103, 180, 78, 173, 152, 8, 173 }
Session ID:  {95, 24, 10, 176, 230, 223, 52, 137, 2, 146, 170, 240, 61, 10, 177, 36, 223, 167, 33, 19, 225, 19, 213, 54, 22, 220, 239, 77, 199, 139, 52, 23}
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
Cipher suite:  TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

Both, Client and Server agree upon the Cipher suite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

Then follows the request in encrypted and plain text. Afterwards follows another ClientHello which ends with

[Raw read]: length = 5
0000: 15 03 03 00 02                                     .....
[Raw read]: length = 2
0000: 02 28                                              .(
I/O dispatcher 25, READ: TLSv1.2 Alert, length = 2
I/O dispatcher 25, RECV TLSv1.2 ALERT:  fatal, handshake_failure
I/O dispatcher 25, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLException: Received fatal alert: handshake_failure
I/O dispatcher 25, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLException: Received fatal alert: handshake_failure
2020-07-22 12:00:45.360 ERROR 78321 --- [io-8081-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.io.IOException: Received fatal alert: handshake_failure

The line

I/O dispatcher 25, READ: TLSv1.2 Alert, length = 2

gives hint to a non-agreement upon the Cipher suite - but in the lines above both agreed upon a suite.

Does anyone has an idea what is happening here and how the the Exception can be resolved?


Solution

  • A proper solution is still missing but I found a well working workaround. I am using curl to transfer the data to the elastic-Instance

    val gson = GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create()
    val fileContent = createTempFile(prefix = "data")
    fileContent.writeText(gson.toJson(it))
    val fileCommand = createTempFile()
    val command = "curl --user $username:$password -PUT '$scheme://$hostname:$port/data/_doc' -H 'Content-Type: application/json' -d @${fileContent.absolutePath}"
    fileCommand.writeText(command)
    
    val result = "bash ${fileCommand.absolutePath}".runCommand(createTempDir())
    logger.debug(result)
    fileCommand.delete()
    fileContent.delete()
    

    The runCommand is motivated by this post. Firstly I tried using only one file but the escaping of special characters did not work as expected.

    With this method the handshake failure does not happen, which indicates an error in my previous code.