Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,34 +157,4 @@ private String contextProtocol() {
"no supported SSL/TLS protocol was found in the configured supported protocols: " + supportedProtocols
);
}

// TODO Add explicitlyConfigured to equals&hashCode?
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final SslConfiguration that = (SslConfiguration) o;
return Objects.equals(this.settingPrefix, that.settingPrefix)
&& Objects.equals(this.trustConfig, that.trustConfig)
&& Objects.equals(this.keyConfig, that.keyConfig)
&& this.verificationMode == that.verificationMode
&& this.clientAuth == that.clientAuth
&& Objects.equals(this.ciphers, that.ciphers)
&& Objects.equals(this.supportedProtocols, that.supportedProtocols)
&& this.handshakeTimeoutMillis == that.handshakeTimeoutMillis;
}

@Override
public int hashCode() {
return Objects.hash(
settingPrefix,
trustConfig,
keyConfig,
verificationMode,
clientAuth,
ciphers,
supportedProtocols,
handshakeTimeoutMillis
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,28 @@

package org.elasticsearch.transport.netty4;

import org.elasticsearch.common.ssl.SslConfiguration;

import javax.net.ssl.SSLEngine;

public record TLSConfig(SslConfiguration sslConfiguration, EngineProvider engineProvider) {
public record TLSConfig(EngineProvider engineProvider) {

public boolean isTLSEnabled() {
return sslConfiguration != null;
return engineProvider != null;
}

public SSLEngine createServerSSLEngine() {
assert isTLSEnabled();
SSLEngine sslEngine = engineProvider.create(sslConfiguration, null, -1);
SSLEngine sslEngine = engineProvider.create(null, -1);
sslEngine.setUseClientMode(false);
return sslEngine;
}

public static TLSConfig noTLS() {
return new TLSConfig(null, null);
return new TLSConfig(null);
}

@FunctionalInterface
public interface EngineProvider {

SSLEngine create(SslConfiguration configuration, String host, int port);
SSLEngine create(String host, int port);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.elasticsearch.common.network.NetworkService;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.ssl.SslConfiguration;
import org.elasticsearch.core.CharArrays;
import org.elasticsearch.core.CheckedFunction;
import org.elasticsearch.core.Releasables;
Expand All @@ -26,6 +25,7 @@
import org.elasticsearch.xpack.core.security.HttpResponse.HttpResponseBuilder;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.ssl.SSLService;
import org.elasticsearch.xpack.core.ssl.SslProfile;

import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -154,10 +154,12 @@ private HttpResponse execute(
sslContext.init(null, new TrustManager[] { fingerprintTrustingTrustManager(pinnedCaCertFingerprint) }, null);
httpsConn.setSSLSocketFactory(sslContext.getSocketFactory());
} else {
final SslConfiguration sslConfiguration = sslService.getHttpTransportSSLConfiguration();
final SslProfile sslProfile = sslService.profile(XPackSettings.HTTP_SSL_PREFIX);
// Requires permission java.lang.RuntimePermission "setFactory";
httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslConfiguration));
final boolean isHostnameVerificationEnabled = sslConfiguration.verificationMode().isHostnameVerificationEnabled();
httpsConn.setSSLSocketFactory(sslProfile.socketFactory());
final boolean isHostnameVerificationEnabled = sslProfile.configuration()
.verificationMode()
.isHostnameVerificationEnabled();
if (isHostnameVerificationEnabled == false) {
httpsConn.setHostnameVerifier((hostname, session) -> true);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.core.ssl;

import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.nio.reactor.IOSession;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.logging.LoggerMessageFormat;
import org.elasticsearch.common.ssl.SslConfiguration;
import org.elasticsearch.common.ssl.SslDiagnostics;

import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.security.auth.x500.X500Principal;

public class SSLIOSessionStrategyBuilder {

public static final SSLIOSessionStrategyBuilder INSTANCE = new SSLIOSessionStrategyBuilder();

public SSLIOSessionStrategy sslIOSessionStrategy(SslConfiguration config, SSLContext sslContext) {
String[] ciphers = supportedCiphers(sslParameters(sslContext).getCipherSuites(), config.getCipherSuites(), false);
String[] supportedProtocols = config.supportedProtocols().toArray(Strings.EMPTY_ARRAY);
HostnameVerifier verifier;

if (config.verificationMode().isHostnameVerificationEnabled()) {
verifier = SSLIOSessionStrategy.getDefaultHostnameVerifier();
} else {
verifier = NoopHostnameVerifier.INSTANCE;
}

return sslIOSessionStrategy(sslContext, supportedProtocols, ciphers, verifier);
}

/**
* This method exists to simplify testing
*/
String[] supportedCiphers(String[] supportedCiphers, List<String> requestedCiphers, boolean log) {
return SSLService.supportedCiphers(supportedCiphers, requestedCiphers, log);
}

/**
* The {@link SSLParameters} that are associated with the {@code sslContext}.
* <p>
* This method exists to simplify testing since {@link SSLContext#getSupportedSSLParameters()} is {@code final}.
*
* @param sslContext The SSL context for the current SSL settings
* @return Never {@code null}.
*/
SSLParameters sslParameters(SSLContext sslContext) {
return sslContext.getSupportedSSLParameters();
}

/**
* This method only exists to simplify testing because {@link SSLIOSessionStrategy} does
* not expose any of the parameters that you give it.
*/
SSLIOSessionStrategy sslIOSessionStrategy(SSLContext sslContext, String[] protocols, String[] ciphers, HostnameVerifier verifier) {
return new SSLIOSessionStrategy(sslContext, protocols, ciphers, verifier) {
@Override
protected void verifySession(HttpHost host, IOSession iosession, SSLSession session) throws SSLException {
if (verifier.verify(host.getHostName(), session) == false) {
final Certificate[] certs = session.getPeerCertificates();
final X509Certificate x509 = (X509Certificate) certs[0];
final X500Principal x500Principal = x509.getSubjectX500Principal();
final String altNames = Strings.collectionToCommaDelimitedString(SslDiagnostics.describeValidHostnames(x509));
throw new SSLPeerUnverifiedException(
LoggerMessageFormat.format(
"Expected SSL certificate to be valid for host [{}],"
+ " but it is only valid for subject alternative names [{}] and subject [{}]",
new Object[] { host.getHostName(), altNames, x500Principal.toString() }
)
);
}
}
};
}
}
Loading