'Micrometer with Elasticsearch over SSL

I'm trying to use Micrometer with Elasticsearch over SSL.

I use Micrometer in version 1.8.0, Elasticsearch in version 7.16.3 and OpenJDK 11.0.2 .

Because I know that it's not possible to use a built-in configuration (link) I tried to inject a custom HttpUrlConnectionSender as in the following class SecureHttpSender:

public class SecureHttpSender extends HttpUrlConnectionSender {
    ...

    public SecureHttpSender(ElasticProperties properties, SecureElasticProperties secureElasticProperties) {
                super(properties.getConnectTimeout(), properties.getReadTimeout());
                this.secureElasticProperties = secureElasticProperties;
                this.sslSocketFactory = buildSslSocketFactory();
            }
    
    @Override
    public Response send(Request request) throws IOException {
        HttpURLConnection httpURLConnection = null;
        try {
            httpURLConnection = (HttpURLConnection) request.getUrl().openConnection();
    
            // if the connection is an instance of the HttpsURLConnection class, the ssl configuration will always been applied.
            if (httpURLConnection instanceof HttpsURLConnection) {
                // - hostname verifier
                if (!secureElasticProperties.isVerifyHostname()) {
                    logger.debug("setting the hostname verifier to: {}", NoopHostnameVerifier.INSTANCE);
                    ((HttpsURLConnection) httpURLConnection).setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
                }
    
                // - trust store configuration
                ((HttpsURLConnection) httpURLConnection).setSSLSocketFactory(sslSocketFactory);
            }
    
            return super.send(request);
    
        } finally {
            try {
                if (httpURLConnection != null) {
                    httpURLConnection.disconnect();
                }
            } catch (Exception ignore) {
            }
        }
    }
    
    private SSLSocketFactory buildSslSocketFactory() {
        SSLSocketFactory sslSocketFactory;
        try (InputStream is = getInputStream(secureElasticProperties.getTrustStorePath())) {
            KeyStore truststore = KeyStore.getInstance(secureElasticProperties.getTrustStoreType());
            truststore.load(is, secureElasticProperties.getTrustStorePassword().toCharArray());
            SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(truststore, null);
            final SSLContext sslContext = sslBuilder.build();
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
            String message = String.format("error while loading the security configuration from: %s", secureElasticProperties);
            logger.error(message, e);
            throw new RuntimeException("management.metrics.export.elastic.ssl");
        }
        return sslSocketFactory;
    }
    
    private InputStream getInputStream(String trustStorePathString) throws IOException {
    PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
    Resource resource = pathMatchingResourcePatternResolver.getResource(trustStorePathString);
    return resource.getInputStream();
    }
}

that I injected with Spring Boot so I can apply the desired configuration, but I got the following error:

ERROR 10912 --- [trics-publisher] i.m.elastic.ElasticMeterRegistry         : failed to send metrics to elastic
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...

The server certificate and the client truststore are valid because I already used them with success. I also tried to force a specific version of the TLS protocol during the handshake phase: TLSv1.3 and TLSv1.2 but the error still occurs.

Anyone have any suggestions on how to fix it? thanks



Solution 1:[1]

I did a test with a simple change to the code I had posted and I solved it: I copied all code of the super.send() method, adding the additional code to set the custom SslSocketFactory and all was OK!

so the reason was that

it creates a new connection without using the one you created

as Jonatan said... a my trivial mistake. :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1