Skip to content

Commit 6373d73

Browse files
committed
Introduce SslProfile object
1 parent aa58fc7 commit 6373d73

File tree

3 files changed

+159
-15
lines changed

3 files changed

+159
-15
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.apache.http.HttpHost;
1010
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
1111
import org.apache.http.conn.ssl.NoopHostnameVerifier;
12+
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
1213
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
1314
import org.apache.http.nio.reactor.IOSession;
1415
import org.apache.logging.log4j.LogManager;
@@ -216,6 +217,14 @@ public static void registerSettings(List<Setting<?>> settingList) {
216217
settingList.add(DIAGNOSE_TRUST_EXCEPTIONS_SETTING);
217218
}
218219

220+
public SslProfile profile(String profileName) {
221+
final SslConfiguration configuration = getSSLConfiguration(profileName);
222+
if (configuration == null) {
223+
throw new IllegalArgumentException(Strings.format("No SSL configuration for context name [%s]", profileName));
224+
}
225+
return sslContextHolder(configuration);
226+
}
227+
219228
/**
220229
* Create a new {@link SSLIOSessionStrategy} based on the provided settings. The settings are used to identify the SSL configuration
221230
* that should be used to create the context.
@@ -797,7 +806,7 @@ private static SSLSocket createWithPermissions(CheckedSupplier<Socket, IOExcepti
797806
}
798807
}
799808

800-
final class SSLContextHolder {
809+
final class SSLContextHolder implements SslProfile {
801810
private volatile SSLContext context;
802811
private final SslKeyConfig keyConfig;
803812
private final SslTrustConfig trustConfig;
@@ -812,10 +821,40 @@ final class SSLContextHolder {
812821
this.reloadListeners = new ArrayList<>();
813822
}
814823

815-
SSLContext sslContext() {
824+
public SSLContext sslContext() {
816825
return context;
817826
}
818827

828+
@Override
829+
public SslConfiguration configuration() {
830+
return this.sslConfiguration;
831+
}
832+
833+
@Override
834+
public SSLSocketFactory socketFactory() {
835+
return SSLService.this.sslSocketFactory(this.sslConfiguration);
836+
}
837+
838+
@Override
839+
public HostnameVerifier hostnameVerifier() {
840+
return SSLService.getHostnameVerifier(this.sslConfiguration);
841+
}
842+
843+
@Override
844+
public SSLConnectionSocketFactory socketConnectionFactory() {
845+
return new SSLConnectionSocketFactory(socketFactory(), hostnameVerifier());
846+
}
847+
848+
@Override
849+
public SSLIOSessionStrategy ioSessionStrategy4() {
850+
return SSLService.this.sslIOSessionStrategy(this.sslConfiguration);
851+
}
852+
853+
@Override
854+
public SSLEngine engine(String host, int port) {
855+
return SSLService.this.createSSLEngine(this.configuration(), host, port);
856+
}
857+
819858
synchronized void reload() {
820859
invalidateSessions(context.getClientSessionContext());
821860
invalidateSessions(context.getServerSessionContext());
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.ssl;
9+
10+
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
11+
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
12+
import org.elasticsearch.common.ssl.SslConfiguration;
13+
14+
import javax.net.ssl.HostnameVerifier;
15+
import javax.net.ssl.SSLContext;
16+
import javax.net.ssl.SSLEngine;
17+
import javax.net.ssl.SSLSocketFactory;
18+
19+
public interface SslProfile {
20+
SslConfiguration configuration();
21+
22+
SSLContext sslContext();
23+
24+
SSLSocketFactory socketFactory();
25+
26+
HostnameVerifier hostnameVerifier();
27+
28+
SSLConnectionSocketFactory socketConnectionFactory();
29+
30+
/**
31+
* @return An object that is useful for configuring Apache Http Client v4.x
32+
*/
33+
SSLIOSessionStrategy ioSessionStrategy4();
34+
35+
SSLEngine engine(String host, int port);
36+
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@
7777
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
7878
import static org.hamcrest.Matchers.arrayWithSize;
7979
import static org.hamcrest.Matchers.contains;
80+
import static org.hamcrest.Matchers.containsInAnyOrder;
8081
import static org.hamcrest.Matchers.containsString;
8182
import static org.hamcrest.Matchers.equalTo;
8283
import static org.hamcrest.Matchers.greaterThan;
8384
import static org.hamcrest.Matchers.hasItem;
85+
import static org.hamcrest.Matchers.hasItemInArray;
8486
import static org.hamcrest.Matchers.instanceOf;
8587
import static org.hamcrest.Matchers.is;
8688
import static org.hamcrest.Matchers.iterableWithSize;
@@ -170,6 +172,18 @@ public void testThatCustomTruststoreCanBeSpecified() throws Exception {
170172
final SslConfiguration profileConfiguration = sslService.getSSLConfiguration("transport.profiles.foo.xpack.security.ssl");
171173
assertThat(profileConfiguration, notNullValue());
172174
assertThat(profileConfiguration.getDependentFiles(), contains(testClientStore));
175+
176+
final SslProfile defaultSslProfile = sslService.profile(
177+
randomFrom("xpack.security.transport.ssl", "xpack.security.transport.ssl.")
178+
);
179+
assertThat(defaultSslProfile, notNullValue());
180+
assertThat(defaultSslProfile.configuration().trustConfig().getDependentFiles(), containsInAnyOrder(testnodeStore));
181+
182+
final SslProfile fooSslProfile = sslService.profile(
183+
randomFrom("transport.profiles.foo.xpack.security.ssl", "transport.profiles.foo.xpack.security.ssl.")
184+
);
185+
assertThat(fooSslProfile, notNullValue());
186+
assertThat(fooSslProfile.configuration().trustConfig().getDependentFiles(), containsInAnyOrder(testClientStore));
173187
}
174188

175189
public void testThatSslContextCachingWorks() throws Exception {
@@ -192,6 +206,12 @@ public void testThatSslContextCachingWorks() throws Exception {
192206
final SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
193207
final SSLContext configContext = sslService.sslContext(configuration);
194208
assertThat(configContext, is(sameInstance(sslContext)));
209+
210+
final SslProfile defaultSslProfile = sslService.profile(
211+
randomFrom("xpack.security.transport.ssl", "xpack.security.transport.ssl.")
212+
);
213+
assertThat(defaultSslProfile, notNullValue());
214+
assertThat(defaultSslProfile.sslContext(), sameInstance(sslContext));
195215
}
196216

197217
public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
@@ -211,6 +231,9 @@ public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
211231
final SSLService sslService = new SSLService(TestEnvironment.newEnvironment(buildEnvSettings(settings)));
212232
SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
213233
sslService.createSSLEngine(configuration, null, -1);
234+
235+
final SslProfile profile = sslService.profile("xpack.security.transport.ssl.");
236+
profile.engine(null, -1);
214237
}
215238

216239
public void testIncorrectKeyPasswordThrowsException() throws Exception {
@@ -248,13 +271,20 @@ public void testThatSSLv3IsNotEnabled() throws Exception {
248271
SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
249272
SSLEngine engine = sslService.createSSLEngine(configuration, null, -1);
250273
assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3")));
274+
275+
final SslProfile profile = sslService.profile("xpack.security.transport.ssl.");
276+
final String[] profileProtocols = profile.engine(null, -1).getEnabledProtocols();
277+
assertThat(profileProtocols, not(hasItemInArray("SSLv3")));
278+
assertThat(profileProtocols, hasItemInArray("TLSv1.2"));
251279
}
252280

253281
public void testThatCreateClientSSLEngineWithoutAnySettingsWorks() throws Exception {
254282
SSLService sslService = new SSLService(env);
255283
SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
256284
SSLEngine sslEngine = sslService.createSSLEngine(configuration, null, -1);
257285
assertThat(sslEngine, notNullValue());
286+
287+
assertThat(sslService.profile("xpack.security.transport.ssl.").engine(null, -1), notNullValue());
258288
}
259289

260290
public void testThatCreateSSLEngineWithOnlyTruststoreWorks() throws Exception {
@@ -269,6 +299,8 @@ public void testThatCreateSSLEngineWithOnlyTruststoreWorks() throws Exception {
269299
SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.http.ssl");
270300
SSLEngine sslEngine = sslService.createSSLEngine(configuration, null, -1);
271301
assertThat(sslEngine, notNullValue());
302+
303+
assertThat(sslService.profile("xpack.security.http.ssl.").engine(null, -1), notNullValue());
272304
}
273305

274306
public void testCreateWithKeystoreIsValidForServer() throws Exception {
@@ -366,6 +398,15 @@ public void testGetVerificationMode() throws Exception {
366398
sslService.getSSLConfiguration("transport.profiles.foo.xpack.security.ssl.").verificationMode(),
367399
is(SslVerificationMode.FULL)
368400
);
401+
402+
assertThat(
403+
sslService.profile("xpack.security.transport.ssl").configuration().verificationMode(),
404+
is(SslVerificationMode.CERTIFICATE)
405+
);
406+
assertThat(
407+
sslService.profile("transport.profiles.foo.xpack.security.ssl").configuration().verificationMode(),
408+
is(SslVerificationMode.FULL)
409+
);
369410
}
370411

371412
public void testIsSSLClientAuthEnabled() throws Exception {
@@ -453,11 +494,18 @@ public void testCiphersAndInvalidCiphersWork() throws Exception {
453494
.putList("xpack.security.transport.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
454495
.build();
455496
SSLService sslService = new SSLService(TestEnvironment.newEnvironment(buildEnvSettings(settings)));
456-
SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
497+
498+
final SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
457499
SSLEngine engine = sslService.createSSLEngine(configuration, null, -1);
458500
assertThat(engine, is(notNullValue()));
459501
String[] enabledCiphers = engine.getEnabledCipherSuites();
460502
assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar")));
503+
504+
final SslProfile profile = sslService.profile("xpack.security.transport.ssl.");
505+
engine = profile.engine(null, -1);
506+
assertThat(engine, is(notNullValue()));
507+
enabledCiphers = engine.getEnabledCipherSuites();
508+
assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar")));
461509
}
462510

463511
public void testInvalidCiphersOnlyThrowsException() throws Exception {
@@ -492,11 +540,18 @@ public void testThatSSLEngineHasCipherSuitesOrderSet() throws Exception {
492540
.put("xpack.security.transport.ssl.key", testnodeKey)
493541
.setSecureSettings(secureSettings)
494542
.build();
543+
495544
SSLService sslService = new SSLService(TestEnvironment.newEnvironment(buildEnvSettings(settings)));
496-
SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
545+
546+
final SslConfiguration configuration = sslService.getSSLConfiguration("xpack.security.transport.ssl");
497547
SSLEngine engine = sslService.createSSLEngine(configuration, null, -1);
498548
assertThat(engine, is(notNullValue()));
499549
assertTrue(engine.getSSLParameters().getUseCipherSuitesOrder());
550+
551+
final SslProfile profile = sslService.profile("xpack.security.transport.ssl.");
552+
engine = profile.engine(null, -1);
553+
assertThat(engine, is(notNullValue()));
554+
assertTrue(engine.getSSLParameters().getUseCipherSuitesOrder());
500555
}
501556

502557
public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Exception {
@@ -508,14 +563,22 @@ public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Except
508563
.put("xpack.security.transport.ssl.key", testnodeKey)
509564
.setSecureSettings(secureSettings)
510565
.build();
566+
511567
SSLService sslService = new SSLService(TestEnvironment.newEnvironment(buildEnvSettings(settings)));
568+
512569
SslConfiguration config = sslService.getSSLConfiguration("xpack.security.transport.ssl");
513-
final SSLSocketFactory factory = sslService.sslSocketFactory(config);
514-
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), config.getCipherSuites(), false);
515-
assertThat(factory.getDefaultCipherSuites(), is(ciphers));
570+
571+
final SSLSocketFactory configFactory = sslService.sslSocketFactory(config);
572+
final String[] ciphers = sslService.supportedCiphers(configFactory.getSupportedCipherSuites(), config.getCipherSuites(), false);
573+
assertThat(configFactory.getDefaultCipherSuites(), is(ciphers));
574+
575+
SslProfile profile = sslService.profile("xpack.security.transport.ssl");
576+
final SSLSocketFactory profileFactory = profile.socketFactory();
577+
assertThat(profileFactory.getSupportedCipherSuites(), is(configFactory.getSupportedCipherSuites()));
578+
assertThat(profileFactory.getDefaultCipherSuites(), is(configFactory.getDefaultCipherSuites()));
516579

517580
final String[] getSupportedProtocols = config.supportedProtocols().toArray(Strings.EMPTY_ARRAY);
518-
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
581+
try (SSLSocket socket = (SSLSocket) randomFrom(configFactory, profileFactory).createSocket()) {
519582
assertThat(socket.getEnabledCipherSuites(), is(ciphers));
520583
// the order we set the protocols in is not going to be what is returned as internally the JDK may sort the versions
521584
assertThat(socket.getEnabledProtocols(), arrayContainingInAnyOrder(getSupportedProtocols));
@@ -644,6 +707,8 @@ public void testGetConfigurationByContextName() throws Exception {
644707
assertThat("KeyStore Path for " + name, keyConfig.getDependentFiles(), contains(testnodeStore));
645708
assertThat("Cipher for " + name, configuration.getCipherSuites(), contains(getCipherSuites[i]));
646709
assertThat("Configuration for " + name + ".", sslService.getSSLConfiguration(name + "."), sameInstance(configuration));
710+
711+
assertThat(sslService.profile(name).configuration(), sameInstance(configuration));
647712
}
648713
}
649714

@@ -893,13 +958,17 @@ public int getSessionCacheSize() {
893958

894959
@Network
895960
public void testThatSSLContextWithoutSettingsWorks() throws Exception {
896-
SSLService sslService = new SSLService(env);
897-
SSLContext sslContext = sslService.sslContext(sslService.sslConfiguration(Settings.EMPTY));
898-
try (CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build()) {
899-
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
900-
// This will result in an SSLHandshakeException if the SSLContext does not trust the CA, but the default
901-
// truststore trusts all common public CAs so the handshake will succeed
902-
privilegedConnect(() -> client.execute(new HttpGet("https://www.elastic.co/")).close());
961+
final SSLService sslService = new SSLService(env);
962+
final SSLContext sslContext1 = sslService.sslContext(sslService.sslConfiguration(Settings.EMPTY));
963+
final SSLContext sslContext2 = sslService.profile("xpack.http.ssl").sslContext();
964+
965+
for (var sslContext : List.of(sslContext1, sslContext2)) {
966+
try (CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build()) {
967+
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
968+
// This will result in an SSLHandshakeException if the SSLContext does not trust the CA, but the default
969+
// truststore trusts all common public CAs so the handshake will succeed
970+
privilegedConnect(() -> client.execute(new HttpGet("https://www.elastic.co/")).close());
971+
}
903972
}
904973
}
905974

0 commit comments

Comments
 (0)