Skip to content

Commit 79b6ef0

Browse files
tvernumJeremyDahlgren
authored andcommitted
Introduce SslProfile class to simplify SSLService (elastic#132241)
This change introduces a new `SslProfile` class. The `SslProfile` is roughly analogous to `SslConfiguration` and provides the runtime implementation support for an "ssl context" (but here called a "profile" because `SSLContext` is already a thing). This means that a number of methods that were previously called on `SSLService` are now called on a contextual `SslProfile`. For example, `SSLService.getHostnameVerifier(SslConfiguration)` is now `SslProfile.hostnameVerifier()` This has two primary benefits 1. Moving these methods out of `SSLService` provides a cleaner split. `SSLService` is now (more of) a manager for accessing profiles and configurations, and the profiles provide the runtime support for creating SSL connections. 2. It moves us a step towards having extensible SSL contexts/profiles. Because a `SslProfile` is self contained, we can inject one into a defined extension point without needing any direct reference to an `SSLService`. That is we can work towards an SPI extension point such as ``` public interface SslExtension { public Set<String> getSslProfileNames(); public void applySslProfile(String name, SslProfile profile); } ``` and that interface doesn't need to know anything more than what is contained in `SslProfile` (it knows nothing of `SSLService`).
1 parent 5169d09 commit 79b6ef0

File tree

31 files changed

+622
-468
lines changed

31 files changed

+622
-468
lines changed

libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfiguration.java

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -157,34 +157,4 @@ private String contextProtocol() {
157157
"no supported SSL/TLS protocol was found in the configured supported protocols: " + supportedProtocols
158158
);
159159
}
160-
161-
// TODO Add explicitlyConfigured to equals&hashCode?
162-
@Override
163-
public boolean equals(Object o) {
164-
if (this == o) return true;
165-
if (o == null || getClass() != o.getClass()) return false;
166-
final SslConfiguration that = (SslConfiguration) o;
167-
return Objects.equals(this.settingPrefix, that.settingPrefix)
168-
&& Objects.equals(this.trustConfig, that.trustConfig)
169-
&& Objects.equals(this.keyConfig, that.keyConfig)
170-
&& this.verificationMode == that.verificationMode
171-
&& this.clientAuth == that.clientAuth
172-
&& Objects.equals(this.ciphers, that.ciphers)
173-
&& Objects.equals(this.supportedProtocols, that.supportedProtocols)
174-
&& this.handshakeTimeoutMillis == that.handshakeTimeoutMillis;
175-
}
176-
177-
@Override
178-
public int hashCode() {
179-
return Objects.hash(
180-
settingPrefix,
181-
trustConfig,
182-
keyConfig,
183-
verificationMode,
184-
clientAuth,
185-
ciphers,
186-
supportedProtocols,
187-
handshakeTimeoutMillis
188-
);
189-
}
190160
}

modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/TLSConfig.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,28 @@
99

1010
package org.elasticsearch.transport.netty4;
1111

12-
import org.elasticsearch.common.ssl.SslConfiguration;
13-
1412
import javax.net.ssl.SSLEngine;
1513

16-
public record TLSConfig(SslConfiguration sslConfiguration, EngineProvider engineProvider) {
14+
public record TLSConfig(EngineProvider engineProvider) {
1715

1816
public boolean isTLSEnabled() {
19-
return sslConfiguration != null;
17+
return engineProvider != null;
2018
}
2119

2220
public SSLEngine createServerSSLEngine() {
2321
assert isTLSEnabled();
24-
SSLEngine sslEngine = engineProvider.create(sslConfiguration, null, -1);
22+
SSLEngine sslEngine = engineProvider.create(null, -1);
2523
sslEngine.setUseClientMode(false);
2624
return sslEngine;
2725
}
2826

2927
public static TLSConfig noTLS() {
30-
return new TLSConfig(null, null);
28+
return new TLSConfig(null);
3129
}
3230

3331
@FunctionalInterface
3432
public interface EngineProvider {
3533

36-
SSLEngine create(SslConfiguration configuration, String host, int port);
34+
SSLEngine create(String host, int port);
3735
}
3836
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/CommandLineHttpClient.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import org.elasticsearch.common.network.NetworkService;
1515
import org.elasticsearch.common.settings.SecureString;
1616
import org.elasticsearch.common.settings.Settings;
17-
import org.elasticsearch.common.ssl.SslConfiguration;
1817
import org.elasticsearch.core.CharArrays;
1918
import org.elasticsearch.core.CheckedFunction;
2019
import org.elasticsearch.core.Releasables;
@@ -26,6 +25,7 @@
2625
import org.elasticsearch.xpack.core.security.HttpResponse.HttpResponseBuilder;
2726
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
2827
import org.elasticsearch.xpack.core.ssl.SSLService;
28+
import org.elasticsearch.xpack.core.ssl.SslProfile;
2929

3030
import java.io.IOException;
3131
import java.io.InputStream;
@@ -154,10 +154,12 @@ private HttpResponse execute(
154154
sslContext.init(null, new TrustManager[] { fingerprintTrustingTrustManager(pinnedCaCertFingerprint) }, null);
155155
httpsConn.setSSLSocketFactory(sslContext.getSocketFactory());
156156
} else {
157-
final SslConfiguration sslConfiguration = sslService.getHttpTransportSSLConfiguration();
157+
final SslProfile sslProfile = sslService.profile(XPackSettings.HTTP_SSL_PREFIX);
158158
// Requires permission java.lang.RuntimePermission "setFactory";
159-
httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslConfiguration));
160-
final boolean isHostnameVerificationEnabled = sslConfiguration.verificationMode().isHostnameVerificationEnabled();
159+
httpsConn.setSSLSocketFactory(sslProfile.socketFactory());
160+
final boolean isHostnameVerificationEnabled = sslProfile.configuration()
161+
.verificationMode()
162+
.isHostnameVerificationEnabled();
161163
if (isHostnameVerificationEnabled == false) {
162164
httpsConn.setHostnameVerifier((hostname, session) -> true);
163165
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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.HttpHost;
11+
import org.apache.http.conn.ssl.NoopHostnameVerifier;
12+
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
13+
import org.apache.http.nio.reactor.IOSession;
14+
import org.elasticsearch.common.Strings;
15+
import org.elasticsearch.common.logging.LoggerMessageFormat;
16+
import org.elasticsearch.common.ssl.SslConfiguration;
17+
import org.elasticsearch.common.ssl.SslDiagnostics;
18+
19+
import java.security.cert.Certificate;
20+
import java.security.cert.X509Certificate;
21+
import java.util.List;
22+
23+
import javax.net.ssl.HostnameVerifier;
24+
import javax.net.ssl.SSLContext;
25+
import javax.net.ssl.SSLException;
26+
import javax.net.ssl.SSLParameters;
27+
import javax.net.ssl.SSLPeerUnverifiedException;
28+
import javax.net.ssl.SSLSession;
29+
import javax.security.auth.x500.X500Principal;
30+
31+
public class SSLIOSessionStrategyBuilder {
32+
33+
public static final SSLIOSessionStrategyBuilder INSTANCE = new SSLIOSessionStrategyBuilder();
34+
35+
public SSLIOSessionStrategy sslIOSessionStrategy(SslConfiguration config, SSLContext sslContext) {
36+
String[] ciphers = supportedCiphers(sslParameters(sslContext).getCipherSuites(), config.getCipherSuites(), false);
37+
String[] supportedProtocols = config.supportedProtocols().toArray(Strings.EMPTY_ARRAY);
38+
HostnameVerifier verifier;
39+
40+
if (config.verificationMode().isHostnameVerificationEnabled()) {
41+
verifier = SSLIOSessionStrategy.getDefaultHostnameVerifier();
42+
} else {
43+
verifier = NoopHostnameVerifier.INSTANCE;
44+
}
45+
46+
return sslIOSessionStrategy(sslContext, supportedProtocols, ciphers, verifier);
47+
}
48+
49+
/**
50+
* This method exists to simplify testing
51+
*/
52+
String[] supportedCiphers(String[] supportedCiphers, List<String> requestedCiphers, boolean log) {
53+
return SSLService.supportedCiphers(supportedCiphers, requestedCiphers, log);
54+
}
55+
56+
/**
57+
* The {@link SSLParameters} that are associated with the {@code sslContext}.
58+
* <p>
59+
* This method exists to simplify testing since {@link SSLContext#getSupportedSSLParameters()} is {@code final}.
60+
*
61+
* @param sslContext The SSL context for the current SSL settings
62+
* @return Never {@code null}.
63+
*/
64+
SSLParameters sslParameters(SSLContext sslContext) {
65+
return sslContext.getSupportedSSLParameters();
66+
}
67+
68+
/**
69+
* This method only exists to simplify testing because {@link SSLIOSessionStrategy} does
70+
* not expose any of the parameters that you give it.
71+
*/
72+
SSLIOSessionStrategy sslIOSessionStrategy(SSLContext sslContext, String[] protocols, String[] ciphers, HostnameVerifier verifier) {
73+
return new SSLIOSessionStrategy(sslContext, protocols, ciphers, verifier) {
74+
@Override
75+
protected void verifySession(HttpHost host, IOSession iosession, SSLSession session) throws SSLException {
76+
if (verifier.verify(host.getHostName(), session) == false) {
77+
final Certificate[] certs = session.getPeerCertificates();
78+
final X509Certificate x509 = (X509Certificate) certs[0];
79+
final X500Principal x500Principal = x509.getSubjectX500Principal();
80+
final String altNames = Strings.collectionToCommaDelimitedString(SslDiagnostics.describeValidHostnames(x509));
81+
throw new SSLPeerUnverifiedException(
82+
LoggerMessageFormat.format(
83+
"Expected SSL certificate to be valid for host [{}],"
84+
+ " but it is only valid for subject alternative names [{}] and subject [{}]",
85+
new Object[] { host.getHostName(), altNames, x500Principal.toString() }
86+
)
87+
);
88+
}
89+
}
90+
};
91+
}
92+
}

0 commit comments

Comments
 (0)