Skip to content

Commit 406961c

Browse files
authored
Allow SSL configuration with the Elasticsearch CA fingerprint.
Original Pull Request #2540 Closes #2539
1 parent 17ecce4 commit 406961c

File tree

6 files changed

+62
-29
lines changed

6 files changed

+62
-29
lines changed

src/main/asciidoc/reference/elasticsearch-clients.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
240240
241241
<.> Define default headers, if they need to be customized
242242
<.> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
243-
<.> Optionally enable SSL.
243+
<.> Optionally enable SSL. There exist overloads of this function that can take a `SSLContext` or as an alternative the fingerprint of the certificate as it is output by Elasticsearch 8 on startup.
244244
<.> Optionally set a proxy.
245245
<.> Optionally set a path prefix, mostly used when different clusters a behind some reverse proxy.
246246
<.> Set the connection timeout.

src/main/asciidoc/reference/elasticsearch-new.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
== New in Spring Data Elasticsearch 5.1
66

77
* Upgrade to Elasticsearch 8.7.0
8+
* Allow specification of the TLS certificate when connecting to an Elasticsearch 8 cluster
89

910
[[new-features.5-0-0]]
1011
== New in Spring Data Elasticsearch 5.0

src/main/java/org/springframework/data/elasticsearch/client/ClientConfiguration.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,13 @@
2020
import java.time.Duration;
2121
import java.util.List;
2222
import java.util.Optional;
23-
import java.util.function.Function;
2423
import java.util.function.Supplier;
2524

2625
import javax.net.ssl.HostnameVerifier;
2726
import javax.net.ssl.SSLContext;
2827

2928
import org.springframework.data.elasticsearch.support.HttpHeaders;
3029
import org.springframework.lang.Nullable;
31-
import org.springframework.web.reactive.function.client.WebClient;
3230

3331
/**
3432
* Configuration interface exposing common client configuration properties for Elasticsearch clients.
@@ -122,6 +120,12 @@ static ClientConfiguration create(InetSocketAddress socketAddress) {
122120
*/
123121
Optional<SSLContext> getSslContext();
124122

123+
/**
124+
* @return the optional SHA-256 fingerprint of the self-signed http_ca.crt certificate output by Elasticsearch at
125+
* startup time.
126+
*/
127+
Optional<String> getCaFingerprint();
128+
125129
/**
126130
* Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
127131
*
@@ -250,6 +254,15 @@ interface MaybeSecureClientConfigurationBuilder extends TerminalClientConfigurat
250254
* @return the {@link TerminalClientConfigurationBuilder}.
251255
*/
252256
TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext, HostnameVerifier hostnameVerifier);
257+
258+
/**
259+
* Connect via https using a SSLContext that is build from the given certificate fingerprint.
260+
*
261+
* @param caFingerprint the SHA-256 fingerprint of the self-signed http_ca.crt certificate output by Elasticsearch
262+
* at startup time.
263+
* @return the {@link TerminalClientConfigurationBuilder}.
264+
*/
265+
TerminalClientConfigurationBuilder usingSsl(String caFingerprint);
253266
}
254267

255268
/**

src/main/java/org/springframework/data/elasticsearch/client/ClientConfigurationBuilder.java

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.ArrayList;
2121
import java.util.Arrays;
2222
import java.util.List;
23-
import java.util.function.Function;
2423
import java.util.function.Supplier;
2524

2625
import javax.net.ssl.HostnameVerifier;
@@ -33,7 +32,6 @@
3332
import org.springframework.data.elasticsearch.support.HttpHeaders;
3433
import org.springframework.lang.Nullable;
3534
import org.springframework.util.Assert;
36-
import org.springframework.web.reactive.function.client.WebClient;
3735

3836
/**
3937
* Default builder implementation for {@link ClientConfiguration}.
@@ -51,14 +49,15 @@ class ClientConfigurationBuilder
5149
private final List<InetSocketAddress> hosts = new ArrayList<>();
5250
private HttpHeaders headers = new HttpHeaders();
5351
private boolean useSsl;
54-
private @Nullable SSLContext sslContext;
55-
private @Nullable HostnameVerifier hostnameVerifier;
52+
@Nullable private SSLContext sslContext;
53+
@Nullable private String caFingerprint;
54+
@Nullable private HostnameVerifier hostnameVerifier;
5655
private Duration connectTimeout = Duration.ofSeconds(10);
5756
private Duration soTimeout = Duration.ofSeconds(5);
58-
private @Nullable String username;
59-
private @Nullable String password;
60-
private @Nullable String pathPrefix;
61-
private @Nullable String proxy;
57+
@Nullable private String username;
58+
@Nullable private String password;
59+
@Nullable private String pathPrefix;
60+
@Nullable private String proxy;
6261
private Supplier<HttpHeaders> headersSupplier = HttpHeaders::new;
6362
@Deprecated private final HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
6463
private final List<ClientConfiguration.ClientConfigurationCallback<?>> clientConfigurers = new ArrayList<>();
@@ -138,10 +137,20 @@ public TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext, Hostna
138137
return this;
139138
}
140139

140+
@Override
141+
public TerminalClientConfigurationBuilder usingSsl(String caFingerprint) {
142+
143+
Assert.notNull(caFingerprint, "caFingerprint must not be null");
144+
145+
this.useSsl = true;
146+
this.caFingerprint = caFingerprint;
147+
return this;
148+
}
149+
141150
/*
142-
* (non-Javadoc)
143-
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withDefaultHeaders(org.springframework.http.HttpHeaders)
144-
*/
151+
* (non-Javadoc)
152+
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withDefaultHeaders(org.springframework.http.HttpHeaders)
153+
*/
145154
@Override
146155
public TerminalClientConfigurationBuilder withDefaultHeaders(HttpHeaders defaultHeaders) {
147156

@@ -228,8 +237,12 @@ public ClientConfiguration build() {
228237
headers.setBasicAuth(username, password);
229238
}
230239

231-
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout, pathPrefix,
232-
hostnameVerifier, proxy, httpClientConfigurer, clientConfigurers, headersSupplier);
240+
if (sslContext != null && caFingerprint != null) {
241+
throw new IllegalArgumentException("Either SSLContext or caFingerprint must be set, but not both");
242+
}
243+
244+
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, caFingerprint, soTimeout, connectTimeout,
245+
pathPrefix, hostnameVerifier, proxy, httpClientConfigurer, clientConfigurers, headersSupplier);
233246
}
234247

235248
private static InetSocketAddress parse(String hostAndPort) {

src/main/java/org/springframework/data/elasticsearch/client/DefaultClientConfiguration.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,28 @@ class DefaultClientConfiguration implements ClientConfiguration {
4242
private final List<InetSocketAddress> hosts;
4343
private final HttpHeaders headers;
4444
private final boolean useSsl;
45-
private final @Nullable SSLContext sslContext;
45+
@Nullable private final SSLContext sslContext;
46+
@Nullable private final String caFingerprint;
4647
private final Duration soTimeout;
4748
private final Duration connectTimeout;
48-
private final @Nullable String pathPrefix;
49-
private final @Nullable HostnameVerifier hostnameVerifier;
50-
private final @Nullable String proxy;
49+
@Nullable private final String pathPrefix;
50+
@Nullable private final HostnameVerifier hostnameVerifier;
51+
@Nullable private final String proxy;
5152
private final HttpClientConfigCallback httpClientConfigurer;
5253
private final Supplier<HttpHeaders> headersSupplier;
5354
private final List<ClientConfigurationCallback<?>> clientConfigurers;
5455

5556
DefaultClientConfiguration(List<InetSocketAddress> hosts, HttpHeaders headers, boolean useSsl,
56-
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix,
57-
@Nullable HostnameVerifier hostnameVerifier, @Nullable String proxy,
57+
@Nullable SSLContext sslContext, @Nullable String caFingerprint, Duration soTimeout, Duration connectTimeout,
58+
@Nullable String pathPrefix, @Nullable HostnameVerifier hostnameVerifier, @Nullable String proxy,
5859
HttpClientConfigCallback httpClientConfigurer, List<ClientConfigurationCallback<?>> clientConfigurers,
5960
Supplier<HttpHeaders> headersSupplier) {
6061

6162
this.hosts = List.copyOf(hosts);
6263
this.headers = headers;
6364
this.useSsl = useSsl;
6465
this.sslContext = sslContext;
66+
this.caFingerprint = caFingerprint;
6567
this.soTimeout = soTimeout;
6668
this.connectTimeout = connectTimeout;
6769
this.pathPrefix = pathPrefix;
@@ -92,6 +94,11 @@ public Optional<SSLContext> getSslContext() {
9294
return Optional.ofNullable(this.sslContext);
9395
}
9496

97+
@Override
98+
public Optional<String> getCaFingerprint() {
99+
return Optional.ofNullable(this.caFingerprint);
100+
}
101+
95102
@Override
96103
public Optional<HostnameVerifier> getHostNameVerifier() {
97104
return Optional.ofNullable(this.hostnameVerifier);

src/main/java/org/springframework/data/elasticsearch/client/elc/ElasticsearchClients.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
2020
import co.elastic.clients.transport.ElasticsearchTransport;
2121
import co.elastic.clients.transport.TransportOptions;
22+
import co.elastic.clients.transport.TransportUtils;
2223
import co.elastic.clients.transport.Version;
2324
import co.elastic.clients.transport.rest_client.RestClientOptions;
2425
import co.elastic.clients.transport.rest_client.RestClientTransport;
@@ -34,13 +35,7 @@
3435
import java.util.function.Supplier;
3536
import java.util.stream.Collectors;
3637

37-
import org.apache.http.HttpEntity;
38-
import org.apache.http.HttpEntityEnclosingRequest;
39-
import org.apache.http.HttpHost;
40-
import org.apache.http.HttpRequest;
41-
import org.apache.http.HttpRequestInterceptor;
42-
import org.apache.http.HttpResponse;
43-
import org.apache.http.HttpResponseInterceptor;
38+
import org.apache.http.*;
4439
import org.apache.http.client.config.RequestConfig;
4540
import org.apache.http.entity.ByteArrayEntity;
4641
import org.apache.http.entity.ContentType;
@@ -197,6 +192,10 @@ private static RestClientBuilder getRestClientBuilder(ClientConfiguration client
197192
}
198193

199194
builder.setHttpClientConfigCallback(clientBuilder -> {
195+
if (clientConfiguration.getCaFingerprint().isPresent()) {
196+
clientBuilder
197+
.setSSLContext(TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get()));
198+
}
200199
clientConfiguration.getSslContext().ifPresent(clientBuilder::setSSLContext);
201200
clientConfiguration.getHostNameVerifier().ifPresent(clientBuilder::setSSLHostnameVerifier);
202201
clientBuilder.addInterceptorLast(new CustomHeaderInjector(clientConfiguration.getHeadersSupplier()));

0 commit comments

Comments
 (0)