Elasticsearch - How to set-up configuration in a springboot application when ssl is enabled for elasticsearch

  1. The Elasticsearch documentation shows 2 different ways to create a SSLContext that can be used to access the cluster (this is nothing that can be done from Spring Data Elasticsearch):

    1. Using certificate fingerprint

    https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/connecting.html#_verifying_https_with_a_certificate_fingerprint

    String fingerprint = "<certificate fingerprint>";
    SSLContext sslContext = TransportUtils.sslContextFromCaFingerprint(fingerprint);
    

    Using it in code - with spring data elasticsearch

    import co.elastic.clients.transport.TransportUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.elasticsearch.client.ClientConfiguration;
    import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
    
    import javax.net.ssl.SSLContext;
    import java.net.URI;
    
    @Configuration
    @Slf4j
    public class ESRestClient extends ElasticsearchConfiguration {
    
        @Value("${elasticsearch.url}")
        private String elasticsearchUrl;
        @Value("${elasticsearch.hostname}")
        private String hostname;
        @Value("${elasticsearch.port}")
        private Integer port;
        @Value("${elasticsearch.username}")
        private String username;
        @Value("${elasticsearch.password}")
        private String password;
    
        @Override
        public ClientConfiguration clientConfiguration() {
               String fingerprint = "55f8d706125b0a54896e7e457842e1d4a036a8cb413199de3489a38d701016b0";
               SSLContext sslContext = TransportUtils
                       .sslContextFromCaFingerprint(fingerprint);
    
               // final String stringUrl = System.getenv("ELASTICSEARCH_URL");
               final URI uri = URI.create(elasticsearchUrl);
    
               String host = uri.getHost();
               int port = uri.getPort() == -1 ? 9200 : uri.getPort();
               final ClientConfiguration.MaybeSecureClientConfigurationBuilder builder =
                       ClientConfiguration.builder().connectedTo(host + ":" + port);
    
               // enable SSL if https is being used in the URL
               boolean isSsl = "https".equals(uri.getScheme());
               if (isSsl) {
                   builder.usingSsl(sslContext);
                   builder.withBasicAuth(username, password);
               }
    
               return builder.build();
        }
    }
    

    Using it in code - with Java API client

    import co.elastic.clients.elasticsearch.ElasticsearchClient;
    import co.elastic.clients.json.jackson.JacksonJsonpMapper;
    import co.elastic.clients.transport.ElasticsearchTransport;
    import co.elastic.clients.transport.TransportUtils;
    import co.elastic.clients.transport.rest_client.RestClientTransport;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HttpHost;
    import org.apache.http.auth.AuthScope;
    import org.apache.http.auth.UsernamePasswordCredentials;
    import org.apache.http.impl.client.BasicCredentialsProvider;
    import org.elasticsearch.client.RestClient;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.net.ssl.SSLContext;
    
    @Configuration
    @Slf4j
    public class ElasticsearchClientConfig {
    
        @Value("${elasticsearch.url}")
        private String elasticsearchUrl;
        @Value("${elasticsearch.hostname}")
        private String hostname;
        @Value("${elasticsearch.port}")
        private Integer port;
        @Value("${elasticsearch.username}")
        private String username;
        @Value("${elasticsearch.password}")
        private String password;
    
        @Bean
        public ElasticsearchClient getElasticSearchClient() {
    
               String fingerprint = "55f8d706125b0a54896e7e457842e1d4a036a8cb413199de3489a38d701016b0";
               SSLContext sslContext = TransportUtils
                       .sslContextFromCaFingerprint(fingerprint);
    
               BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
               credsProv.setCredentials(
                       AuthScope.ANY, new UsernamePasswordCredentials(username, password)
               );
    
               RestClient restClient = RestClient
                       .builder(new HttpHost(hostname, port, "https"))
                       .setHttpClientConfigCallback(hc -> hc
                               .setSSLContext(sslContext)
                               .setDefaultCredentialsProvider(credsProv)
                       )
                       .build();
    
               // Create the transport with a Jackson mapper
               ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
    
               // And create the API client
               return new ElasticsearchClient(transport);
        }
    }
    
    1. Using ca_certificate

    https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/connecting.html#_verifying_https_with_a_ca_certificate

    Note that the certificate fingerprint can also be calculated using openssl x509 with the certificate file:

    openssl x509 -fingerprint -sha256 -noout -in /path/to/http_ca.crt
    

    If you don’t have access to the generated CA file from Elasticsearch you can use the following script to output the root CA fingerprint of the Elasticsearch instance with openssl s_client:

    openssl s_client -connect localhost:9200 -servername localhost -showcerts </dev/null 2>/dev/null \
      | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin
    

    Using it in code:

    File certFile = new File("/path/to/http_ca.crt");
    SSLContext sslContext = TransportUtils.sslContextFromHttpCaCrt(certFile);
    

    This SSLContext can then be passed to the Spring Data Elasticsearch configuration - there’s an overload method usingSsl(SSLContext).

    Depending on the context, you have two options for verifying the HTTPS connection: either verifying with the CA certificate itself or using the CA certificate fingerprint. For both cases, the Java API Client’s TransportUtils class provides convenience methods to easily create an SSLContext.


Links to this note