3

Does anyone know how to use SSL on Spring Boot application to connect with ElasticSearch which is deployed at Openshift in the form of https? I have a config.java in my Spring Boot application like the following:

@Configuration
@EnableElasticsearchRepositories(basePackages = "com.siolbca.repository")
@ComponentScan(basePackages = "com.siolbca.services")
public class Config {
    @Bean
    public RestHighLevelClient client() {
        ClientConfiguration clientConfiguration 
            = ClientConfiguration.builder()
                .connectedTo("elasticsearch-siol-es-http.siolbca-dev.svc.cluster.local")
                .usingSsl()
                .withBasicAuth("elastic","G0D1g6TurJ79pcxr1065pU0U")
                .build();

        return RestClients.create(clientConfiguration).rest();
    }

    @Bean
    public ElasticsearchOperations elasticsearchTemplate() {
        return new ElasticsearchRestTemplate(client());
    }
}

However, when I run it with Postman to run elasticsearch, an error appears like this:

javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

I've seen some tutorials on the internet that say it's a certificate issue but I can't get a clue how to implement it in my code because I'm a beginner to Java & Spring Boot. using-elasticsearch-java-rest-api-with-self-signed-certificates how-to-connect-spring-boot-2-1-with-elasticsearch-6-6-with-cluster-node-https

And here’s my configuration for elasticsearch.yml:

cluster:
  name: elasticsearch-siol
  routing:
    allocation:
      awareness:
        attributes: k8s_node_name
discovery:
  seed_providers: file
http:
  publish_host: ${POD_NAME}.${HEADLESS_SERVICE_NAME}.${NAMESPACE}.svc
network:
  host: "0"
  publish_host: ${POD_IP}
node:
  attr:
    attr_name: attr_value
    k8s_node_name: ${NODE_NAME}
  name: ${POD_NAME}
  roles:
  - master
  - data
  store:
    allow_mmap: false
path:
  data: /usr/share/elasticsearch/data
  logs: /usr/share/elasticsearch/logs
xpack:
  license:
    upload:
      types:
      - trial
      - enterprise
  security:
    authc:
      realms:
        file:
          file1:
            order: -100
        native:
          native1:
            order: -99
      reserved_realm:
        enabled: "false"
    enabled: "true"
    http:
      ssl:
        certificate: /usr/share/elasticsearch/config/http-certs/tls.crt
        certificate_authorities: /usr/share/elasticsearch/config/http-certs/ca.crt
        enabled: true
        key: /usr/share/elasticsearch/config/http-certs/tls.key
    transport:
      ssl:
        certificate: /usr/share/elasticsearch/config/node-transport-cert/transport.tls.crt
        certificate_authorities:
        - /usr/share/elasticsearch/config/transport-certs/ca.crt
        - /usr/share/elasticsearch/config/transport-remote-certs/ca.crt
        enabled: "true"
        key: /usr/share/elasticsearch/config/node-transport-cert/transport.tls.key
        verification_mode: certificate

Does anyone know how to use the provided certificate in my Spring Boot application? Thank you.

2 Answers2

5

I solved my problem by ignoring SSL certificate verification while connecting to elasticsearch from my Backend (Spring Boot). I followed some instruction from website below:

Ignore SSL Certificate Verification

I also modified the code by adding basic authentication as follows:

@Configuration
@EnableElasticsearchRepositories(basePackages = "com.siolbca.repository")
@ComponentScan(basePackages = "com.siolbca.services")
public class Config {
    @Bean
    public RestHighLevelClient createSimpleElasticClient() throws Exception {
        try {
            final CredentialsProvider credentialsProvider =
                    new BasicCredentialsProvider();
                credentialsProvider.setCredentials(AuthScope.ANY,
                    new UsernamePasswordCredentials("elastic","G0D1g6TurJ79pcxr1065pU0U"));
                
            SSLContextBuilder sslBuilder = SSLContexts.custom()
                    .loadTrustMaterial(null, (x509Certificates, s) -> true);
                    final SSLContext sslContext = sslBuilder.build();
            RestHighLevelClient client = new RestHighLevelClient(RestClient
                    .builder(new HttpHost("elasticsearch-siol-es-http.siolbca-dev.svc.cluster.local", 9200, "https")) 
//port number is given as 443 since its https schema
                    .setHttpClientConfigCallback(new HttpClientConfigCallback() {
                        @Override
                        public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                            return httpClientBuilder
                                     .setSSLContext(sslContext)
                                     .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                                     .setDefaultCredentialsProvider(credentialsProvider);
                        }
                    })
                    .setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
                        @Override
                        public RequestConfig.Builder customizeRequestConfig(
                                RequestConfig.Builder requestConfigBuilder) {
                            return requestConfigBuilder.setConnectTimeout(5000)
                                    .setSocketTimeout(120000);
                        }
                    }));
            System.out.println("elasticsearch client created");
            return client;
        } catch (Exception e) {
            System.out.println(e);
            throw new Exception("Could not create an elasticsearch client!!");
        }
    }
    
}
2

In org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations class bean RestClientBuilder is created in method elasticsearchRestClientBuilder. We can configure this bean with RestClientBuilderCustomizer

@Bean
public RestClientBuilderCustomizer restClientBuilderCustomizer() {
    return new RestClientBuilderCustomizer() {
      @Override
      public void customize(RestClientBuilder builder) {
      }

      @SneakyThrows
      @Override
      public void customize(HttpAsyncClientBuilder builder) {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        Certificate ca;
        try (InputStream certificateInputStream = new FileInputStream("http_ca.crt")) {
          ca = cf.generateCertificate(certificateInputStream);
        }

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        // Create an SSLContext that uses our TrustManager
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        builder.setSSLContext(context);
      }
    };
  }

http_ca.crt file you can get after executing docker cp CONTAINER_NAME:/usr/share/elasticsearch/config/certs/http_ca.crt .

according to https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
  • We can also not use customizer but configure the SSLContext directly as shown in OPs code because there is `usingSSL` method that takes the `SSLContext` as parameter. – Michał Krzywański Jan 07 '23 at 08:50