[cleanup] separate SSLService into ClientSSLService and ServerSSLService
Separates the SSLService into a ClientSSLService and a ServerSSLService. The ClientSSLService should be used for client connections and removes the requirement for setting a keystore or a truststore. The ServerSSLService maintains the existing requirement to have a keystore. Also, fixed a small issue where the HandshakeWaitingHandlerTests called handshake more than once. Closes elastic/elasticsearch#622 Original commit: elastic/x-pack-elasticsearch@67b6121b04
This commit is contained in:
parent
458daa2323
commit
f6b2950323
|
@ -6,7 +6,7 @@
|
|||
package org.elasticsearch.shield.authc.support.ldap;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
@ -19,7 +19,7 @@ import java.net.InetAddress;
|
|||
*/
|
||||
public abstract class AbstractLdapSslSocketFactory extends SocketFactory {
|
||||
|
||||
protected static SSLService sslService;
|
||||
protected static ClientSSLService clientSSLService;
|
||||
|
||||
private final SSLSocketFactory socketFactory;
|
||||
|
||||
|
@ -27,8 +27,8 @@ public abstract class AbstractLdapSslSocketFactory extends SocketFactory {
|
|||
* This should only be invoked once to establish a static instance that will be used for each constructor.
|
||||
*/
|
||||
@Inject
|
||||
public static void init(SSLService sslService) {
|
||||
AbstractLdapSslSocketFactory.sslService = sslService;
|
||||
public static void init(ClientSSLService sslService) {
|
||||
AbstractLdapSslSocketFactory.clientSSLService = sslService;
|
||||
}
|
||||
|
||||
public AbstractLdapSslSocketFactory(SSLSocketFactory sslSocketFactory) {
|
||||
|
@ -78,7 +78,7 @@ public abstract class AbstractLdapSslSocketFactory extends SocketFactory {
|
|||
* @param sslSocket
|
||||
*/
|
||||
protected void configureSSLSocket(SSLSocket sslSocket) {
|
||||
sslSocket.setEnabledProtocols(sslService.supportedProtocols());
|
||||
sslSocket.setEnabledCipherSuites(sslService.ciphers());
|
||||
sslSocket.setEnabledProtocols(clientSSLService.supportedProtocols());
|
||||
sslSocket.setEnabledCipherSuites(clientSSLService.ciphers());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ import javax.net.SocketFactory;
|
|||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* This factory is needed for JNDI configuration for LDAP connections with hostname verification. Each SSLSocket must
|
||||
|
@ -39,7 +37,7 @@ public class HostnameVerifyingLdapSslSocketFactory extends AbstractLdapSslSocket
|
|||
*/
|
||||
public static synchronized SocketFactory getDefault() {
|
||||
if (instance == null) {
|
||||
instance = new HostnameVerifyingLdapSslSocketFactory(sslService.getSSLSocketFactory());
|
||||
instance = new HostnameVerifyingLdapSslSocketFactory(clientSSLService.getSSLSocketFactory());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class LdapSslSocketFactory extends AbstractLdapSslSocketFactory {
|
|||
*/
|
||||
public static synchronized SocketFactory getDefault() {
|
||||
if (instance == null) {
|
||||
instance = new LdapSslSocketFactory(sslService.getSSLSocketFactory());
|
||||
instance = new LdapSslSocketFactory(clientSSLService.getSSLSocketFactory());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.elasticsearch.common.cache.CacheBuilder;
|
||||
import org.elasticsearch.common.cache.CacheLoader;
|
||||
import org.elasticsearch.common.cache.LoadingCache;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.primitives.Ints;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import java.io.FileInputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This service houses the private key and trust managers needed for SSL/TLS negotiation. It is the central place to
|
||||
* get SSLEngines and SocketFactories.
|
||||
*/
|
||||
public abstract class AbstractSSLService extends AbstractComponent {
|
||||
|
||||
static final String[] DEFAULT_CIPHERS = new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" };
|
||||
static final String[] DEFAULT_SUPPORTED_PROTOCOLS = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
|
||||
static final TimeValue DEFAULT_SESSION_CACHE_TIMEOUT = TimeValue.timeValueHours(24);
|
||||
static final int DEFAULT_SESSION_CACHE_SIZE = 1000;
|
||||
static final String DEFAULT_PROTOCOL = "TLS";
|
||||
|
||||
protected LoadingCache<SSLSettings, SSLContext> sslContexts = CacheBuilder.newBuilder().build(new SSLContextCacheLoader());
|
||||
|
||||
public AbstractSSLService(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A SSLSocketFactory (for client-side SSL handshaking)
|
||||
*/
|
||||
public SSLSocketFactory getSSLSocketFactory() {
|
||||
return sslContext(ImmutableSettings.EMPTY).getSocketFactory();
|
||||
}
|
||||
|
||||
public String[] supportedProtocols() {
|
||||
return componentSettings.getAsArray("supported_protocols", DEFAULT_SUPPORTED_PROTOCOLS);
|
||||
}
|
||||
|
||||
public String[] ciphers() {
|
||||
return componentSettings.getAsArray("ciphers", DEFAULT_CIPHERS);
|
||||
}
|
||||
|
||||
public SSLEngine createSSLEngine() {
|
||||
return createSSLEngine(ImmutableSettings.EMPTY);
|
||||
}
|
||||
|
||||
public SSLEngine createSSLEngine(Settings settings) {
|
||||
return createSSLEngine(settings, null, -1);
|
||||
}
|
||||
|
||||
public SSLEngine createSSLEngine(Settings settings, String host, int port) {
|
||||
String[] ciphers = settings.getAsArray("ciphers", ciphers());
|
||||
String[] supportedProtocols = settings.getAsArray("supported_protocols", supportedProtocols());
|
||||
return createSSLEngine(sslContext(settings), ciphers, supportedProtocols, host, port);
|
||||
}
|
||||
|
||||
public SSLContext sslContext() {
|
||||
return sslContext(ImmutableSettings.EMPTY);
|
||||
}
|
||||
|
||||
protected SSLContext sslContext(Settings settings) {
|
||||
SSLSettings sslSettings = sslSettings(settings);
|
||||
try {
|
||||
return sslContexts.getUnchecked(sslSettings);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
// Unwrap ElasticsearchSSLException
|
||||
if (e.getCause() instanceof ElasticsearchSSLException) {
|
||||
throw (ElasticsearchSSLException) e.getCause();
|
||||
} else {
|
||||
throw new ElasticsearchSSLException("failed to load SSLContext", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract SSLSettings sslSettings(Settings customSettings);
|
||||
|
||||
SSLEngine createSSLEngine(SSLContext sslContext, String[] ciphers, String[] supportedProtocols, String host, int port) {
|
||||
SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
|
||||
try {
|
||||
sslEngine.setEnabledCipherSuites(ciphers);
|
||||
} catch (Throwable t) {
|
||||
throw new ElasticsearchSSLException("failed loading cipher suites [" + Arrays.asList(ciphers) + "]", t);
|
||||
}
|
||||
|
||||
try {
|
||||
sslEngine.setEnabledProtocols(supportedProtocols);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ElasticsearchSSLException("failed setting supported protocols [" + Arrays.asList(supportedProtocols) + "]", e);
|
||||
}
|
||||
return sslEngine;
|
||||
}
|
||||
|
||||
private class SSLContextCacheLoader extends CacheLoader<SSLSettings, SSLContext> {
|
||||
|
||||
@Override
|
||||
public SSLContext load(SSLSettings sslSettings) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("using keystore[{}], key_algorithm[{}], truststore[{}], truststore_algorithm[{}], tls_protocol[{}], session_cache_size[{}], session_cache_timeout[{}]",
|
||||
sslSettings.keyStorePath, sslSettings.keyStoreAlgorithm, sslSettings.trustStorePath, sslSettings.trustStoreAlgorithm, sslSettings.sslProtocol, sslSettings.sessionCacheSize, sslSettings.sessionCacheTimeout);
|
||||
}
|
||||
|
||||
TrustManager[] trustManagers = trustManagers(sslSettings.trustStorePath, sslSettings.trustStorePassword, sslSettings.trustStoreAlgorithm);
|
||||
KeyManager[] keyManagers = keyManagers(sslSettings.keyStorePath, sslSettings.keyStorePassword, sslSettings.keyStoreAlgorithm, sslSettings.keyPassword);
|
||||
return createSslContext(keyManagers, trustManagers, sslSettings.sslProtocol, sslSettings.sessionCacheSize, sslSettings.sessionCacheTimeout);
|
||||
}
|
||||
|
||||
|
||||
private KeyManager[] keyManagers(String keyStore, String keyStorePassword, String keyStoreAlgorithm, String keyPassword) {
|
||||
if (keyStore == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Load KeyStore
|
||||
KeyStore ks = readKeystore(keyStore, keyStorePassword);
|
||||
|
||||
// Initialize KeyManagerFactory
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyStoreAlgorithm);
|
||||
kmf.init(ks, keyPassword.toCharArray());
|
||||
return kmf.getKeyManagers();
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize a KeyManagerFactory", e);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLContext createSslContext(KeyManager[] keyManagers, TrustManager[] trustManagers, String sslProtocol, int sessionCacheSize, TimeValue sessionCacheTimeout) {
|
||||
// Initialize sslContext
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance(sslProtocol);
|
||||
sslContext.init(keyManagers, trustManagers, null);
|
||||
sslContext.getServerSessionContext().setSessionCacheSize(sessionCacheSize);
|
||||
sslContext.getServerSessionContext().setSessionTimeout(Ints.checkedCast(sessionCacheTimeout.seconds()));
|
||||
return sslContext;
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize the SSLContext", e);
|
||||
}
|
||||
}
|
||||
|
||||
private TrustManager[] trustManagers(String trustStorePath, String trustStorePassword, String trustStoreAlgorithm) {
|
||||
try {
|
||||
// Load TrustStore
|
||||
KeyStore ks = null;
|
||||
if (trustStorePath != null) {
|
||||
ks = readKeystore(trustStorePath, trustStorePassword);
|
||||
}
|
||||
|
||||
// Initialize a trust manager factory with the trusted store
|
||||
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm);
|
||||
trustFactory.init(ks);
|
||||
return trustFactory.getTrustManagers();
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize a TrustManagerFactory", e);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore readKeystore(String path, String password) throws Exception {
|
||||
try (FileInputStream in = new FileInputStream(path)) {
|
||||
// Load TrustStore
|
||||
KeyStore ks = KeyStore.getInstance("jks");
|
||||
assert password != null;
|
||||
ks.load(in, password.toCharArray());
|
||||
return ks;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SSLSettings {
|
||||
|
||||
private static final ESLogger logger = Loggers.getLogger(SSLSettings.class);
|
||||
|
||||
String keyStorePath;
|
||||
String keyStorePassword;
|
||||
String keyStoreAlgorithm;
|
||||
String keyPassword;
|
||||
String trustStorePath;
|
||||
String trustStorePassword;
|
||||
String trustStoreAlgorithm;
|
||||
String sslProtocol;
|
||||
int sessionCacheSize;
|
||||
TimeValue sessionCacheTimeout;
|
||||
|
||||
SSLSettings(Settings settings, Settings componentSettings) {
|
||||
keyStorePath = settings.get("keystore.path", componentSettings.get("keystore.path", System.getProperty("javax.net.ssl.keyStore")));
|
||||
keyStorePassword = settings.get("keystore.password", componentSettings.get("keystore.password", System.getProperty("javax.net.ssl.keyStorePassword")));
|
||||
keyStoreAlgorithm = settings.get("keystore.algorithm", componentSettings.get("keystore.algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm())));
|
||||
keyPassword = settings.get("keystore.key_password", componentSettings.get("keystore.key_password", keyStorePassword));
|
||||
|
||||
// Truststore settings
|
||||
trustStorePath = settings.get("truststore.path", componentSettings.get("truststore.path", System.getProperty("javax.net.ssl.trustStore")));
|
||||
trustStorePassword = settings.get("truststore.password", componentSettings.get("truststore.password", System.getProperty("javax.net.ssl.trustStorePassword")));
|
||||
trustStoreAlgorithm = settings.get("truststore.algorithm", componentSettings.get("truststore.algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())));
|
||||
|
||||
sslProtocol = settings.get("protocol", componentSettings.get("protocol", DEFAULT_PROTOCOL));
|
||||
sessionCacheSize = settings.getAsInt("session.cache_size", componentSettings.getAsInt("session.cache_size", DEFAULT_SESSION_CACHE_SIZE));
|
||||
sessionCacheTimeout = settings.getAsTime("session.cache_timeout", componentSettings.getAsTime("session.cache_timeout", DEFAULT_SESSION_CACHE_TIMEOUT));
|
||||
|
||||
if (trustStorePath == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("no truststore defined. using keystore [{}] as truststore", keyStorePath);
|
||||
}
|
||||
trustStorePath = keyStorePath;
|
||||
trustStorePassword = keyStorePassword;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SSLSettings that = (SSLSettings) o;
|
||||
|
||||
if (keyStorePath != null ? !keyStorePath.equals(that.keyStorePath) : that.keyStorePath != null) {
|
||||
return false;
|
||||
}
|
||||
if (sslProtocol != null ? !sslProtocol.equals(that.sslProtocol) : that.sslProtocol != null) {
|
||||
return false;
|
||||
}
|
||||
if (trustStorePath != null ? !trustStorePath.equals(that.trustStorePath) : that.trustStorePath != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = keyStorePath != null ? keyStorePath.hashCode() : 0;
|
||||
result = 31 * result + (trustStorePath != null ? trustStorePath.hashCode() : 0);
|
||||
result = 31 * result + (sslProtocol != null ? sslProtocol.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.ShieldSettingsException;
|
||||
|
||||
public class ClientSSLService extends AbstractSSLService {
|
||||
|
||||
@Inject
|
||||
public ClientSSLService(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLSettings sslSettings(Settings customSettings) {
|
||||
SSLSettings sslSettings = new SSLSettings(customSettings, componentSettings);
|
||||
|
||||
if (sslSettings.keyStorePath != null) {
|
||||
if (sslSettings.keyStorePassword == null) {
|
||||
throw new ShieldSettingsException("no keystore password configured");
|
||||
}
|
||||
}
|
||||
|
||||
if (sslSettings.trustStorePath != null) {
|
||||
if (sslSettings.trustStorePassword == null) {
|
||||
throw new ShieldSettingsException("no truststore password configured");
|
||||
}
|
||||
}
|
||||
|
||||
return sslSettings;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.elasticsearch.common.inject.util.Providers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.support.AbstractShieldModule;
|
||||
|
||||
|
@ -19,6 +20,11 @@ public class SSLModule extends AbstractShieldModule {
|
|||
|
||||
@Override
|
||||
protected void configure(boolean clientMode) {
|
||||
bind(SSLService.class).asEagerSingleton();
|
||||
bind(ClientSSLService.class).asEagerSingleton();
|
||||
if (!clientMode) {
|
||||
bind(ServerSSLService.class).asEagerSingleton();
|
||||
} else {
|
||||
bind(ServerSSLService.class).toProvider(Providers.<ServerSSLService>of(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.primitives.Ints;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||
import org.elasticsearch.shield.ShieldSettingsException;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import java.io.FileInputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This service houses the private key and trust managers needed for SSL/TLS negotiation. It is the central place to
|
||||
* get SSLEngines and SocketFactories.
|
||||
*/
|
||||
public class SSLService extends AbstractComponent {
|
||||
|
||||
static final String[] DEFAULT_CIPHERS = new String[]{ "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" };
|
||||
static final String[] DEFAULT_SUPPORTED_PROTOCOLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"};
|
||||
static final TimeValue DEFAULT_SESSION_CACHE_TIMEOUT = TimeValue.timeValueHours(24);
|
||||
|
||||
private Map<String, SSLContext> sslContexts = ConcurrentCollections.newConcurrentMap();
|
||||
|
||||
@Inject
|
||||
public SSLService(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An SSLSocketFactory (for client-side SSL handshaking)
|
||||
*/
|
||||
public SSLSocketFactory getSSLSocketFactory() {
|
||||
return getSslContext(ImmutableSettings.EMPTY).getSocketFactory();
|
||||
}
|
||||
|
||||
public String[] supportedProtocols() {
|
||||
return componentSettings.getAsArray("supported_protocols", DEFAULT_SUPPORTED_PROTOCOLS);
|
||||
}
|
||||
|
||||
public String[] ciphers() {
|
||||
return componentSettings.getAsArray("ciphers", DEFAULT_CIPHERS);
|
||||
}
|
||||
|
||||
public SSLEngine createSSLEngine() {
|
||||
return createSSLEngine(ImmutableSettings.EMPTY);
|
||||
}
|
||||
|
||||
public SSLEngine createSSLEngine(Settings settings) {
|
||||
return createSSLEngine(settings, null, -1);
|
||||
}
|
||||
|
||||
public SSLEngine createSSLEngine(Settings settings, String host, int port) {
|
||||
String[] ciphers = settings.getAsArray("ciphers", ciphers());
|
||||
String[] supportedProtocols = settings.getAsArray("supported_protocols", supportedProtocols());
|
||||
return createSSLEngine(getSslContext(settings), ciphers, supportedProtocols, host, port);
|
||||
}
|
||||
|
||||
//TODO this needs to be removed and the logic folded into the other methods of this class or we create a ClientSSLService class
|
||||
public SSLEngine createClientSSLEngine() {
|
||||
return createClientSSLEngine(null, -1);
|
||||
}
|
||||
|
||||
//TODO this needs to be removed and the logic folded into the other methods of this class or we create a ClientSSLService class
|
||||
public SSLEngine createClientSSLEngine(String host, int port) {
|
||||
SSLContext sslContext = getClientSSLContext();
|
||||
return createSSLEngine(sslContext, ciphers(), supportedProtocols(), host, port);
|
||||
}
|
||||
|
||||
//TODO remove this when createClientSSLEngine is removed. Used for tests
|
||||
SSLContext getClientSSLContext() {
|
||||
String keyStorePath = componentSettings.get("keystore.path", System.getProperty("javax.net.ssl.keyStore"));
|
||||
String keyStorePassword = componentSettings.get("keystore.password", System.getProperty("javax.net.ssl.keyStorePassword"));
|
||||
String keyStoreAlgorithm = componentSettings.get("keystore.algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()));
|
||||
String keyPassword = componentSettings.get("keystore.key_password", keyStorePassword);
|
||||
|
||||
String trustStorePath = componentSettings.get("truststore.path", System.getProperty("javax.net.ssl.trustStore"));
|
||||
String trustStorePassword = componentSettings.get("truststore.password", System.getProperty("javax.net.ssl.trustStorePassword"));
|
||||
String trustStoreAlgorithm = componentSettings.get("truststore.algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()));
|
||||
|
||||
if (trustStorePath == null) {
|
||||
//the keystore will also be the truststore
|
||||
trustStorePath = keyStorePath;
|
||||
trustStorePassword = keyStorePassword;
|
||||
}
|
||||
|
||||
//protocols supported: https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
|
||||
String sslProtocol = componentSettings.get("protocol", "TLS");
|
||||
|
||||
// no need for a complex key, same path + protocol define about reusability of a SSLContext
|
||||
// also no need for pwd verification. If it worked before, it will work again
|
||||
String key = keyStorePath + trustStorePath + sslProtocol;
|
||||
SSLContext sslContext = sslContexts.get(key);
|
||||
if (sslContext == null) {
|
||||
logger.debug("using keystore[{}], key_algorithm[{}], truststore[{}], truststore_algorithm[{}], tls_protocol[{}]",
|
||||
keyStorePath, keyStoreAlgorithm, trustStorePath, trustStoreAlgorithm, sslProtocol);
|
||||
|
||||
TrustManagerFactory trustFactory;
|
||||
try {
|
||||
trustFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm);
|
||||
if (trustStorePath != null) {
|
||||
trustFactory.init(readKeystore(trustStorePath, trustStorePassword));
|
||||
} else {
|
||||
trustFactory.init((KeyStore) null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize a TrustManagerFactory", e);
|
||||
}
|
||||
|
||||
KeyManager[] keyManagers = null;
|
||||
if (keyStorePath != null) {
|
||||
if (keyStorePassword == null) {
|
||||
throw new ShieldSettingsException("no keystore password configured");
|
||||
}
|
||||
keyManagers = createKeyManagerFactory(keyStorePath, keyStorePassword, keyStoreAlgorithm, keyPassword).getKeyManagers();
|
||||
}
|
||||
|
||||
try {
|
||||
sslContext = SSLContext.getInstance(sslProtocol);
|
||||
sslContext.init(keyManagers, trustFactory.getTrustManagers(), null);
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize the SSLContext", e);
|
||||
}
|
||||
sslContexts.put(key, sslContext);
|
||||
} else {
|
||||
logger.trace("found keystore[{}], truststore[{}], tls_protocol[{}] in SSL context cache, reusing", keyStorePath, trustStorePath, sslProtocol);
|
||||
}
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
public SSLContext getSslContext() {
|
||||
return getSslContext(ImmutableSettings.EMPTY);
|
||||
}
|
||||
|
||||
private SSLContext getSslContext(Settings settings) {
|
||||
String keyStorePath = settings.get("keystore.path", componentSettings.get("keystore.path", System.getProperty("javax.net.ssl.keyStore")));
|
||||
String keyStorePassword = settings.get("keystore.password", componentSettings.get("keystore.password", System.getProperty("javax.net.ssl.keyStorePassword")));
|
||||
String keyStoreAlgorithm = settings.get("keystore.algorithm", componentSettings.get("keystore.algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm())));
|
||||
String keyPassword = settings.get("keystore.key_password", componentSettings.get("keystore.key_password", keyStorePassword));
|
||||
|
||||
String trustStorePath = settings.get("truststore.path", componentSettings.get("truststore.path", System.getProperty("javax.net.ssl.trustStore")));
|
||||
String trustStorePassword = settings.get("truststore.password", componentSettings.get("truststore.password", System.getProperty("javax.net.ssl.trustStorePassword")));
|
||||
String trustStoreAlgorithm = settings.get("truststore.algorithm", componentSettings.get("truststore.algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())));
|
||||
|
||||
if (trustStorePath == null) {
|
||||
//the keystore will also be the truststore
|
||||
trustStorePath = keyStorePath;
|
||||
trustStorePassword = keyStorePassword;
|
||||
}
|
||||
|
||||
if (keyStorePath == null) {
|
||||
throw new ShieldSettingsException("no keystore configured");
|
||||
}
|
||||
if (keyStorePassword == null) {
|
||||
throw new ShieldSettingsException("no keystore password configured");
|
||||
}
|
||||
|
||||
//protocols supported: https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
|
||||
String sslProtocol = componentSettings.get("protocol", "TLS");
|
||||
|
||||
// no need for a complex key, same path + protocol define about reusability of a SSLContext
|
||||
// also no need for pwd verification. If it worked before, it will work again
|
||||
String key = keyStorePath + trustStorePath + sslProtocol;
|
||||
SSLContext sslContext = sslContexts.get(key);
|
||||
if (sslContext == null) {
|
||||
int sessionCacheSize = settings.getAsInt("session.cache_size", componentSettings.getAsInt("session.cache_size", 1000));
|
||||
TimeValue sessionCacheTimeout = settings.getAsTime("session.cache_timeout", componentSettings.getAsTime("session.cache_timeout", DEFAULT_SESSION_CACHE_TIMEOUT));
|
||||
logger.debug("using keystore[{}], key_algorithm[{}], truststore[{}], truststore_algorithm[{}], tls_protocol[{}], session_cache_size[{}], session_cache_timeout[{}]",
|
||||
keyStorePath, keyStoreAlgorithm, trustStorePath, trustStoreAlgorithm, sslProtocol, sessionCacheSize, sessionCacheTimeout);
|
||||
|
||||
TrustManagerFactory trustFactory = getTrustFactory(trustStorePath, trustStorePassword, trustStoreAlgorithm);
|
||||
KeyManagerFactory keyManagerFactory = createKeyManagerFactory(keyStorePath, keyStorePassword, keyStoreAlgorithm, keyPassword);
|
||||
sslContext = createSslContext(keyManagerFactory, trustFactory, sslProtocol, sessionCacheSize, sessionCacheTimeout);
|
||||
sslContexts.put(key, sslContext);
|
||||
} else {
|
||||
logger.trace("found keystore[{}], truststore[{}], tls_protocol[{}] in SSL context cache, reusing", keyStorePath, trustStorePath, sslProtocol);
|
||||
}
|
||||
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
private SSLEngine createSSLEngine(SSLContext sslContext, String[] ciphers, String[] supportedProtocols, String host, int port) {
|
||||
SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
|
||||
try {
|
||||
sslEngine.setEnabledCipherSuites(ciphers);
|
||||
} catch (Throwable t) {
|
||||
throw new ElasticsearchSSLException("failed loading cipher suites [" + Arrays.asList(ciphers) + "]", t);
|
||||
}
|
||||
|
||||
try {
|
||||
sslEngine.setEnabledProtocols(supportedProtocols);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ElasticsearchSSLException("failed setting supported protocols [" + Arrays.asList(supportedProtocols) + "]", e);
|
||||
}
|
||||
return sslEngine;
|
||||
}
|
||||
|
||||
private KeyManagerFactory createKeyManagerFactory(String keyStore, String keyStorePassword, String keyStoreAlgorithm, String keyPassword) {
|
||||
try {
|
||||
// Load KeyStore
|
||||
KeyStore ks = readKeystore(keyStore, keyStorePassword);
|
||||
|
||||
// Initialize KeyManagerFactory
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyStoreAlgorithm);
|
||||
kmf.init(ks, keyPassword.toCharArray());
|
||||
return kmf;
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize a KeyManagerFactory", e);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLContext createSslContext(KeyManagerFactory keyManagerFactory, TrustManagerFactory trustFactory, String sslProtocol, int sessionCacheSize, TimeValue sessionCacheTimeout) {
|
||||
// Initialize sslContext
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance(sslProtocol);
|
||||
sslContext.init(keyManagerFactory.getKeyManagers(), trustFactory.getTrustManagers(), null);
|
||||
sslContext.getServerSessionContext().setSessionCacheSize(sessionCacheSize);
|
||||
sslContext.getServerSessionContext().setSessionTimeout(Ints.checkedCast(sessionCacheTimeout.seconds()));
|
||||
return sslContext;
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize the SSLContext", e);
|
||||
}
|
||||
}
|
||||
|
||||
private TrustManagerFactory getTrustFactory(String trustStore, String trustStorePassword, String trustStoreAlgorithm) {
|
||||
try {
|
||||
// Load TrustStore
|
||||
KeyStore ks = readKeystore(trustStore, trustStorePassword);
|
||||
|
||||
// Initialize a trust manager factory with the trusted store
|
||||
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm);
|
||||
trustFactory.init(ks);
|
||||
return trustFactory;
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchSSLException("failed to initialize a TrustManagerFactory", e);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore readKeystore(String path, String password) throws Exception {
|
||||
try (FileInputStream in = new FileInputStream(path)) {
|
||||
// Load TrustStore
|
||||
KeyStore ks = KeyStore.getInstance("jks");
|
||||
ks.load(in, password == null ? null : password.toCharArray());
|
||||
return ks;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.ShieldSettingsException;
|
||||
|
||||
public class ServerSSLService extends AbstractSSLService {
|
||||
|
||||
@Inject
|
||||
public ServerSSLService(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLSettings sslSettings(Settings customSettings) {
|
||||
SSLSettings sslSettings = new SSLSettings(customSettings, componentSettings);
|
||||
|
||||
if (sslSettings.keyStorePath == null) {
|
||||
throw new ShieldSettingsException("no keystore configured");
|
||||
}
|
||||
if (sslSettings.keyStorePassword == null) {
|
||||
throw new ShieldSettingsException("no keystore password configured");
|
||||
}
|
||||
|
||||
assert sslSettings.trustStorePath != null;
|
||||
if (sslSettings.trustStorePassword == null) {
|
||||
throw new ShieldSettingsException("no truststore password configured");
|
||||
}
|
||||
return sslSettings;
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import org.elasticsearch.common.network.NetworkService;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.BigArrays;
|
||||
import org.elasticsearch.http.netty.NettyHttpServerTransport;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ServerSSLService;
|
||||
import org.elasticsearch.shield.transport.filter.IPFilter;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
@ -24,12 +24,12 @@ import javax.net.ssl.SSLEngine;
|
|||
public class ShieldNettyHttpServerTransport extends NettyHttpServerTransport {
|
||||
|
||||
private final IPFilter ipFilter;
|
||||
private final SSLService sslService;
|
||||
private final ServerSSLService sslService;
|
||||
private final boolean ssl;
|
||||
|
||||
@Inject
|
||||
public ShieldNettyHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays,
|
||||
IPFilter ipFilter, SSLService sslService) {
|
||||
IPFilter ipFilter, ServerSSLService sslService) {
|
||||
super(settings, networkService, bigArrays);
|
||||
this.ipFilter = ipFilter;
|
||||
this.ssl = settings.getAsBoolean("shield.http.ssl", false);
|
||||
|
|
|
@ -11,9 +11,11 @@ import org.elasticsearch.common.inject.internal.Nullable;
|
|||
import org.elasticsearch.common.netty.channel.*;
|
||||
import org.elasticsearch.common.netty.handler.ssl.SslHandler;
|
||||
import org.elasticsearch.common.network.NetworkService;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.BigArrays;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.ssl.ServerSSLService;
|
||||
import org.elasticsearch.shield.transport.filter.IPFilter;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.netty.NettyTransport;
|
||||
|
@ -30,17 +32,19 @@ public class ShieldNettyTransport extends NettyTransport {
|
|||
public static final String HOSTNAME_VERIFICATION_SETTING = "shield.ssl.hostname_verification";
|
||||
public static final String HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING = "shield.ssl.hostname_verification.resolve_name";
|
||||
|
||||
private final SSLService sslService;
|
||||
private final ServerSSLService serverSslService;
|
||||
private final ClientSSLService clientSSLService;
|
||||
private final @Nullable IPFilter authenticator;
|
||||
private final boolean ssl;
|
||||
|
||||
@Inject
|
||||
public ShieldNettyTransport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays, Version version,
|
||||
@Nullable IPFilter authenticator, SSLService sslService) {
|
||||
@Nullable IPFilter authenticator, @Nullable ServerSSLService serverSSLService, ClientSSLService clientSSLService) {
|
||||
super(settings, threadPool, networkService, bigArrays, version);
|
||||
this.authenticator = authenticator;
|
||||
this.ssl = settings.getAsBoolean("shield.transport.ssl", false);
|
||||
this.sslService = sslService;
|
||||
this.serverSslService = serverSSLService;
|
||||
this.clientSSLService = clientSSLService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,9 +73,9 @@ public class ShieldNettyTransport extends NettyTransport {
|
|||
if (profileSsl) {
|
||||
SSLEngine serverEngine;
|
||||
if (profileSettings.get("shield.truststore.path") != null) {
|
||||
serverEngine = sslService.createSSLEngine(profileSettings.getByPrefix("shield."));
|
||||
serverEngine = serverSslService.createSSLEngine(profileSettings.getByPrefix("shield."));
|
||||
} else {
|
||||
serverEngine = sslService.createSSLEngine();
|
||||
serverEngine = serverSslService.createSSLEngine();
|
||||
}
|
||||
serverEngine.setUseClientMode(false);
|
||||
serverEngine.setNeedClientAuth(profileSettings.getAsBoolean("shield.ssl.client.auth", settings.getAsBoolean("shield.transport.ssl.client.auth", true)));
|
||||
|
@ -113,12 +117,16 @@ public class ShieldNettyTransport extends NettyTransport {
|
|||
SSLEngine sslEngine;
|
||||
if (settings.getAsBoolean(HOSTNAME_VERIFICATION_SETTING, true)) {
|
||||
InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getValue();
|
||||
sslEngine = sslService.createClientSSLEngine(getHostname(inetSocketAddress), inetSocketAddress.getPort());
|
||||
sslEngine = clientSSLService.createSSLEngine(ImmutableSettings.EMPTY, getHostname(inetSocketAddress), inetSocketAddress.getPort());
|
||||
|
||||
// By default, a SSLEngine will not perform hostname verification. In order to perform hostname verification
|
||||
// we need to specify a EndpointIdentificationAlgorithm. We use the HTTPS algorithm to prevent against
|
||||
// man in the middle attacks for transport connections
|
||||
SSLParameters parameters = new SSLParameters();
|
||||
parameters.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
sslEngine.setSSLParameters(parameters);
|
||||
} else {
|
||||
sslEngine = sslService.createClientSSLEngine();
|
||||
sslEngine = clientSSLService.createSSLEngine();
|
||||
}
|
||||
|
||||
sslEngine.setUseClientMode(true);
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.elasticsearch.shield.authc.ldap.LdapConnectionFactory;
|
|||
import org.elasticsearch.shield.authc.ldap.LdapException;
|
||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||
import org.elasticsearch.shield.authc.support.ldap.*;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -44,7 +44,7 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
|
|||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||
* verification tests since a re-established connection does not perform hostname verification.
|
||||
*/
|
||||
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
|
||||
AbstractLdapSslSocketFactory.init(new ClientSSLService(ImmutableSettings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build()));
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.elasticsearch.shield.authc.support.ldap.AbstractLdapSslSocketFactory;
|
|||
import org.elasticsearch.shield.authc.support.ldap.ConnectionFactory;
|
||||
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
|
||||
import org.elasticsearch.shield.authc.support.ldap.SearchScope;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.support.NoOpLogger;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
|
@ -47,7 +47,7 @@ public class ActiveDirectoryGroupsResolverTests extends ElasticsearchTestCase {
|
|||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||
* verification tests since a re-established connection does not perform hostname verification.
|
||||
*/
|
||||
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
|
||||
AbstractLdapSslSocketFactory.init(new ClientSSLService(ImmutableSettings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build()));
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.shield.authc.RealmConfig;
|
||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||
import org.elasticsearch.shield.authc.support.ldap.*;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.junit.After;
|
||||
|
@ -39,7 +39,7 @@ public class OpenLdapTests extends ElasticsearchTestCase {
|
|||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||
* verification tests since a re-established connection does not perform hostname verification.
|
||||
*/
|
||||
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
|
||||
AbstractLdapSslSocketFactory.init(new ClientSSLService(ImmutableSettings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build()));
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.elasticsearch.shield.authc.support.ldap.AbstractLdapSslSocketFactory;
|
|||
import org.elasticsearch.shield.authc.support.ldap.ConnectionFactory;
|
||||
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
|
||||
import org.elasticsearch.shield.authc.support.ldap.SearchScope;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.support.NoOpLogger;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
|
@ -46,7 +46,7 @@ public class SearchGroupsResolverTest extends ElasticsearchTestCase {
|
|||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||
* verification tests since a re-established connection does not perform hostname verification.
|
||||
*/
|
||||
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
|
||||
AbstractLdapSslSocketFactory.init(new ClientSSLService(ImmutableSettings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build()));
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.elasticsearch.shield.authc.active_directory.ActiveDirectoryFactoryTes
|
|||
import org.elasticsearch.shield.authc.support.ldap.AbstractLdapSslSocketFactory;
|
||||
import org.elasticsearch.shield.authc.support.ldap.ConnectionFactory;
|
||||
import org.elasticsearch.shield.authc.support.ldap.LdapSslSocketFactory;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.support.NoOpLogger;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
|
@ -31,21 +31,21 @@ import java.util.List;
|
|||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@Network
|
||||
public class UserAttributeGroupsResolverTest extends ElasticsearchTestCase {
|
||||
public class UserAttributeGroupsResolverTests extends ElasticsearchTestCase {
|
||||
public static final String BRUCE_BANNER_DN = "cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||
private InitialDirContext ldapContext;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Path keystore = Paths.get(UserAttributeGroupsResolverTest.class.getResource("../support/ldap/ldaptrust.jks").toURI()).toAbsolutePath();
|
||||
Path keystore = Paths.get(UserAttributeGroupsResolverTests.class.getResource("../support/ldap/ldaptrust.jks").toURI()).toAbsolutePath();
|
||||
|
||||
/*
|
||||
* Prior to each test we reinitialize the socket factory with a new SSLService so that we get a new SSLContext.
|
||||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||
* verification tests since a re-established connection does not perform hostname verification.
|
||||
*/
|
||||
AbstractLdapSslSocketFactory.init(new SSLService(ImmutableSettings.builder()
|
||||
AbstractLdapSslSocketFactory.init(new ClientSSLService(ImmutableSettings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build()));
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.shield.ShieldSettingsException;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class ClientSSLServiceTests extends ElasticsearchTestCase {
|
||||
|
||||
Path testclientStore;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
testclientStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks").toURI());
|
||||
}
|
||||
|
||||
@Test(expected = ElasticsearchSSLException.class)
|
||||
public void testThatInvalidProtocolThrowsException() throws Exception {
|
||||
new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.protocol", "non-existing")
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.put("shield.ssl.truststore.path", testclientStore)
|
||||
.put("shield.ssl.truststore.password", "testclient")
|
||||
.build()).createSSLEngine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCustomTruststoreCanBeSpecified() throws Exception {
|
||||
Path testnodeStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI());
|
||||
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build());
|
||||
|
||||
ImmutableSettings.Builder settingsBuilder = settingsBuilder()
|
||||
.put("truststore.path", testnodeStore)
|
||||
.put("truststore.password", "testnode");
|
||||
|
||||
SSLEngine sslEngineWithTruststore = sslService.createSSLEngine(settingsBuilder.build());
|
||||
assertThat(sslEngineWithTruststore, is(not(nullValue())));
|
||||
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngineWithTruststore, is(not(sameInstance(sslEngine))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSslContextCachingWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build());
|
||||
|
||||
SSLContext sslContext = sslService.sslContext();
|
||||
SSLContext cachedSslContext = sslService.sslContext();
|
||||
|
||||
assertThat(sslContext, is(sameInstance(cachedSslContext)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
|
||||
Path differentPasswordsStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks").toURI());
|
||||
new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", differentPasswordsStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.put("shield.ssl.keystore.key_password", "testnode1")
|
||||
.build()).createSSLEngine();
|
||||
}
|
||||
|
||||
@Test(expected = ElasticsearchSSLException.class)
|
||||
public void testIncorrectKeyPasswordThrowsException() throws Exception {
|
||||
Path differentPasswordsStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks").toURI());
|
||||
new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", differentPasswordsStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build()).createSSLEngine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSSLv3IsNotEnabled() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build());
|
||||
SSLEngine engine = sslService.createSSLEngine();
|
||||
assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSSLSessionCacheHasDefaultLimits() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build());
|
||||
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
|
||||
assertThat(context.getSessionCacheSize(), equalTo(1000));
|
||||
assertThat(context.getSessionTimeout(), equalTo((int) TimeValue.timeValueHours(24).seconds()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingSSLSessionCacheLimitsWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.put("shield.ssl.session.cache_size", "300")
|
||||
.put("shield.ssl.session.cache_timeout", "600s")
|
||||
.build());
|
||||
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
|
||||
assertThat(context.getSessionCacheSize(), equalTo(300));
|
||||
assertThat(context.getSessionTimeout(), equalTo(600));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateClientSSLEngineWithoutAnySettingsWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(ImmutableSettings.EMPTY);
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateSSLEngineWithOnlyTruststoreWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.truststore.path", testclientStore)
|
||||
.put("shield.ssl.truststore.password", "testclient")
|
||||
.build());
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateSSLEngineWithOnlyKeystoreWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build());
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Network
|
||||
public void testThatSSLContextWithoutSettingsWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(ImmutableSettings.EMPTY);
|
||||
SSLContext sslContext = sslService.sslContext();
|
||||
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
|
||||
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
|
||||
// This will result in a SSLHandshakeException if the SSLContext does not trust the CA, but the default
|
||||
// truststore trusts all common public CAs so the handshake will succeed
|
||||
client.execute(new HttpGet("https://www.elasticsearch.com/"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Network
|
||||
public void testThatSSLContextWithKeystoreDoesNotTrustAllPublicCAs() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build());
|
||||
SSLContext sslContext = sslService.sslContext();
|
||||
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
|
||||
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
|
||||
// This will result in a SSLHandshakeException because the truststore is the testnodestore, which doesn't
|
||||
// trust any public CAs
|
||||
client.execute(new HttpGet("https://www.elasticsearch.com/"));
|
||||
fail("A SSLHandshakeException should have been thrown here");
|
||||
} catch (Exception e) {
|
||||
assertThat(e, instanceOf(SSLHandshakeException.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testThatTruststorePasswordIsRequired() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.truststore.path", testclientStore)
|
||||
.build());
|
||||
sslService.sslContext();
|
||||
}
|
||||
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testThatKeystorePasswordIsRequired() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.build());
|
||||
sslService.sslContext();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.shield.ssl.AbstractSSLService.SSLSettings;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class SSLSettingsTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testThatSSLSettingsWithEmptySettingsHaveCorrectDefaults() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, ImmutableSettings.EMPTY);
|
||||
assertThat(sslSettings.keyStorePath, is(nullValue()));
|
||||
assertThat(sslSettings.keyStorePassword, is(nullValue()));
|
||||
assertThat(sslSettings.keyPassword, is(nullValue()));
|
||||
assertThat(sslSettings.keyStoreAlgorithm, is(equalTo(KeyManagerFactory.getDefaultAlgorithm())));
|
||||
assertThat(sslSettings.sessionCacheSize, is(equalTo(AbstractSSLService.DEFAULT_SESSION_CACHE_SIZE)));
|
||||
assertThat(sslSettings.sessionCacheTimeout, is(equalTo(AbstractSSLService.DEFAULT_SESSION_CACHE_TIMEOUT)));
|
||||
assertThat(sslSettings.sslProtocol, is(equalTo(AbstractSSLService.DEFAULT_PROTOCOL)));
|
||||
assertThat(sslSettings.trustStoreAlgorithm, is(equalTo(TrustManagerFactory.getDefaultAlgorithm())));
|
||||
assertThat(sslSettings.trustStorePassword, is(nullValue()));
|
||||
assertThat(sslSettings.trustStorePath, is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatOnlyKeystoreInSettingsSetsTruststoreSettings() {
|
||||
Settings settings = settingsBuilder()
|
||||
.put("keystore.path", "path")
|
||||
.put("keystore.password", "password")
|
||||
.build();
|
||||
// Pass settings in as component settings
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settings);
|
||||
assertThat(sslSettings.keyStorePath, is(equalTo("path")));
|
||||
assertThat(sslSettings.keyStorePassword, is(equalTo("password")));
|
||||
assertThat(sslSettings.trustStorePath, is(equalTo(sslSettings.keyStorePath)));
|
||||
assertThat(sslSettings.trustStorePassword, is(equalTo(sslSettings.keyStorePassword)));
|
||||
|
||||
// Pass settings in as profile settings
|
||||
SSLSettings sslSettings1 = new SSLSettings(settings, ImmutableSettings.EMPTY);
|
||||
assertThat(sslSettings1.keyStorePath, is(equalTo("path")));
|
||||
assertThat(sslSettings1.keyStorePassword, is(equalTo("password")));
|
||||
assertThat(sslSettings1.trustStorePath, is(equalTo(sslSettings1.keyStorePath)));
|
||||
assertThat(sslSettings1.trustStorePassword, is(equalTo(sslSettings1.keyStorePassword)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatKeystorePasswordIsDefaultKeyPassword() {
|
||||
Settings settings = settingsBuilder()
|
||||
.put("keystore.password", "password")
|
||||
.build();
|
||||
// Pass settings in as component settings
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settings);
|
||||
assertThat(sslSettings.keyPassword, is(equalTo(sslSettings.keyStorePassword)));
|
||||
|
||||
// Pass settings in as profile settings
|
||||
SSLSettings sslSettings1 = new SSLSettings(settings, ImmutableSettings.EMPTY);
|
||||
assertThat(sslSettings1.keyPassword, is(equalTo(sslSettings1.keyStorePassword)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatKeyPasswordCanBeSet() {
|
||||
Settings settings = settingsBuilder()
|
||||
.put("keystore.password", "password")
|
||||
.put("keystore.key_password", "key")
|
||||
.build();
|
||||
// Pass settings in as component settings
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settings);
|
||||
assertThat(sslSettings.keyStorePassword, is(equalTo("password")));
|
||||
assertThat(sslSettings.keyPassword, is(equalTo("key")));
|
||||
|
||||
// Pass settings in as profile settings
|
||||
SSLSettings sslSettings1 = new SSLSettings(settings, ImmutableSettings.EMPTY);
|
||||
assertThat(sslSettings1.keyStorePassword, is(equalTo("password")));
|
||||
assertThat(sslSettings1.keyPassword, is(equalTo("key")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatProfileSettingsOverrideComponentSettings() {
|
||||
Settings profileSettings = settingsBuilder()
|
||||
.put("keystore.path", "path")
|
||||
.put("keystore.password", "password")
|
||||
.put("keystore.key_password", "key")
|
||||
.put("keystore.algorithm", "algo")
|
||||
.put("truststore.path", "trust path")
|
||||
.put("truststore.password", "password for trust")
|
||||
.put("truststore.algorithm", "trusted")
|
||||
.put("protocol", "ssl")
|
||||
.put("session.cache_size", "3")
|
||||
.put("session.cache_timeout", "10m")
|
||||
.build();
|
||||
|
||||
Settings componentSettings = settingsBuilder()
|
||||
.put("keystore.path", "comp path")
|
||||
.put("keystore.password", "comp password")
|
||||
.put("keystore.key_password", "comp key")
|
||||
.put("keystore.algorithm", "comp algo")
|
||||
.put("truststore.path", "comp trust path")
|
||||
.put("truststore.password", "comp password for trust")
|
||||
.put("truststore.algorithm", "comp trusted")
|
||||
.put("protocol", "tls")
|
||||
.put("session.cache_size", "7")
|
||||
.put("session.cache_timeout", "20m")
|
||||
.build();
|
||||
|
||||
SSLSettings sslSettings = new SSLSettings(profileSettings, componentSettings);
|
||||
assertThat(sslSettings.keyStorePath, is(equalTo("path")));
|
||||
assertThat(sslSettings.keyStorePassword, is(equalTo("password")));
|
||||
assertThat(sslSettings.keyPassword, is(equalTo("key")));
|
||||
assertThat(sslSettings.keyStoreAlgorithm, is(equalTo("algo")));
|
||||
assertThat(sslSettings.trustStorePath, is(equalTo("trust path")));
|
||||
assertThat(sslSettings.trustStorePassword, is(equalTo("password for trust")));
|
||||
assertThat(sslSettings.trustStoreAlgorithm, is(equalTo("trusted")));
|
||||
assertThat(sslSettings.sslProtocol, is(equalTo("ssl")));
|
||||
assertThat(sslSettings.sessionCacheSize, is(equalTo(3)));
|
||||
assertThat(sslSettings.sessionCacheTimeout, is(equalTo(TimeValue.parseTimeValue("10m", null))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatEmptySettingsAreEqual() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, ImmutableSettings.EMPTY);
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, ImmutableSettings.EMPTY);
|
||||
assertThat(sslSettings.equals(sslSettings1), is(equalTo(true)));
|
||||
assertThat(sslSettings1.equals(sslSettings), is(equalTo(true)));
|
||||
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
|
||||
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingsWithDifferentKeystoresAreNotEqual() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("keystore.path", "path").build());
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("keystore.path", "path1").build());
|
||||
assertThat(sslSettings.equals(sslSettings1), is(equalTo(false)));
|
||||
assertThat(sslSettings1.equals(sslSettings), is(equalTo(false)));
|
||||
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
|
||||
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingsWithDifferentProtocolsAreNotEqual() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("protocol", "ssl").build());
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("protocol", "tls").build());
|
||||
assertThat(sslSettings.equals(sslSettings1), is(equalTo(false)));
|
||||
assertThat(sslSettings1.equals(sslSettings), is(equalTo(false)));
|
||||
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
|
||||
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingsWithDifferentTruststoresAreNotEqual() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("truststore.path", "/trust").build());
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("truststore.path", "/truststore").build());
|
||||
assertThat(sslSettings.equals(sslSettings1), is(equalTo(false)));
|
||||
assertThat(sslSettings1.equals(sslSettings), is(equalTo(false)));
|
||||
assertThat(sslSettings.equals(sslSettings), is(equalTo(true)));
|
||||
assertThat(sslSettings1.equals(sslSettings1), is(equalTo(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatEmptySettingsHaveSameHashCode() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, ImmutableSettings.EMPTY);
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, ImmutableSettings.EMPTY);
|
||||
assertThat(sslSettings.hashCode(), is(equalTo(sslSettings1.hashCode())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingsWithDifferentKeystoresHaveDifferentHashCode() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("keystore.path", "path").build());
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("keystore.path", "path1").build());
|
||||
assertThat(sslSettings.hashCode(), is(not(equalTo(sslSettings1.hashCode()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingsWithDifferentProtocolsHaveDifferentHashCode() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("protocol", "ssl").build());
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("protocol", "tls").build());
|
||||
assertThat(sslSettings.hashCode(), is(not(equalTo(sslSettings1.hashCode()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingsWithDifferentTruststoresHaveDifferentHashCode() {
|
||||
SSLSettings sslSettings = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("truststore.path", "/trust").build());
|
||||
SSLSettings sslSettings1 = new SSLSettings(ImmutableSettings.EMPTY, settingsBuilder()
|
||||
.put("truststore.path", "/truststore").build());
|
||||
assertThat(sslSettings.hashCode(), is(not(equalTo(sslSettings1.hashCode()))));
|
||||
}
|
||||
}
|
|
@ -5,19 +5,15 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.shield.ShieldSettingsException;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -26,7 +22,7 @@ import java.util.Arrays;
|
|||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class SSLServiceTests extends ElasticsearchTestCase {
|
||||
public class ServerSSLServiceTests extends ElasticsearchTestCase {
|
||||
|
||||
Path testnodeStore;
|
||||
|
||||
|
@ -37,7 +33,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
|
||||
@Test(expected = ElasticsearchSSLException.class)
|
||||
public void testThatInvalidProtocolThrowsException() throws Exception {
|
||||
new SSLService(settingsBuilder()
|
||||
new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.protocol", "non-existing")
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
|
@ -50,7 +46,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
public void testThatCustomTruststoreCanBeSpecified() throws Exception {
|
||||
Path testClientStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks").toURI());
|
||||
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build());
|
||||
|
@ -68,13 +64,13 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
|
||||
@Test
|
||||
public void testThatSslContextCachingWorks() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build());
|
||||
|
||||
SSLContext sslContext = sslService.getSslContext();
|
||||
SSLContext cachedSslContext = sslService.getSslContext();
|
||||
SSLContext sslContext = sslService.sslContext();
|
||||
SSLContext cachedSslContext = sslService.sslContext();
|
||||
|
||||
assertThat(sslContext, is(sameInstance(cachedSslContext)));
|
||||
}
|
||||
|
@ -82,7 +78,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
@Test
|
||||
public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
|
||||
Path differentPasswordsStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks").toURI());
|
||||
new SSLService(settingsBuilder()
|
||||
new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", differentPasswordsStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.put("shield.ssl.keystore.key_password", "testnode1")
|
||||
|
@ -92,7 +88,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
@Test(expected = ElasticsearchSSLException.class)
|
||||
public void testIncorrectKeyPasswordThrowsException() throws Exception {
|
||||
Path differentPasswordsStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks").toURI());
|
||||
new SSLService(settingsBuilder()
|
||||
new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", differentPasswordsStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build()).createSSLEngine();
|
||||
|
@ -100,7 +96,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
|
||||
@Test
|
||||
public void testThatSSLv3IsNotEnabled() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build());
|
||||
|
@ -110,84 +106,59 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
|
||||
@Test
|
||||
public void testThatSSLSessionCacheHasDefaultLimits() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build());
|
||||
SSLSessionContext context = sslService.getSslContext().getServerSessionContext();
|
||||
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
|
||||
assertThat(context.getSessionCacheSize(), equalTo(1000));
|
||||
assertThat(context.getSessionTimeout(), equalTo((int) TimeValue.timeValueHours(24).seconds()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSettingSSLSessionCacheLimitsWorks() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.put("shield.ssl.session.cache_size", "300")
|
||||
.put("shield.ssl.session.cache_timeout", "600s")
|
||||
.build());
|
||||
SSLSessionContext context = sslService.getSslContext().getServerSessionContext();
|
||||
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
|
||||
assertThat(context.getSessionCacheSize(), equalTo(300));
|
||||
assertThat(context.getSessionTimeout(), equalTo(600));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateClientSSLEngineWithoutAnySettingsWorks() throws Exception {
|
||||
SSLService sslService = new SSLService(ImmutableSettings.EMPTY);
|
||||
SSLEngine sslEngine = sslService.createClientSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testThatCreateSSLEngineWithoutAnySettingsDoesNotWork() throws Exception {
|
||||
ServerSSLService sslService = new ServerSSLService(ImmutableSettings.EMPTY);
|
||||
sslService.createSSLEngine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateClientSSLEngineWithOnlyTruststoreWorks() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testThatCreateSSLEngineWithOnlyTruststoreDoesNotWork() throws Exception {
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.truststore.path", testnodeStore)
|
||||
.put("shield.ssl.truststore.password", "testnode")
|
||||
.build());
|
||||
SSLEngine sslEngine = sslService.createClientSSLEngine();
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateClientSSLEngineWithOnlyKeystoreWorks() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testThatTruststorePasswordIsRequired() throws Exception {
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.put("shield.ssl.truststore.path", testnodeStore)
|
||||
.build());
|
||||
SSLEngine sslEngine = sslService.createClientSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
sslService.sslContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Network
|
||||
public void testThatClientSSLContextWithoutSettingsWorks() throws Exception {
|
||||
SSLService sslService = new SSLService(ImmutableSettings.EMPTY);
|
||||
SSLContext sslContext = sslService.getClientSSLContext();
|
||||
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
|
||||
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
|
||||
// This will result in a SSLHandshakeException if the SSLContext does not trust the CA, but the default
|
||||
// truststore trusts all common public CAs so the handshake will succeed
|
||||
client.execute(new HttpGet("https://www.elasticsearch.com/"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Network
|
||||
public void testThatClientSSLContextWithKeystoreDoesNotTrustAllPublicCAs() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testThatKeystorePasswordIsRequired() throws Exception {
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build());
|
||||
SSLContext sslContext = sslService.getSslContext();
|
||||
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
|
||||
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
|
||||
// This will result in a SSLHandshakeException because the truststore is the testnodestore, which doesn't
|
||||
// trust any public CAs
|
||||
client.execute(new HttpGet("https://www.elasticsearch.com/"));
|
||||
fail("A SSLHandshakeException should have been thrown here");
|
||||
} catch (Exception e) {
|
||||
assertThat(e, instanceOf(SSLHandshakeException.class));
|
||||
}
|
||||
sslService.sslContext();
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ import org.elasticsearch.common.netty.channel.*;
|
|||
import org.elasticsearch.common.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||
import org.elasticsearch.common.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import org.elasticsearch.common.netty.handler.ssl.SslHandler;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ServerSSLService;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -64,12 +64,12 @@ public class HandshakeWaitingHandlerTests extends ElasticsearchTestCase {
|
|||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
SSLService sslService = new SSLService(settingsBuilder()
|
||||
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", Paths.get(HandshakeWaitingHandlerTests.class.getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI()))
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build());
|
||||
|
||||
sslContext = sslService.getSslContext();
|
||||
sslContext = sslService.sslContext();
|
||||
|
||||
serverBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory());
|
||||
serverBootstrap.setPipelineFactory(getServerFactory());
|
||||
|
@ -231,8 +231,10 @@ public class HandshakeWaitingHandlerTests extends ElasticsearchTestCase {
|
|||
// in the test
|
||||
ChannelFuture handshakeFuture = null;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (handshakeFuture == null) {
|
||||
handshakeFuture = channel.getPipeline().get(SslHandler.class).handshake();
|
||||
}
|
||||
channel.write(buffer);
|
||||
handshakeFuture = channel.getPipeline().get(SslHandler.class).handshake();
|
||||
}
|
||||
|
||||
return handshakeFuture;
|
||||
|
|
|
@ -13,7 +13,8 @@ import org.elasticsearch.common.network.NetworkService;
|
|||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.BigArrays;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.shield.ssl.ServerSSLService;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.netty.NettyTransport;
|
||||
|
@ -30,21 +31,24 @@ import static org.hamcrest.Matchers.*;
|
|||
|
||||
public class ShieldNettyTransportTests extends ElasticsearchTestCase {
|
||||
|
||||
private SSLService sslService;
|
||||
private ServerSSLService serverSSLService;
|
||||
private ClientSSLService clientSSLService;
|
||||
|
||||
@Before
|
||||
public void createSSLService() throws Exception {
|
||||
Path testnodeStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI());
|
||||
sslService = new SSLService(settingsBuilder()
|
||||
Settings settings = settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testnodeStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build());
|
||||
.build();
|
||||
serverSSLService = new ServerSSLService(settings);
|
||||
clientSSLService = new ClientSSLService(settings);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSSLCanBeDisabledByProfile() throws Exception {
|
||||
Settings settings = ImmutableSettings.builder().put("shield.transport.ssl", true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, sslService);
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService);
|
||||
setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", ImmutableSettings.builder().put("shield.ssl", false).build());
|
||||
assertThat(factory.getPipeline().get(SslHandler.class), nullValue());
|
||||
|
@ -53,7 +57,7 @@ public class ShieldNettyTransportTests extends ElasticsearchTestCase {
|
|||
@Test
|
||||
public void testThatSSLCanBeEnabledByProfile() throws Exception {
|
||||
Settings settings = ImmutableSettings.builder().put("shield.transport.ssl", false).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, sslService);
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService);
|
||||
setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", ImmutableSettings.builder().put("shield.ssl", true).build());
|
||||
assertThat(factory.getPipeline().get(SslHandler.class), notNullValue());
|
||||
|
@ -62,7 +66,7 @@ public class ShieldNettyTransportTests extends ElasticsearchTestCase {
|
|||
@Test
|
||||
public void testThatProfileTakesDefaultSSLSetting() throws Exception {
|
||||
Settings settings = ImmutableSettings.builder().put("shield.transport.ssl", true).build();
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, sslService);
|
||||
ShieldNettyTransport transport = new ShieldNettyTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, serverSSLService, clientSSLService);
|
||||
setOpenChannelsHandlerToMock(transport);
|
||||
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", ImmutableSettings.EMPTY);
|
||||
assertThat(factory.getPipeline().get(SslHandler.class), notNullValue());
|
||||
|
|
|
@ -15,7 +15,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.internal.InternalNode;
|
||||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
import org.elasticsearch.test.ShieldIntegrationTest;
|
||||
import org.elasticsearch.test.ShieldSettingsSource;
|
||||
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
|
||||
|
@ -73,10 +73,10 @@ public class SslClientAuthTests extends ShieldIntegrationTest {
|
|||
@Test
|
||||
public void testThatHttpWorksWithSslClientAuth() throws IOException {
|
||||
Settings settings = settingsBuilder().put(ShieldSettingsSource.getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient")).build();
|
||||
SSLService sslService = new SSLService(settings);
|
||||
ClientSSLService sslService = new ClientSSLService(settings);
|
||||
|
||||
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||
sslService.getSslContext(),
|
||||
sslService.sslContext(),
|
||||
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
|
||||
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
|
||||
|
|
Loading…
Reference in New Issue