security: move to a single SSLService

This change refactors the ssl services into a single class and reduces the number of
public methods that are exposed by the class. As part of this change, ssl configurations
must be loaded during construction of the service otherwise an exception will be thrown
when trying to retrieve the configuration. Additionally, the reloading of key material and
trust material is now handled by the SSLService instead of the SSLConfiguration. The
SSLConfiguration class has been made package private as its usage has been contained
to the ssl package entirely.

See elastic/elasticsearch#2971

Original commit: elastic/x-pack-elasticsearch@46e4535bb1
This commit is contained in:
jaymode 2016-08-04 14:56:07 -04:00
parent 20041446f3
commit af44ea0440
43 changed files with 1263 additions and 1446 deletions

View File

@ -110,10 +110,8 @@ import org.elasticsearch.xpack.security.rest.action.user.RestChangePasswordActio
import org.elasticsearch.xpack.security.rest.action.user.RestDeleteUserAction;
import org.elasticsearch.xpack.security.rest.action.user.RestGetUsersAction;
import org.elasticsearch.xpack.security.rest.action.user.RestPutUserAction;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration;
import org.elasticsearch.xpack.security.ssl.SSLConfigurationReloader;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.support.OptionalSettings;
import org.elasticsearch.xpack.security.transport.SecurityClientTransportService;
import org.elasticsearch.xpack.security.transport.SecurityServerTransportService;
@ -182,8 +180,7 @@ public class Security implements ActionPlugin, IngestPlugin {
modules.add(new SecurityTransportModule(settings));
modules.add(b -> {
// for transport client we still must inject these ssl classes with guice
b.bind(ServerSSLService.class).toProvider(Providers.<ServerSSLService>of(null));
b.bind(ClientSSLService.class).toInstance(new ClientSSLService(settings, null, new SSLConfiguration.Global(settings)));
b.bind(SSLService.class).toInstance(new SSLService(settings, null));
});
return modules;
@ -228,15 +225,10 @@ public class Security implements ActionPlugin, IngestPlugin {
final SecurityContext securityContext = new SecurityContext(settings, threadPool, cryptoService);
components.add(securityContext);
final SSLConfiguration.Global globalSslConfig = new SSLConfiguration.Global(settings);
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, globalSslConfig);
final ServerSSLService serverSSLService = new ServerSSLService(settings, env, globalSslConfig);
// just create the reloader as it will register itself as a listener to the ssl service and nothing else depends on it
// IMPORTANT: if the reloader construction is moved to later, then it needs to be updated to ensure any SSLContexts that have been
// loaded by the services are also monitored by the reloader!
new SSLConfigurationReloader(settings, env, serverSSLService, clientSSLService, resourceWatcherService);
components.add(clientSSLService);
components.add(serverSSLService);
final SSLService sslService = new SSLService(settings, env);
// just create the reloader as it will pull all of the loaded ssl configurations and start watching them
new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService);
components.add(sslService);
// realms construction
final NativeUsersStore nativeUsersStore = new NativeUsersStore(settings, client, threadPool);
@ -245,8 +237,8 @@ public class Security implements ActionPlugin, IngestPlugin {
realmFactories.put(FileRealm.TYPE, config -> new FileRealm(config, resourceWatcherService));
realmFactories.put(NativeRealm.TYPE, config -> new NativeRealm(config, nativeUsersStore));
realmFactories.put(ActiveDirectoryRealm.TYPE,
config -> new ActiveDirectoryRealm(config, resourceWatcherService, clientSSLService));
realmFactories.put(LdapRealm.TYPE, config -> new LdapRealm(config, resourceWatcherService, clientSSLService));
config -> new ActiveDirectoryRealm(config, resourceWatcherService, sslService));
realmFactories.put(LdapRealm.TYPE, config -> new LdapRealm(config, resourceWatcherService, sslService));
realmFactories.put(PkiRealm.TYPE, config -> new PkiRealm(config, resourceWatcherService));
for (XPackExtension extension : extensions) {
Map<String, Realm.Factory> newRealms = extension.getRealms();
@ -379,7 +371,7 @@ public class Security implements ActionPlugin, IngestPlugin {
settingsList.add(USER_SETTING);
// SSL settings
SSLConfiguration.Global.addSettings(settingsList);
SSLService.addSettings(settingsList);
// transport settings
SecurityNetty3Transport.addSettings(settingsList);

View File

@ -10,7 +10,7 @@ import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.ldap.support.AbstractLdapRealm;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
/**
*
@ -19,8 +19,8 @@ public class ActiveDirectoryRealm extends AbstractLdapRealm {
public static final String TYPE = "active_directory";
public ActiveDirectoryRealm(RealmConfig config, ResourceWatcherService watcherService, ClientSSLService clientSSLService) {
this(config, new ActiveDirectorySessionFactory(config, clientSSLService),
public ActiveDirectoryRealm(RealmConfig config, ResourceWatcherService watcherService, SSLService sslService) {
this(config, new ActiveDirectorySessionFactory(config, sslService),
new DnRoleMapper(TYPE, config, watcherService, null));
}

View File

@ -23,7 +23,7 @@ import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import java.util.concurrent.ExecutionException;
@ -54,7 +54,7 @@ public class ActiveDirectorySessionFactory extends SessionFactory {
private final DownLevelADAuthenticator downLevelADAuthenticator;
private final UpnADAuthenticator upnADAuthenticator;
public ActiveDirectorySessionFactory(RealmConfig config, ClientSSLService sslService) {
public ActiveDirectorySessionFactory(RealmConfig config, SSLService sslService) {
super(config, sslService);
Settings settings = config.settings();
domainName = settings.get(AD_DOMAIN_NAME_SETTING);

View File

@ -29,8 +29,7 @@ import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.store.FileRolesStore;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration;
import org.elasticsearch.xpack.security.ssl.SSLService;
import java.io.BufferedReader;
import java.io.IOException;
@ -134,8 +133,7 @@ public class ESNativeRealmMigrateTool extends MultiCommand {
// If using SSL, need a custom service because it's likely a self-signed certificate
if ("https".equalsIgnoreCase(uri.getScheme())) {
Settings sslSettings = settings.getByPrefix(setting("http.ssl."));
SSLConfiguration.Global globalConfig = new SSLConfiguration.Global(settings);
final ClientSSLService sslService = new ClientSSLService(sslSettings, env, globalConfig);
final SSLService sslService = new SSLService(settings, env);
final HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection();
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override

View File

@ -15,7 +15,7 @@ import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.ldap.support.AbstractLdapRealm;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
/**
@ -25,8 +25,8 @@ public class LdapRealm extends AbstractLdapRealm {
public static final String TYPE = "ldap";
public LdapRealm(RealmConfig config, ResourceWatcherService watcherService, ClientSSLService clientSSLService) {
this(config, sessionFactory(config, clientSSLService), new DnRoleMapper(TYPE, config, watcherService, null));
public LdapRealm(RealmConfig config, ResourceWatcherService watcherService, SSLService sslService) {
this(config, sessionFactory(config, sslService), new DnRoleMapper(TYPE, config, watcherService, null));
}
// pkg private for testing
@ -34,7 +34,7 @@ public class LdapRealm extends AbstractLdapRealm {
super(TYPE, config, sessionFactory, roleMapper);
}
static SessionFactory sessionFactory(RealmConfig config, ClientSSLService clientSSLService) {
static SessionFactory sessionFactory(RealmConfig config, SSLService sslService) {
Settings searchSettings = userSearchSettings(config);
try {
if (!searchSettings.names().isEmpty()) {
@ -43,9 +43,9 @@ public class LdapRealm extends AbstractLdapRealm {
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
"authentication section of the X-Pack guide.");
}
return new LdapUserSearchSessionFactory(config, clientSSLService);
return new LdapUserSearchSessionFactory(config, sslService);
}
return new LdapSessionFactory(config, clientSSLService);
return new LdapSessionFactory(config, sslService);
} catch (LDAPException e) {
throw new ElasticsearchException("failed to create realm [{}/{}]", e, LdapRealm.TYPE, config.name());
}

View File

@ -13,7 +13,7 @@ import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import java.text.MessageFormat;
import java.util.Locale;
@ -33,7 +33,7 @@ public class LdapSessionFactory extends SessionFactory {
private final String[] userDnTemplates;
private final GroupsResolver groupResolver;
public LdapSessionFactory(RealmConfig config, ClientSSLService sslService) {
public LdapSessionFactory(RealmConfig config, SSLService sslService) {
super(config, sslService);
Settings settings = config.settings();
userDnTemplates = settings.getAsArray(USER_DN_TEMPLATES_SETTING);

View File

@ -24,7 +24,7 @@ import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.support.Exceptions;
import java.util.Locale;
@ -49,7 +49,7 @@ class LdapUserSearchSessionFactory extends SessionFactory {
private final LDAPConnectionPool connectionPool;
LdapUserSearchSessionFactory(RealmConfig config, ClientSSLService sslService) throws LDAPException {
LdapUserSearchSessionFactory(RealmConfig config, SSLService sslService) throws LDAPException {
super(config, sslService);
Settings settings = config.settings();
userSearchBaseDn = settings.get("user_search.base_dn");

View File

@ -16,7 +16,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import javax.net.SocketFactory;
import java.util.regex.Pattern;
@ -50,12 +50,12 @@ public abstract class SessionFactory {
protected final ESLogger logger;
protected final RealmConfig config;
protected final TimeValue timeout;
protected final ClientSSLService sslService;
protected final SSLService sslService;
protected final ServerSet serverSet;
protected final boolean sslUsed;
protected SessionFactory(RealmConfig config, ClientSSLService sslService) {
protected SessionFactory(RealmConfig config, SSLService sslService) {
this.config = config;
this.logger = config.logger(getClass());
TimeValue searchTimeout = config.settings().getAsTime(TIMEOUT_LDAP_SETTING, TIMEOUT_DEFAULT);
@ -142,7 +142,7 @@ public abstract class SessionFactory {
return null;
}
protected ServerSet serverSet(Settings settings, ClientSSLService clientSSLService, LDAPServers ldapServers) {
protected ServerSet serverSet(Settings settings, SSLService clientSSLService, LDAPServers ldapServers) {
SocketFactory socketFactory = null;
if (ldapServers.ssl()) {
socketFactory = clientSSLService.sslSocketFactory(settings.getByPrefix("ssl."));

View File

@ -1,307 +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.xpack.security.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Custom;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 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 {
private final SSLContextCacheLoader cacheLoader = new SSLContextCacheLoader();
private final ConcurrentHashMap<SSLConfiguration, SSLContext> sslContexts = new ConcurrentHashMap<>();
protected final SSLConfiguration globalSSLConfiguration;
protected final Environment env;
private Listener listener = Listener.NOOP;
AbstractSSLService(Settings settings, Environment environment, Global globalSSLConfiguration) {
super(settings);
this.env = environment;
this.globalSSLConfiguration = globalSSLConfiguration;
}
public String[] supportedProtocols() {
return globalSSLConfiguration.supportedProtocols().toArray(Strings.EMPTY_ARRAY);
}
public String[] ciphers() {
return globalSSLConfiguration.ciphers().toArray(Strings.EMPTY_ARRAY);
}
public SSLSocketFactory sslSocketFactory(Settings settings) {
return sslSocketFactory(sslContext(settings));
}
protected SSLSocketFactory sslSocketFactory(SSLContext sslContext) {
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
return new SecuritySSLSocketFactory(socketFactory, supportedProtocols(),
supportedCiphers(socketFactory.getSupportedCipherSuites(), ciphers(), false));
}
public SSLEngine createSSLEngine() {
return createSSLEngine(globalSSLConfiguration, null, -1);
}
public SSLEngine createSSLEngine(Settings settings) {
return createSSLEngine(settings, null, -1);
}
public SSLEngine createSSLEngine(Settings settings, String host, int port) {
if (settings.isEmpty()) {
return createSSLEngine(globalSSLConfiguration, host, port);
}
return createSSLEngine(sslConfiguration(settings), host, port);
}
public SSLEngine createSSLEngine(SSLConfiguration configuration, String host, int port) {
return createSSLEngine(sslContext(configuration),
configuration.ciphers().toArray(Strings.EMPTY_ARRAY),
configuration.supportedProtocols().toArray(Strings.EMPTY_ARRAY),
host, port);
}
public SSLContext sslContext() {
return sslContext(globalSSLConfiguration);
}
public SSLContext sslContext(Settings settings) {
if (settings.isEmpty()) {
return sslContext();
}
SSLConfiguration sslConfiguration = sslConfiguration(settings);
return sslContext(sslConfiguration);
}
protected SSLContext sslContext(SSLConfiguration sslConfiguration) {
return sslContexts.computeIfAbsent(sslConfiguration, cacheLoader::load);
}
protected SSLConfiguration sslConfiguration(Settings customSettings) {
return new Custom(customSettings, globalSSLConfiguration);
}
protected abstract void validateSSLConfiguration(SSLConfiguration configuration);
SSLEngine createSSLEngine(SSLContext sslContext, String[] ciphers, String[] supportedProtocols, String host, int port) {
SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
try {
sslEngine.setEnabledCipherSuites(supportedCiphers(sslEngine.getSupportedCipherSuites(), ciphers, false));
} catch (ElasticsearchException e) {
throw e;
} catch (Exception e) {
throw new IllegalArgumentException("failed loading cipher suites [" + Arrays.asList(ciphers) + "]", e);
}
try {
sslEngine.setEnabledProtocols(supportedProtocols);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("failed setting supported protocols [" + Arrays.asList(supportedProtocols) + "]", e);
}
return sslEngine;
}
String[] supportedCiphers(String[] supportedCiphers, String[] requestedCiphers, boolean log) {
List<String> requestedCiphersList = new ArrayList<>(requestedCiphers.length);
List<String> unsupportedCiphers = new LinkedList<>();
boolean found;
for (String requestedCipher : requestedCiphers) {
found = false;
for (String supportedCipher : supportedCiphers) {
if (supportedCipher.equals(requestedCipher)) {
found = true;
requestedCiphersList.add(requestedCipher);
break;
}
}
if (!found) {
unsupportedCiphers.add(requestedCipher);
}
}
if (requestedCiphersList.isEmpty()) {
throw new IllegalArgumentException("none of the ciphers " + Arrays.asList(requestedCiphers) + " are supported by this JVM");
}
if (log && !unsupportedCiphers.isEmpty()) {
logger.error("unsupported ciphers [{}] were requested but cannot be used in this JVM, however there are supported ciphers " +
"that will be used [{}]. If you are trying to use ciphers with a key length greater than 128 bits on an Oracle JVM, " +
"you will need to install the unlimited strength JCE policy files.", unsupportedCiphers, requestedCiphersList);
}
return requestedCiphersList.toArray(new String[requestedCiphersList.size()]);
}
/**
* Sets the listener to the value provided. Must not be {@code null}
*/
void setListener(Listener listener) {
this.listener = Objects.requireNonNull(listener);
}
/**
* Returns the existing {@link SSLContext} for the configuration or {@code null}
*/
SSLContext getSSLContext(SSLConfiguration sslConfiguration) {
return sslContexts.get(sslConfiguration);
}
/**
* Accessor to the loaded ssl configuration objects at the current point in time. This is useful for testing
*/
Collection<SSLConfiguration> getLoadedSSLConfigurations() {
return Collections.unmodifiableSet(new HashSet<>(sslContexts.keySet()));
}
private class SSLContextCacheLoader {
public SSLContext load(SSLConfiguration sslConfiguration) {
validateSSLConfiguration(sslConfiguration);
if (logger.isDebugEnabled()) {
logger.debug("using ssl settings [{}]", sslConfiguration);
}
TrustManager[] trustManagers = sslConfiguration.trustConfig().trustManagers(env);
KeyManager[] keyManagers = sslConfiguration.keyConfig().keyManagers(env);
SSLContext sslContext = createSslContext(keyManagers, trustManagers, sslConfiguration.protocol(),
sslConfiguration.sessionCacheSize(), sslConfiguration.sessionCacheTimeout());
// check the supported ciphers and log them here
supportedCiphers(sslContext.getSupportedSSLParameters().getCipherSuites(),
sslConfiguration.ciphers().toArray(Strings.EMPTY_ARRAY), true);
listener.onSSLContextLoaded(sslConfiguration);
return sslContext;
}
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(Math.toIntExact(sessionCacheTimeout.seconds()));
return sslContext;
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize the SSLContext", e);
}
}
}
/**
* This socket factory set the protocols and ciphers on each SSLSocket after it is created
*/
static class SecuritySSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
private final String[] supportedProtocols;
private final String[] ciphers;
SecuritySSLSocketFactory(SSLSocketFactory delegate, String[] supportedProtocols, String[] ciphers) {
this.delegate = delegate;
this.supportedProtocols = supportedProtocols;
this.ciphers = ciphers;
}
@Override
public String[] getDefaultCipherSuites() {
return ciphers;
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket();
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(socket, host, port, autoClose);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(String host, int port) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
configureSSLSocket(sslSocket);
return sslSocket;
}
private void configureSSLSocket(SSLSocket socket) {
socket.setEnabledProtocols(supportedProtocols);
socket.setEnabledCipherSuites(ciphers);
}
}
interface Listener {
/**
* Called after a new SSLContext has been created
* @param sslConfiguration the configuration used to create the SSLContext
*/
void onSSLContextLoaded(SSLConfiguration sslConfiguration);
Listener NOOP = (s) -> {};
}
}

View File

@ -1,23 +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.xpack.security.ssl;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
public class ClientSSLService extends AbstractSSLService {
public ClientSSLService(Settings settings, Environment env, Global globalSSLConfiguration) {
super(settings, env, globalSSLConfiguration);
}
@Override
protected void validateSSLConfiguration(SSLConfiguration sslConfiguration) {
sslConfiguration.keyConfig().validate();
sslConfiguration.trustConfig().validate();
}
}

View File

@ -21,15 +21,13 @@ import java.util.List;
abstract class KeyConfig extends TrustConfig {
private X509ExtendedKeyManager[] keyManagers = null;
KeyConfig(boolean includeSystem) {
super(includeSystem);
}
static final KeyConfig NONE = new KeyConfig(false) {
@Override
X509ExtendedKeyManager loadKeyManager(@Nullable Environment environment) {
X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) {
return null;
}
@ -53,97 +51,5 @@ abstract class KeyConfig extends TrustConfig {
}
};
final synchronized X509ExtendedKeyManager[] keyManagers(@Nullable Environment environment) {
if (keyManagers == null) {
X509ExtendedKeyManager keyManager = loadKeyManager(environment);
setKeyManagers(keyManager);
}
return keyManagers;
}
@Override
synchronized void reload(@Nullable Environment environment) {
if (trustManagers == null) {
// trust managers were never initialized... do it lazily!
X509ExtendedKeyManager loadedKeyManager = loadKeyManager(environment);
setKeyManagers(loadedKeyManager);
return;
}
X509ExtendedTrustManager loadedTrustManager = loadAndMergeIfNecessary(environment);
X509ExtendedKeyManager loadedKeyManager = loadKeyManager(environment);
setTrustManagers(loadedTrustManager);
setKeyManagers(loadedKeyManager);
}
final synchronized void setKeyManagers(X509ExtendedKeyManager loadedKeyManager) {
if (loadedKeyManager == null) {
this.keyManagers = new X509ExtendedKeyManager[0];
} else if (this.keyManagers == null || this.keyManagers.length == 0) {
this.keyManagers = new X509ExtendedKeyManager[] { new ReloadableX509KeyManager(loadedKeyManager) };
} else {
assert this.keyManagers[0] instanceof ReloadableX509KeyManager;
((ReloadableX509KeyManager)this.keyManagers[0]).setKeyManager(loadedKeyManager);
}
}
abstract X509ExtendedKeyManager loadKeyManager(@Nullable Environment environment);
final class ReloadableX509KeyManager extends X509ExtendedKeyManager {
private volatile X509ExtendedKeyManager keyManager;
ReloadableX509KeyManager(X509ExtendedKeyManager keyManager) {
this.keyManager = keyManager;
}
@Override
public String[] getClientAliases(String s, Principal[] principals) {
return keyManager.getClientAliases(s, principals);
}
@Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return keyManager.chooseClientAlias(strings, principals, socket);
}
@Override
public String[] getServerAliases(String s, Principal[] principals) {
return keyManager.getServerAliases(s, principals);
}
@Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return keyManager.chooseServerAlias(s, principals, socket);
}
@Override
public X509Certificate[] getCertificateChain(String s) {
return keyManager.getCertificateChain(s);
}
@Override
public PrivateKey getPrivateKey(String s) {
return keyManager.getPrivateKey(s);
}
@Override
public String chooseEngineClientAlias(String[] strings, Principal[] principals, SSLEngine engine) {
return keyManager.chooseEngineClientAlias(strings, principals, engine);
}
@Override
public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine engine) {
return keyManager.chooseEngineServerAlias(s, principals, engine);
}
synchronized void setKeyManager(X509ExtendedKeyManager x509ExtendedKeyManager) {
this.keyManager = x509ExtendedKeyManager;
}
// pkg-private accessor for testing
X509ExtendedKeyManager getKeyManager() {
return keyManager;
}
}
abstract X509ExtendedKeyManager createKeyManager(@Nullable Environment environment);
}

View File

@ -36,7 +36,7 @@ class PEMKeyConfig extends KeyConfig {
}
@Override
X509ExtendedKeyManager loadKeyManager(@Nullable Environment environment) {
X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) {
// password must be non-null for keystore...
char[] password = keyPassword == null ? new char[0] : keyPassword.toCharArray();
try {

View File

@ -31,26 +31,26 @@ import static org.elasticsearch.xpack.security.support.OptionalSettings.createTi
/**
* Class that contains all configuration related to SSL use within x-pack
*/
public abstract class SSLConfiguration {
abstract class SSLConfiguration {
public abstract KeyConfig keyConfig();
abstract KeyConfig keyConfig();
public abstract TrustConfig trustConfig();
abstract TrustConfig trustConfig();
public abstract String protocol();
abstract String protocol();
public abstract int sessionCacheSize();
abstract int sessionCacheSize();
public abstract TimeValue sessionCacheTimeout();
abstract TimeValue sessionCacheTimeout();
public abstract List<String> ciphers();
abstract List<String> ciphers();
public abstract List<String> supportedProtocols();
abstract List<String> supportedProtocols();
/**
* Provides the list of paths to files that back this configuration
*/
public List<Path> filesToMonitor(@Nullable Environment environment) {
List<Path> filesToMonitor(@Nullable Environment environment) {
if (keyConfig() == trustConfig()) {
return keyConfig().filesToMonitor(environment);
}
@ -59,30 +59,6 @@ public abstract class SSLConfiguration {
return paths;
}
/**
* Reloads the portion of this configuration that makes use of the modified file
*/
public void reload(Path file, @Nullable Environment environment) {
if (keyConfig() == trustConfig()) {
keyConfig().reload(environment);
return;
}
for (Path path : keyConfig().filesToMonitor(environment)) {
if (file.equals(path)) {
keyConfig().reload(environment);
break;
}
}
for (Path path : trustConfig().filesToMonitor(environment)) {
if (file.equals(path)) {
trustConfig().reload(environment);
break;
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -125,14 +101,14 @@ public abstract class SSLConfiguration {
return result;
}
public static class Global extends SSLConfiguration {
static class Global extends SSLConfiguration {
public static final List<String> DEFAULT_SUPPORTED_PROTOCOLS = Arrays.asList("TLSv1", "TLSv1.1", "TLSv1.2");
public static final List<String> DEFAULT_CIPHERS =
static final List<String> DEFAULT_SUPPORTED_PROTOCOLS = Arrays.asList("TLSv1", "TLSv1.1", "TLSv1.2");
static final List<String> DEFAULT_CIPHERS =
Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
public static final TimeValue DEFAULT_SESSION_CACHE_TIMEOUT = TimeValue.timeValueHours(24);
public static final int DEFAULT_SESSION_CACHE_SIZE = 1000;
public static final String DEFAULT_PROTOCOL = "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 = "TLSv1.2";
// common settings
static final Setting<List<String>> CIPHERS_SETTING = Setting.listSetting(globalKey(Custom.CIPHERS_SETTING), DEFAULT_CIPHERS,
@ -145,8 +121,6 @@ public abstract class SSLConfiguration {
DEFAULT_SESSION_CACHE_SIZE, Property.NodeScope, Property.Filtered);
static final Setting<TimeValue> SESSION_CACHE_TIMEOUT_SETTING = Setting.timeSetting(globalKey(Custom.CACHE_TIMEOUT_SETTING),
DEFAULT_SESSION_CACHE_TIMEOUT, Property.NodeScope, Property.Filtered);
static final Setting<Boolean> RELOAD_ENABLED_SETTING =
Setting.boolSetting(globalKey(Custom.RELOAD_ENABLED_SETTING), true, Property.NodeScope, Property.Filtered);
// keystore settings
static final Setting<Optional<String>> KEYSTORE_PATH_SETTING = createString(globalKey(Custom.KEYSTORE_PATH_SETTING),
@ -185,27 +159,6 @@ public abstract class SSLConfiguration {
static final Setting<Boolean> INCLUDE_JDK_CERTS_SETTING = Setting.boolSetting(globalKey(Custom.INCLUDE_JDK_CERTS_SETTING), true,
Property.NodeScope, Property.Filtered);
public static void addSettings(List<Setting<?>> settings) {
settings.add(Global.CIPHERS_SETTING);
settings.add(Global.SUPPORTED_PROTOCOLS_SETTING);
settings.add(Global.KEYSTORE_PATH_SETTING);
settings.add(Global.KEYSTORE_PASSWORD_SETTING);
settings.add(Global.KEYSTORE_ALGORITHM_SETTING);
settings.add(Global.KEYSTORE_KEY_PASSWORD_SETTING);
settings.add(Global.KEY_PATH_SETTING);
settings.add(Global.KEY_PASSWORD_SETTING);
settings.add(Global.CERT_SETTING);
settings.add(Global.TRUSTSTORE_PATH_SETTING);
settings.add(Global.TRUSTSTORE_PASSWORD_SETTING);
settings.add(Global.TRUSTSTORE_ALGORITHM_SETTING);
settings.add(Global.PROTOCOL_SETTING);
settings.add(Global.SESSION_CACHE_SIZE_SETTING);
settings.add(Global.SESSION_CACHE_TIMEOUT_SETTING);
settings.add(Global.CA_PATHS_SETTING);
settings.add(Global.INCLUDE_JDK_CERTS_SETTING);
settings.add(Global.RELOAD_ENABLED_SETTING);
}
private final KeyConfig keyConfig;
private final TrustConfig trustConfig;
private final String sslProtocol;
@ -219,7 +172,7 @@ public abstract class SSLConfiguration {
*
* @param settings the global settings to build the SSL configuration from
*/
public Global(Settings settings) {
Global(Settings settings) {
this.keyConfig = createGlobalKeyConfig(settings);
this.trustConfig = createGlobalTrustConfig(settings, keyConfig);
this.sslProtocol = PROTOCOL_SETTING.get(settings);
@ -230,44 +183,44 @@ public abstract class SSLConfiguration {
}
@Override
public KeyConfig keyConfig() {
KeyConfig keyConfig() {
return keyConfig;
}
@Override
public TrustConfig trustConfig() {
TrustConfig trustConfig() {
return trustConfig;
}
@Override
public String protocol() {
String protocol() {
return sslProtocol;
}
@Override
public int sessionCacheSize() {
int sessionCacheSize() {
return sessionCacheSize;
}
@Override
public TimeValue sessionCacheTimeout() {
TimeValue sessionCacheTimeout() {
return sessionCacheTimeout;
}
@Override
public List<String> ciphers() {
List<String> ciphers() {
return ciphers;
}
@Override
public List<String> supportedProtocols() {
List<String> supportedProtocols() {
return supportedProtocols;
}
@Override
public String toString() {
return "SSLConfiguration{" +
", keyConfig=[" + keyConfig +
"keyConfig=[" + keyConfig +
"], trustConfig=" + trustConfig +
"], sslProtocol=['" + sslProtocol + '\'' +
"], sessionCacheSize=[" + sessionCacheSize +
@ -291,7 +244,6 @@ public abstract class SSLConfiguration {
}
boolean includeSystem = INCLUDE_JDK_CERTS_SETTING.get(settings);
boolean reloadEnabled = RELOAD_ENABLED_SETTING.get(settings);
if (keyPath != null) {
String keyPassword = KEY_PASSWORD_SETTING.get(settings).orElse(null);
List<String> certPaths = getListOrNull(CERT_SETTING, settings);
@ -330,7 +282,7 @@ public abstract class SSLConfiguration {
}
}
public static class Custom extends SSLConfiguration {
static class Custom extends SSLConfiguration {
static final Setting<Optional<String>> PROTOCOL_SETTING = createString("protocol");
static final Setting<Optional<Integer>> CACHE_SIZE_SETTING = createInt("session.cache_size");
@ -360,7 +312,6 @@ public abstract class SSLConfiguration {
static final Setting<List<String>> CA_PATHS_SETTING = Setting.listSetting("ca", Collections.emptyList(), s -> s);
static final Setting<Boolean> INCLUDE_JDK_CERTS_SETTING = Setting.boolSetting("trust_cacerts", true);
static final Setting<Boolean> RELOAD_ENABLED_SETTING = Setting.boolSetting("reload.enabled", true);
private final KeyConfig keyConfig;
private final TrustConfig trustConfig;
@ -376,7 +327,7 @@ public abstract class SSLConfiguration {
* @param settings the profile settings to get the SSL configuration for
* @param defaultConfig the default SSL configuration
*/
public Custom(Settings settings, SSLConfiguration defaultConfig) {
Custom(Settings settings, SSLConfiguration defaultConfig) {
Objects.requireNonNull(settings);
this.keyConfig = createKeyConfig(settings, defaultConfig);
this.trustConfig = createTrustConfig(settings, keyConfig, defaultConfig);
@ -388,44 +339,44 @@ public abstract class SSLConfiguration {
}
@Override
public KeyConfig keyConfig() {
KeyConfig keyConfig() {
return keyConfig;
}
@Override
public TrustConfig trustConfig() {
TrustConfig trustConfig() {
return trustConfig;
}
@Override
public String protocol() {
String protocol() {
return sslProtocol;
}
@Override
public int sessionCacheSize() {
int sessionCacheSize() {
return sessionCacheSize;
}
@Override
public TimeValue sessionCacheTimeout() {
TimeValue sessionCacheTimeout() {
return sessionCacheTimeout;
}
@Override
public List<String> ciphers() {
List<String> ciphers() {
return ciphers;
}
@Override
public List<String> supportedProtocols() {
List<String> supportedProtocols() {
return supportedProtocols;
}
@Override
public String toString() {
return "SSLConfiguration{" +
", keyConfig=[" + keyConfig +
"keyConfig=[" + keyConfig +
"], trustConfig=" + trustConfig +
"], sslProtocol=['" + sslProtocol + '\'' +
"], sessionCacheSize=[" + sessionCacheSize +
@ -445,7 +396,6 @@ public abstract class SSLConfiguration {
}
boolean includeSystem = INCLUDE_JDK_CERTS_SETTING.get(settings);
boolean reloadEnabled = RELOAD_ENABLED_SETTING.get(settings);
if (keyPath != null) {
String keyPassword = KEY_PASSWORD_SETTING.get(settings).orElse(null);
List<String> certPaths = getListOrNull(CERT_SETTING, settings);

View File

@ -12,14 +12,12 @@ import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.watcher.ResourceWatcherService.Frequency;
import org.elasticsearch.xpack.security.ssl.SSLService.SSLContextHolder;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSessionContext;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -30,28 +28,19 @@ import java.util.concurrent.CopyOnWriteArraySet;
* Ensures that the files backing an {@link SSLConfiguration} are monitored for changes and the underlying key/trust material is reloaded
* and the {@link SSLContext} has existing sessions invalidated to force the use of the new key/trust material
*/
public class SSLConfigurationReloader extends AbstractComponent implements AbstractSSLService.Listener {
public class SSLConfigurationReloader extends AbstractComponent {
private final ConcurrentHashMap<Path, ChangeListener> pathToChangeListenerMap = new ConcurrentHashMap<>();
private final Environment environment;
private final ResourceWatcherService resourceWatcherService;
private final ServerSSLService serverSSLService;
private final ClientSSLService clientSSLService;
private final SSLService sslService;
public SSLConfigurationReloader(Settings settings, Environment env, ServerSSLService serverSSLService,
ClientSSLService clientSSLService, ResourceWatcherService resourceWatcher) {
public SSLConfigurationReloader(Settings settings, Environment env, SSLService sslService, ResourceWatcherService resourceWatcher) {
super(settings);
this.environment = env;
this.resourceWatcherService = resourceWatcher;
this.serverSSLService = serverSSLService;
this.clientSSLService = clientSSLService;
serverSSLService.setListener(this);
clientSSLService.setListener(this);
}
@Override
public void onSSLContextLoaded(SSLConfiguration sslConfiguration) {
startWatching(Collections.singleton(sslConfiguration));
this.sslService = sslService;
startWatching(sslService.getLoadedSSLConfigurations());
}
/**
@ -84,24 +73,11 @@ public class SSLConfigurationReloader extends AbstractComponent implements Abstr
}
/**
* Invalidates all of the sessions in the provided {@link SSLContext}
* Reloads the ssl context associated with this configuration. It is visible so that tests can override as needed
*/
private static void invalidateAllSessions(SSLContext context) {
if (context != null) {
invalidateSessions(context.getClientSessionContext());
invalidateSessions(context.getServerSessionContext());
}
}
/**
* Invalidates the sessions in the provided {@link SSLSessionContext}
*/
private static void invalidateSessions(SSLSessionContext sslSessionContext) {
Enumeration<byte[]> sessionIds = sslSessionContext.getIds();
while (sessionIds.hasMoreElements()) {
byte[] sessionId = sessionIds.nextElement();
sslSessionContext.getSession(sessionId).invalidate();
}
void reloadSSLContext(SSLConfiguration configuration) {
logger.debug("reloading ssl configuration [{}]", configuration);
sslService.sslContextHolder(configuration).reload();
}
/**
@ -140,9 +116,7 @@ public class SSLConfigurationReloader extends AbstractComponent implements Abstr
public void onFileChanged(Path file) {
for (SSLConfiguration sslConfiguration : sslConfigurations) {
if (sslConfiguration.filesToMonitor(environment).contains(file)) {
sslConfiguration.reload(file, environment);
invalidateAllSessions(serverSSLService.getSSLContext(sslConfiguration));
invalidateAllSessions(clientSSLService.getSSLContext(sslConfiguration));
reloadSSLContext(sslConfiguration);
}
}
}

View File

@ -0,0 +1,653 @@
/*
* 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.xpack.security.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.transport.TransportSettings;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Custom;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4HttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static org.elasticsearch.xpack.security.Security.setting;
import static org.elasticsearch.xpack.security.Security.settingPrefix;
import static org.elasticsearch.xpack.security.authc.Realms.REALMS_GROUPS_SETTINGS;
import static org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport.SSL_SETTING;
/**
* Provides access to {@link SSLEngine} and {@link SSLSocketFactory} objects based on a provided configuration. All
* configurations loaded by this service must be configured on construction.
*/
public class SSLService extends AbstractComponent {
private final Map<SSLConfiguration, SSLContextHolder> sslContexts;
private final SSLConfiguration globalSSLConfiguration;
private final Environment env;
public SSLService(Settings settings, Environment environment) {
super(settings);
this.env = environment;
this.globalSSLConfiguration = new Global(settings);
this.sslContexts = loadSSLConfigurations();
}
/**
* Create a new {@link SSLSocketFactory} based on the provided settings. The settings are used to identify the ssl configuration that
* should be used to create the socket factory. The socket factory will also properly configure the ciphers and protocols on each
* socket that is created
* @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. An empty settings will return
* a factory created from the default configuration
* @return {@link SSLSocketFactory}
*/
public SSLSocketFactory sslSocketFactory(Settings settings) {
SSLConfiguration sslConfiguration = sslConfiguration(settings);
SSLSocketFactory socketFactory = sslContext(sslConfiguration).getSocketFactory();
return new SecuritySSLSocketFactory(socketFactory, sslConfiguration.supportedProtocols().toArray(Strings.EMPTY_ARRAY),
supportedCiphers(socketFactory.getSupportedCipherSuites(), sslConfiguration.ciphers(), false));
}
/**
* Creates an {@link SSLEngine} based on the provided settings. The settings are used to identify the ssl configuration that should be
* used to create the engine. This SSLEngine cannot be used for hostname verification since the engine will not be created with the
* host and port. This method is useful to obtain an SSLEngine that will be used for server connections or client connections that
* will not use hostname verification.
* @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. An empty settings will return
* a SSLEngine created from the default configuration
* @return {@link SSLEngine}
*/
public SSLEngine createSSLEngine(Settings settings) {
return createSSLEngine(settings, null, -1);
}
/**
* Creates an {@link SSLEngine} based on the provided settings. The settings are used to identify the ssl configuration that should be
* used to create the engine. This SSLEngine can be used for a connection that requires hostname verification assuming the provided
* host and port are correct. The SSLEngine created by this method is most useful for clients with hostname verificaton enabled
* @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. An empty settings will return
* a SSLEngine created from the default configuration
* @param host the host of the remote endpoint. If using hostname verification, this should match what is in the remote endpoint's
* certificate
* @param port the port of the remote endpoint
* @return {@link SSLEngine}
*/
public SSLEngine createSSLEngine(Settings settings, String host, int port) {
SSLConfiguration configuration = sslConfiguration(settings);
SSLContext sslContext = sslContext(configuration);
SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
String[] ciphers = supportedCiphers(sslEngine.getSupportedCipherSuites(), configuration.ciphers(), false);
try {
sslEngine.setEnabledCipherSuites(ciphers);
} catch (ElasticsearchException e) {
throw e;
} catch (Exception e) {
throw new IllegalArgumentException("failed loading cipher suites " + Arrays.toString(ciphers), e);
}
String[] supportedProtocols = configuration.supportedProtocols().toArray(Strings.EMPTY_ARRAY);
try {
sslEngine.setEnabledProtocols(supportedProtocols);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("failed setting supported protocols " + Arrays.toString(supportedProtocols), e);
}
return sslEngine;
}
/**
* Returns whether the provided settings results in a valid configuration that can be used for server connections
*/
public boolean isConfigurationValidForServerUsage(Settings settings) {
SSLConfiguration sslConfiguration = sslConfiguration(settings);
return sslConfiguration.keyConfig() != KeyConfig.NONE;
}
/**
* Returns the {@link SSLContext} for the global configuration. Mainly used for testing
*/
SSLContext sslContext() {
return sslContextHolder(globalSSLConfiguration).sslContext();
}
/**
* Returns the {@link SSLContext} for the configuration
*/
SSLContext sslContext(SSLConfiguration configuration) {
return sslContextHolder(configuration).sslContext();
}
/**
* Returns the existing {@link SSLContextHolder} for the configuration
* @throws IllegalArgumentException if not found
*/
SSLContextHolder sslContextHolder(SSLConfiguration sslConfiguration) {
SSLContextHolder holder = sslContexts.get(sslConfiguration);
if (holder == null) {
throw new IllegalArgumentException("did not find a SSLContext for [" + sslConfiguration.toString() + "]");
}
return holder;
}
/**
* Returns the existing {@link SSLConfiguration} for the given settings
* @param settings the settings for the ssl configuration
* @return the ssl configuration for the provided settings. If the settings are empty, the global configuration is returned
*/
SSLConfiguration sslConfiguration(Settings settings) {
if (settings.isEmpty()) {
return globalSSLConfiguration;
}
return new Custom(settings, globalSSLConfiguration);
}
/**
* Accessor to the loaded ssl configuration objects at the current point in time. This is useful for testing
*/
Collection<SSLConfiguration> getLoadedSSLConfigurations() {
return Collections.unmodifiableSet(new HashSet<>(sslContexts.keySet()));
}
/**
* Returns the intersection of the supported ciphers with the requested ciphers. This method will also optionally log if unsupported
* ciphers were requested.
* @throws IllegalArgumentException if no supported ciphers are in the requested ciphers
*/
String[] supportedCiphers(String[] supportedCiphers, List<String> requestedCiphers, boolean log) {
List<String> supportedCiphersList = new ArrayList<>(requestedCiphers.size());
List<String> unsupportedCiphers = new LinkedList<>();
boolean found;
for (String requestedCipher : requestedCiphers) {
found = false;
for (String supportedCipher : supportedCiphers) {
if (supportedCipher.equals(requestedCipher)) {
found = true;
supportedCiphersList.add(requestedCipher);
break;
}
}
if (!found) {
unsupportedCiphers.add(requestedCipher);
}
}
if (supportedCiphersList.isEmpty()) {
throw new IllegalArgumentException("none of the ciphers " + Arrays.toString(requestedCiphers.toArray())
+ " are supported by this JVM");
}
if (log && !unsupportedCiphers.isEmpty()) {
logger.error("unsupported ciphers [{}] were requested but cannot be used in this JVM, however there are supported ciphers " +
"that will be used [{}]. If you are trying to use ciphers with a key length greater than 128 bits on an Oracle JVM, " +
"you will need to install the unlimited strength JCE policy files.", unsupportedCiphers, supportedCiphersList);
}
return supportedCiphersList.toArray(new String[supportedCiphersList.size()]);
}
/**
* Creates an {@link SSLContext} based on the provided configuration
* @param sslConfiguration the configuration to use for context creation
* @return the created SSLContext
*/
private SSLContextHolder createSslContext(SSLConfiguration sslConfiguration) {
if (logger.isDebugEnabled()) {
logger.debug("using ssl settings [{}]", sslConfiguration);
}
ReloadableTrustManager trustManager =
new ReloadableTrustManager(sslConfiguration.trustConfig().createTrustManager(env), sslConfiguration.trustConfig());
ReloadableX509KeyManager keyManager =
new ReloadableX509KeyManager(sslConfiguration.keyConfig().createKeyManager(env), sslConfiguration.keyConfig());
// Initialize sslContext
try {
SSLContext sslContext = SSLContext.getInstance(sslConfiguration.protocol());
sslContext.init(new X509ExtendedKeyManager[] { keyManager }, new X509ExtendedTrustManager[] { trustManager }, null);
sslContext.getServerSessionContext().setSessionCacheSize(sslConfiguration.sessionCacheSize());
sslContext.getServerSessionContext().setSessionTimeout(Math.toIntExact(sslConfiguration.sessionCacheTimeout().seconds()));
// check the supported ciphers and log them here to prevent spamming logs on every call
supportedCiphers(sslContext.getSupportedSSLParameters().getCipherSuites(), sslConfiguration.ciphers(), true);
return new SSLContextHolder(sslContext, trustManager, keyManager);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new ElasticsearchException("failed to initialize the SSLContext", e);
}
}
/**
* Parses the settings to load all SSLConfiguration objects that will be used.
*/
private Map<SSLConfiguration, SSLContextHolder> loadSSLConfigurations() {
Map<SSLConfiguration, SSLContextHolder> sslConfigurations = new HashMap<>();
validateSSLConfiguration(globalSSLConfiguration);
sslConfigurations.put(globalSSLConfiguration, createSslContext(globalSSLConfiguration));
List<Settings> sslSettings = new ArrayList<>();
sslSettings.addAll(getTransportSSLSettings(settings));
sslSettings.addAll(getRealmsSSLSettings(settings));
sslSettings.addAll(getHttpSSLSettings(settings));
for (Settings settings : sslSettings) {
SSLConfiguration sslConfiguration = new Custom(settings, globalSSLConfiguration);
validateSSLConfiguration(sslConfiguration);
if (sslConfigurations.containsKey(sslConfiguration) == false) {
sslConfigurations.put(sslConfiguration, createSslContext(sslConfiguration));
}
}
return Collections.unmodifiableMap(sslConfigurations);
}
private void validateSSLConfiguration(SSLConfiguration configuration) {
configuration.keyConfig().validate();
configuration.trustConfig().validate();
}
/**
* This socket factory wraps an existing SSLSocketFactory and sets the protocols and ciphers on each SSLSocket after it is created. This
* is needed even though the SSLContext is configured properly as the configuration does not flow down to the sockets created by the
* SSLSocketFactory obtained from the SSLContext.
*/
private static class SecuritySSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
private final String[] supportedProtocols;
private final String[] ciphers;
SecuritySSLSocketFactory(SSLSocketFactory delegate, String[] supportedProtocols, String[] ciphers) {
this.delegate = delegate;
this.supportedProtocols = supportedProtocols;
this.ciphers = ciphers;
}
@Override
public String[] getDefaultCipherSuites() {
return ciphers;
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket();
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(socket, host, port, autoClose);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(String host, int port) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port);
configureSSLSocket(sslSocket);
return sslSocket;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
configureSSLSocket(sslSocket);
return sslSocket;
}
private void configureSSLSocket(SSLSocket socket) {
socket.setEnabledProtocols(supportedProtocols);
socket.setEnabledCipherSuites(ciphers);
}
}
/**
* Wraps a trust manager to delegate to. If the trust material needs to be reloaded, then the delegate will be switched after
* reloading
*/
final class ReloadableTrustManager extends X509ExtendedTrustManager {
private volatile X509ExtendedTrustManager trustManager;
private final TrustConfig trustConfig;
ReloadableTrustManager(X509ExtendedTrustManager trustManager, TrustConfig trustConfig) {
this.trustManager = trustManager == null ? new EmptyX509TrustManager() : trustManager;
this.trustConfig = trustConfig;
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s, socket);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s, socket);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s, sslEngine);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s, sslEngine);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
void reload() {
X509ExtendedTrustManager loadedTrustManager = trustConfig.createTrustManager(env);
if (loadedTrustManager == null) {
this.trustManager = new EmptyX509TrustManager();
} else {
this.trustManager = loadedTrustManager;
}
}
X509ExtendedTrustManager getTrustManager() {
return trustManager;
}
}
/**
* Wraps a key manager and delegates all calls to it. When the key material needs to be reloaded, then the delegate is swapped after
* a new one has been loaded
*/
final class ReloadableX509KeyManager extends X509ExtendedKeyManager {
private volatile X509ExtendedKeyManager keyManager;
private final KeyConfig keyConfig;
ReloadableX509KeyManager(X509ExtendedKeyManager keyManager, KeyConfig keyConfig) {
this.keyManager = keyManager == null ? new EmptyKeyManager() : keyManager;
this.keyConfig = keyConfig;
}
@Override
public String[] getClientAliases(String s, Principal[] principals) {
return keyManager.getClientAliases(s, principals);
}
@Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return keyManager.chooseClientAlias(strings, principals, socket);
}
@Override
public String[] getServerAliases(String s, Principal[] principals) {
return keyManager.getServerAliases(s, principals);
}
@Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return keyManager.chooseServerAlias(s, principals, socket);
}
@Override
public X509Certificate[] getCertificateChain(String s) {
return keyManager.getCertificateChain(s);
}
@Override
public PrivateKey getPrivateKey(String s) {
return keyManager.getPrivateKey(s);
}
@Override
public String chooseEngineClientAlias(String[] strings, Principal[] principals, SSLEngine engine) {
return keyManager.chooseEngineClientAlias(strings, principals, engine);
}
@Override
public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine engine) {
return keyManager.chooseEngineServerAlias(s, principals, engine);
}
void reload() {
X509ExtendedKeyManager loadedKeyManager = keyConfig.createKeyManager(env);
if (loadedKeyManager == null) {
this.keyManager = new EmptyKeyManager();
} else {
this.keyManager = loadedKeyManager;
}
}
// pkg-private accessor for testing
X509ExtendedKeyManager getKeyManager() {
return keyManager;
}
}
/**
* A struct for holding the SSLContext and the backing key manager and trust manager
*/
static final class SSLContextHolder {
private final SSLContext context;
private final ReloadableTrustManager trustManager;
private final ReloadableX509KeyManager keyManager;
SSLContextHolder(SSLContext context, ReloadableTrustManager trustManager, ReloadableX509KeyManager keyManager) {
this.context = context;
this.trustManager = trustManager;
this.keyManager = keyManager;
}
SSLContext sslContext() {
return context;
}
ReloadableX509KeyManager keyManager() {
return keyManager;
}
ReloadableTrustManager trustManager() {
return trustManager;
}
synchronized void reload() {
trustManager.reload();
keyManager.reload();
invalidateSessions(context.getClientSessionContext());
invalidateSessions(context.getServerSessionContext());
}
/**
* Invalidates the sessions in the provided {@link SSLSessionContext}
*/
private static void invalidateSessions(SSLSessionContext sslSessionContext) {
Enumeration<byte[]> sessionIds = sslSessionContext.getIds();
while (sessionIds.hasMoreElements()) {
byte[] sessionId = sessionIds.nextElement();
sslSessionContext.getSession(sessionId).invalidate();
}
}
}
/**
* This is an empty key manager that is used in case a loaded key manager is null
*/
private static final class EmptyKeyManager extends X509ExtendedKeyManager {
@Override
public String[] getClientAliases(String s, Principal[] principals) {
return new String[0];
}
@Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return null;
}
@Override
public String[] getServerAliases(String s, Principal[] principals) {
return new String[0];
}
@Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return null;
}
@Override
public X509Certificate[] getCertificateChain(String s) {
return new X509Certificate[0];
}
@Override
public PrivateKey getPrivateKey(String s) {
return null;
}
}
/**
* This is an empty trust manager that is used in case a loaded trust manager is null
*/
private static final class EmptyX509TrustManager extends X509ExtendedTrustManager {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
public static void addSettings(List<Setting<?>> settings) {
settings.add(Global.CIPHERS_SETTING);
settings.add(Global.SUPPORTED_PROTOCOLS_SETTING);
settings.add(Global.KEYSTORE_PATH_SETTING);
settings.add(Global.KEYSTORE_PASSWORD_SETTING);
settings.add(Global.KEYSTORE_ALGORITHM_SETTING);
settings.add(Global.KEYSTORE_KEY_PASSWORD_SETTING);
settings.add(Global.KEY_PATH_SETTING);
settings.add(Global.KEY_PASSWORD_SETTING);
settings.add(Global.CERT_SETTING);
settings.add(Global.TRUSTSTORE_PATH_SETTING);
settings.add(Global.TRUSTSTORE_PASSWORD_SETTING);
settings.add(Global.TRUSTSTORE_ALGORITHM_SETTING);
settings.add(Global.PROTOCOL_SETTING);
settings.add(Global.SESSION_CACHE_SIZE_SETTING);
settings.add(Global.SESSION_CACHE_TIMEOUT_SETTING);
settings.add(Global.CA_PATHS_SETTING);
settings.add(Global.INCLUDE_JDK_CERTS_SETTING);
}
private static List<Settings> getRealmsSSLSettings(Settings settings) {
List<Settings> sslSettings = new ArrayList<>();
Settings realmsSettings = REALMS_GROUPS_SETTINGS.get(settings);
for (String name : realmsSettings.names()) {
Settings realmSSLSettings = realmsSettings.getAsSettings(name).getByPrefix("ssl.");
if (realmSSLSettings.isEmpty() == false) {
sslSettings.add(realmSSLSettings);
}
}
return sslSettings;
}
private static List<Settings> getTransportSSLSettings(Settings settings) {
List<Settings> sslSettings = new ArrayList<>();
Map<String, Settings> profiles = TransportSettings.TRANSPORT_PROFILES_SETTING.get(settings).getAsGroups(true);
for (Entry<String, Settings> entry : profiles.entrySet()) {
Settings profileSettings = entry.getValue();
final boolean profileSsl = SecurityNetty4Transport.profileSSL(profileSettings, SSL_SETTING.get(settings));
if (profileSsl && profileSettings.isEmpty() == false) {
sslSettings.add(profileSettings.getByPrefix(settingPrefix()));
}
}
return sslSettings;
}
private static List<Settings> getHttpSSLSettings(Settings settings) {
if (SecurityNetty4HttpServerTransport.SSL_SETTING.get(settings)) {
return Collections.singletonList(settings.getByPrefix(setting("http.ssl.")));
}
return Collections.emptyList();
}
}

View File

@ -1,26 +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.xpack.security.ssl;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
public class ServerSSLService extends AbstractSSLService {
public ServerSSLService(Settings settings, Environment environment, Global globalSSLConfiguration) {
super(settings, environment, globalSSLConfiguration);
}
@Override
protected void validateSSLConfiguration(SSLConfiguration sslConfiguration) {
if (sslConfiguration.keyConfig() == KeyConfig.NONE) {
throw new IllegalArgumentException("a key must be configured to act as a server");
}
sslConfiguration.keyConfig().validate();
sslConfiguration.trustConfig().validate();
}
}

View File

@ -37,7 +37,7 @@ class StoreKeyConfig extends KeyConfig {
}
@Override
X509ExtendedKeyManager loadKeyManager(@Nullable Environment environment) {
X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) {
try (InputStream in = Files.newInputStream(CertUtils.resolvePath(keyStorePath, environment))) {
// TODO remove reliance on JKS since we can PKCS12 stores...
KeyStore ks = KeyStore.getInstance("jks");

View File

@ -10,10 +10,7 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.Collections;
import java.util.List;

View File

@ -24,34 +24,18 @@ abstract class TrustConfig {
protected final boolean includeSystem;
X509ExtendedTrustManager[] trustManagers = null;
TrustConfig(boolean includeSystem) {
this.includeSystem = includeSystem;
}
final synchronized X509ExtendedTrustManager[] trustManagers(@Nullable Environment environment) {
if (trustManagers == null) {
X509ExtendedTrustManager loadedTrustManager = loadAndMergeIfNecessary(environment);
setTrustManagers(loadedTrustManager);
}
return trustManagers;
}
synchronized void reload(@Nullable Environment environment) {
X509ExtendedTrustManager loadedTrustManager = loadAndMergeIfNecessary(environment);
setTrustManagers(loadedTrustManager);
}
final synchronized void setTrustManagers(X509ExtendedTrustManager loadedTrustManager) {
if (loadedTrustManager == null) {
this.trustManagers = new X509ExtendedTrustManager[0];
} else if (this.trustManagers == null || this.trustManagers.length == 0) {
this.trustManagers = new X509ExtendedTrustManager[] { new ReloadableTrustManager(loadedTrustManager) };
} else {
assert this.trustManagers[0] instanceof ReloadableTrustManager;
((ReloadableTrustManager)this.trustManagers[0]).setTrustManager(loadedTrustManager);
final X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
X509ExtendedTrustManager trustManager = nonSystemTrustManager(environment);
if (includeSystem) {
trustManager = mergeWithSystem(trustManager);
} else if (trustManager == null) {
return null;
}
return trustManager;
}
abstract X509ExtendedTrustManager nonSystemTrustManager(@Nullable Environment environment);
@ -62,16 +46,6 @@ abstract class TrustConfig {
public abstract String toString();
final X509ExtendedTrustManager loadAndMergeIfNecessary(@Nullable Environment environment) {
X509ExtendedTrustManager trustManager = nonSystemTrustManager(environment);
if (includeSystem) {
trustManager = mergeWithSystem(trustManager);
} else if (trustManager == null) {
return null;
}
return trustManager;
}
private X509ExtendedTrustManager mergeWithSystem(X509ExtendedTrustManager nonSystemTrustManager) {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
@ -179,56 +153,4 @@ abstract class TrustConfig {
return acceptedIssuers;
}
}
final class ReloadableTrustManager extends X509ExtendedTrustManager {
private volatile X509ExtendedTrustManager trustManager;
ReloadableTrustManager(X509ExtendedTrustManager trustManager) {
this.trustManager = trustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s, socket);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s, socket);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s, sslEngine);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s, sslEngine);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
trustManager.checkClientTrusted(x509Certificates, s);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
trustManager.checkServerTrusted(x509Certificates, s);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
synchronized void setTrustManager(X509ExtendedTrustManager trustManager) {
this.trustManager = trustManager;
}
X509ExtendedTrustManager getTrustManager() {
return trustManager;
}
}
}

View File

@ -58,7 +58,7 @@ public enum SSLClientAuth {
case "true":
return REQUIRED;
default:
throw new IllegalArgumentException("could not resolve ssl client auth auth. unknown ssl client auth value [" + value + "]");
throw new IllegalArgumentException("could not resolve ssl client auth. unknown ssl client auth value [" + value + "]");
}
}
}

View File

@ -12,7 +12,7 @@ import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.http.netty3.Netty3HttpServerTransport;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.threadpool.ThreadPool;
@ -47,13 +47,13 @@ public class SecurityNetty3HttpServerTransport extends Netty3HttpServerTransport
new Setting<>(setting("http.ssl.client.auth"), CLIENT_AUTH_DEFAULT, SSLClientAuth::parse, Property.NodeScope);
private final IPFilter ipFilter;
private final ServerSSLService sslService;
private final SSLService sslService;
private final boolean ssl;
private final Settings sslSettings;
@Inject
public SecurityNetty3HttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, IPFilter ipFilter,
ServerSSLService sslService, ThreadPool threadPool) {
SSLService sslService, ThreadPool threadPool) {
super(settings, networkService, bigArrays, threadPool);
this.ipFilter = ipFilter;
this.ssl = SSL_SETTING.get(settings);
@ -109,6 +109,9 @@ public class SecurityNetty3HttpServerTransport extends Netty3HttpServerTransport
public HttpSslChannelPipelineFactory(Netty3HttpServerTransport transport) {
super(transport, detailedErrorsEnabled, threadPool.getThreadContext());
clientAuth = CLIENT_AUTH_SETTING.get(settings);
if (ssl && sslService.isConfigurationValidForServerUsage(sslSettings) == false) {
throw new IllegalArgumentException("a key must be provided to run as a server");
}
}
@Override

View File

@ -15,8 +15,7 @@ import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.threadpool.ThreadPool;
@ -91,21 +90,18 @@ public class SecurityNetty3Transport extends Netty3Transport {
SSLClientAuth::parse,
new Property[]{Property.NodeScope, Property.Filtered, Property.Shared});
private final ServerSSLService serverSslService;
private final ClientSSLService clientSSLService;
private final SSLService sslService;
@Nullable private final IPFilter authenticator;
private final boolean ssl;
@Inject
public SecurityNetty3Transport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays,
@Nullable IPFilter authenticator, @Nullable ServerSSLService serverSSLService,
ClientSSLService clientSSLService, NamedWriteableRegistry namedWriteableRegistry,
@Nullable IPFilter authenticator, SSLService sslService, NamedWriteableRegistry namedWriteableRegistry,
CircuitBreakerService circuitBreakerService) {
super(settings, threadPool, networkService, bigArrays, namedWriteableRegistry, circuitBreakerService);
this.authenticator = authenticator;
this.ssl = SSL_SETTING.get(settings);
this.serverSslService = serverSSLService;
this.clientSSLService = clientSSLService;
this.sslService = sslService;
}
@Override
@ -161,28 +157,27 @@ public class SecurityNetty3Transport extends Netty3Transport {
private class SslServerChannelPipelineFactory extends ServerChannelPipelineFactory {
private final Settings profileSettings;
private final boolean sslEnabled;
private final Settings securityProfileSettings;
private final SSLClientAuth sslClientAuth;
public SslServerChannelPipelineFactory(Netty3Transport nettyTransport, String name, Settings settings, Settings profileSettings) {
super(nettyTransport, name, settings);
this.profileSettings = profileSettings;
this.sslEnabled = profileSsl(profileSettings, settings);
this.securityProfileSettings = profileSettings.getByPrefix(settingPrefix());
this.sslClientAuth = PROFILE_CLIENT_AUTH_SETTING.get(profileSettings, settings);
if (sslEnabled && sslService.isConfigurationValidForServerUsage(securityProfileSettings) == false) {
throw new IllegalArgumentException("a key must be provided to run as a server");
}
}
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = super.getPipeline();
final boolean profileSsl = profileSsl(profileSettings, settings);
final SSLClientAuth clientAuth = PROFILE_CLIENT_AUTH_SETTING.get(profileSettings, settings);
if (profileSsl) {
SSLEngine serverEngine;
Settings securityProfileSettings = profileSettings.getByPrefix(settingPrefix());
if (securityProfileSettings.names().isEmpty() == false) {
serverEngine = serverSslService.createSSLEngine(securityProfileSettings);
} else {
serverEngine = serverSslService.createSSLEngine();
}
if (sslEnabled) {
SSLEngine serverEngine = sslService.createSSLEngine(securityProfileSettings);
serverEngine.setUseClientMode(false);
clientAuth.configure(serverEngine);
sslClientAuth.configure(serverEngine);
pipeline.addFirst("ssl", new SslHandler(serverEngine));
}
@ -219,7 +214,7 @@ public class SecurityNetty3Transport extends Netty3Transport {
SSLEngine sslEngine;
if (HOSTNAME_VERIFICATION_SETTING.get(settings)) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getValue();
sslEngine = clientSSLService.createSSLEngine(Settings.EMPTY, getHostname(inetSocketAddress),
sslEngine = sslService.createSSLEngine(Settings.EMPTY, getHostname(inetSocketAddress),
inetSocketAddress.getPort());
// By default, a SSLEngine will not perform hostname verification. In order to perform hostname verification
@ -229,7 +224,7 @@ public class SecurityNetty3Transport extends Netty3Transport {
parameters.setEndpointIdentificationAlgorithm("HTTPS");
sslEngine.setSSLParameters(parameters);
} else {
sslEngine = clientSSLService.createSSLEngine();
sslEngine = sslService.createSSLEngine(Settings.EMPTY);
}
sslEngine.setUseClientMode(true);

View File

@ -8,7 +8,6 @@ package org.elasticsearch.xpack.security.transport.netty4;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslHandler;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.network.NetworkService;
@ -18,7 +17,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.http.netty4.Netty4HttpServerTransport;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@ -44,13 +43,13 @@ public class SecurityNetty4HttpServerTransport extends Netty4HttpServerTransport
new Setting<>(setting("http.ssl.client.auth"), CLIENT_AUTH_DEFAULT, SSLClientAuth::parse, Property.NodeScope);
private final IPFilter ipFilter;
private final ServerSSLService sslService;
private final SSLService sslService;
private final boolean ssl;
private final Settings sslSettings;
@Inject
public SecurityNetty4HttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, IPFilter ipFilter,
ServerSSLService sslService, ThreadPool threadPool) {
SSLService sslService, ThreadPool threadPool) {
super(settings, networkService, bigArrays, threadPool);
this.ipFilter = ipFilter;
this.ssl = SSL_SETTING.get(settings);
@ -105,6 +104,9 @@ public class SecurityNetty4HttpServerTransport extends Netty4HttpServerTransport
HttpSslChannelHandler(Netty4HttpServerTransport transport) {
super(transport, detailedErrorsEnabled, threadPool.getThreadContext());
clientAuth = CLIENT_AUTH_SETTING.get(settings);
if (ssl && sslService.isConfigurationValidForServerUsage(sslSettings) == false) {
throw new IllegalArgumentException("a key must be provided to run as a server");
}
}
@Override
@ -132,5 +134,4 @@ public class SecurityNetty4HttpServerTransport extends Netty4HttpServerTransport
settingsBuilder.put(SETTING_HTTP_COMPRESSION.getKey(), false);
}
}
}

View File

@ -26,8 +26,7 @@ import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.netty4.Netty4Transport;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@ -98,8 +97,7 @@ public class SecurityNetty4Transport extends Netty4Transport {
SSLClientAuth::parse,
new Property[]{Property.NodeScope, Property.Filtered, Property.Shared});
private final ServerSSLService serverSslService;
private final ClientSSLService clientSSLService;
private final SSLService sslService;
@Nullable private final IPFilter authenticator;
private final SSLClientAuth clientAuth;
private final boolean ssl;
@ -107,14 +105,12 @@ public class SecurityNetty4Transport extends Netty4Transport {
@Inject
public SecurityNetty4Transport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays,
NamedWriteableRegistry namedWriteableRegistry, CircuitBreakerService circuitBreakerService,
@Nullable IPFilter authenticator, @Nullable ServerSSLService serverSSLService,
ClientSSLService clientSSLService) {
@Nullable IPFilter authenticator, SSLService sslService) {
super(settings, threadPool, networkService, bigArrays, namedWriteableRegistry, circuitBreakerService);
this.authenticator = authenticator;
this.ssl = SSL_SETTING.get(settings);
this.clientAuth = CLIENT_AUTH_SETTING.get(settings);
this.serverSslService = serverSSLService;
this.clientSSLService = clientSSLService;
this.sslService = sslService;
}
@Override
@ -160,18 +156,22 @@ public class SecurityNetty4Transport extends Netty4Transport {
class SecurityServerChannelInitializer extends ServerChannelInitializer {
private final boolean sslEnabled;
private final Settings securityProfileSettings;
protected SecurityServerChannelInitializer(String name, Settings settings) {
super(name, settings);
this.sslEnabled = profileSSL(settings, ssl);
this.securityProfileSettings = settings.getByPrefix(settingPrefix());
if (sslEnabled && sslService.isConfigurationValidForServerUsage(securityProfileSettings) == false) {
throw new IllegalArgumentException("a key must be provided to run as a server");
}
}
@Override
protected void initChannel(Channel ch) throws Exception {
super.initChannel(ch);
if (sslEnabled) {
Settings securityProfileSettings = settings.getByPrefix(settingPrefix());
SSLEngine serverEngine = serverSslService.createSSLEngine(securityProfileSettings);
SSLEngine serverEngine = sslService.createSSLEngine(securityProfileSettings);
serverEngine.setUseClientMode(false);
final SSLClientAuth profileClientAuth = profileClientAuth(settings, clientAuth);
profileClientAuth.configure(serverEngine);
@ -201,7 +201,7 @@ public class SecurityNetty4Transport extends Netty4Transport {
final SSLEngine sslEngine;
if (HOSTNAME_VERIFICATION_SETTING.get(settings)) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) remoteAddress;
sslEngine = clientSSLService.createSSLEngine(Settings.EMPTY, getHostname(inetSocketAddress), inetSocketAddress.getPort());
sslEngine = sslService.createSSLEngine(Settings.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
@ -210,7 +210,7 @@ public class SecurityNetty4Transport extends Netty4Transport {
parameters.setEndpointIdentificationAlgorithm("HTTPS");
sslEngine.setSSLParameters(parameters);
} else {
sslEngine = clientSSLService.createSSLEngine();
sslEngine = sslService.createSSLEngine(Settings.EMPTY);
}
sslEngine.setUseClientMode(true);
@ -235,7 +235,7 @@ public class SecurityNetty4Transport extends Netty4Transport {
}
}
static boolean profileSSL(Settings profileSettings, boolean defaultSSL) {
public static boolean profileSSL(Settings profileSettings, boolean defaultSSL) {
if (PROFILE_SSL_SETTING.exists(profileSettings)) {
return PROFILE_SSL_SETTING.get(profileSettings);
} else if (DEPRECATED_PROFILE_SSL_SETTING.exists(profileSettings)) {
@ -251,5 +251,4 @@ public class SecurityNetty4Transport extends Netty4Transport {
}
return clientAuth;
}
}

View File

@ -11,7 +11,6 @@ import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsFilter;
import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration;
import org.elasticsearch.xpack.XPackPlugin;
import org.hamcrest.Matcher;
@ -56,7 +55,7 @@ public class SettingsFilterTests extends ESTestCase {
configureFilteredSetting("xpack.security.ssl.keystore.path", "/path/to/keystore");
configureFilteredSetting("xpack.security.ssl.ciphers", "_ciphers");
configureFilteredSetting("xpack.security.ssl.supported_protocols", randomFrom(SSLConfiguration.Global.DEFAULT_SUPPORTED_PROTOCOLS));
configureFilteredSetting("xpack.security.ssl.supported_protocols", randomFrom("TLSv1", "TLSv1.1", "TLSv1.2"));
configureFilteredSetting("xpack.security.ssl.keystore.password", randomAsciiOfLength(5));
configureFilteredSetting("xpack.security.ssl.keystore.algorithm", "_algorithm");
configureFilteredSetting("xpack.security.ssl.keystore.key_password", randomAsciiOfLength(5));
@ -68,7 +67,7 @@ public class SettingsFilterTests extends ESTestCase {
configureFilteredSetting("transport.profiles.client.xpack.security.keystore.path", "/path/to/keystore");
configureFilteredSetting("transport.profiles.client.xpack.security.ciphers", "_ciphers");
configureFilteredSetting("transport.profiles.client.xpack.security.supported_protocols",
randomFrom(SSLConfiguration.Global.DEFAULT_SUPPORTED_PROTOCOLS));
randomFrom("TLSv1", "TLSv1.1", "TLSv1.2"));
configureFilteredSetting("transport.profiles.client.xpack.security.keystore.password", randomAsciiOfLength(5));
configureFilteredSetting("transport.profiles.client.xpack.security.keystore.algorithm", "_algorithm");
configureFilteredSetting("transport.profiles.client.xpack.security.keystore.key_password", randomAsciiOfLength(5));

View File

@ -8,10 +8,9 @@ package org.elasticsearch.xpack.security.authc.activedirectory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.junit.Before;
import java.nio.file.Path;
@ -23,7 +22,7 @@ public class AbstractActiveDirectoryIntegTests extends ESTestCase {
public static final String PASSWORD = "NickFuryHeartsES";
public static final String AD_DOMAIN = "ad.test.elasticsearch.com";
protected ClientSSLService clientSSLService;
protected SSLService sslService;
protected Settings globalSettings;
protected boolean useGlobalSSL;
@ -40,10 +39,14 @@ public class AbstractActiveDirectoryIntegTests extends ESTestCase {
if (useGlobalSSL) {
builder.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit");
} else {
// fake a realm so ssl will get loaded
builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", keystore);
builder.put("xpack.security.authc.realms.foo.ssl.truststore.password", "changeit");
}
globalSettings = builder.build();
Environment environment = new Environment(globalSettings);
clientSSLService = new ClientSSLService(globalSettings, environment, new Global(globalSettings));
sslService = new SSLService(globalSettings, environment);
}
Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN, LdapSearchScope scope,

View File

@ -16,7 +16,6 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
import org.elasticsearch.test.junit.annotations.Network;
import java.io.IOException;
import java.util.List;
import static org.hamcrest.Matchers.anyOf;
@ -31,7 +30,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
@SuppressWarnings("unchecked")
public void testAdAuth() throws Exception {
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false), globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
String userName = "ironman";
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
@ -52,7 +51,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
public void testNetbiosAuth() throws Exception {
final String adUrl = randomFrom("ldap://54.213.145.20:3268", "ldaps://54.213.145.20:3269", AD_LDAP_URL);
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(adUrl, AD_DOMAIN, false), globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
String userName = "ades\\ironman";
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
@ -79,7 +78,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
.put(SessionFactory.TIMEOUT_TCP_READ_SETTING, "1ms")
.build();
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
LDAPException expected = expectThrows(LDAPException.class,
() -> sessionFactory.session("ironman", SecuredStringTests.build(PASSWORD)).groups());
@ -88,7 +87,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
public void testAdAuthAvengers() throws Exception {
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false), globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
String[] users = new String[]{"cap", "hawkeye", "hulk", "ironman", "thor", "blackwidow", };
for(String user: users) {
@ -103,7 +102,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
LdapSearchScope.ONE_LEVEL, false);
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
String userName = "hulk";
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
@ -125,7 +124,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Bruce Banner, CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
LdapSearchScope.BASE, false);
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
String userName = "hulk";
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
@ -151,7 +150,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
.put(ActiveDirectorySessionFactory.AD_GROUP_SEARCH_SCOPE_SETTING, LdapSearchScope.BASE)
.build();
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
String userName = "hulk";
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
@ -166,7 +165,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
LdapSearchScope.ONE_LEVEL, false);
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
//Login with the UserPrincipalName
String userDN = "CN=Erik Selvig,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
@ -184,7 +183,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
LdapSearchScope.ONE_LEVEL, false);
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
//login with sAMAccountName
String userDN = "CN=Erik Selvig,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
@ -208,7 +207,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
"(&(objectclass=user)(userPrincipalName={0}@ad.test.elasticsearch.com))")
.build();
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
//Login with the UserPrincipalName
try (LdapSession ldap = sessionFactory.session("erik.selvig", SecuredStringTests.build(PASSWORD))) {
@ -234,7 +233,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
.build();
}
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
String user = "Bruce Banner";
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
@ -260,7 +259,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
.build();
}
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
String user = "Bruce Banner";
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
@ -276,7 +275,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
public void testAdAuthWithHostnameVerification() throws Exception {
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, true), globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
String userName = "ironman";
LDAPException expected =
@ -292,7 +291,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
.put(LdapSessionFactory.HOSTNAME_VERIFICATION_SETTING, true)
.build();
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
String user = "Bruce Banner";
LDAPException expected = expectThrows(LDAPException.class, () -> sessionFactory.session(user, SecuredStringTests.build(PASSWORD)));

View File

@ -11,9 +11,8 @@ import com.unboundid.ldap.sdk.LDAPURL;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.junit.After;
import org.junit.Before;
@ -37,10 +36,14 @@ public abstract class GroupsResolverTestCase extends ESTestCase {
if (useGlobalSSL) {
builder.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit");
} else {
// fake a realm so ssl will get loaded
builder.put("xpack.security.authc.realms.foo.ssl.keystore.path", keystore);
builder.put("xpack.security.authc.realms.foo.ssl.keystore.password", "changeit");
}
Settings settings = builder.build();
Environment env = new Environment(settings);
ClientSSLService clientSSLService = new ClientSSLService(settings, env, new Global(settings));
SSLService sslService = new SSLService(settings, env);
LDAPURL ldapurl = new LDAPURL(ldapUrl());
LDAPConnectionOptions options = new LDAPConnectionOptions();
@ -57,7 +60,7 @@ public abstract class GroupsResolverTestCase extends ESTestCase {
.put("keystore.password", "changeit").build();
}
ldapConnection = new LDAPConnection(clientSSLService.sslSocketFactory(connectionSettings), options, ldapurl.getHost(),
ldapConnection = new LDAPConnection(sslService.sslSocketFactory(connectionSettings), options, ldapurl.getHost(),
ldapurl.getPort(), bindDN(), bindPassword());
}

View File

@ -25,8 +25,7 @@ import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapTestCase;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.support.NoOpLogger;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.Before;
@ -46,7 +45,7 @@ import static org.hamcrest.Matchers.nullValue;
public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
private ClientSSLService clientSSLService;
private SSLService sslService;
private Settings globalSettings;
@Before
@ -58,13 +57,12 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
* 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.
*/
Settings settings = Settings.builder()
globalSettings = Settings.builder()
.put("path.home", createTempDir())
.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit")
.build();
clientSSLService = new ClientSSLService(settings, env, new Global(settings));
globalSettings = Settings.builder().put("path.home", createTempDir()).build();
sslService = new SSLService(globalSettings, env);
}
public void testSupportsUnauthenticatedSessions() throws Exception {
@ -359,7 +357,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
.put("user_search.pool.enabled", randomBoolean())
.build();
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService);
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, sslService);
String user = "Bruce Banner";
try {
@ -401,7 +399,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
.put("bind_password", OpenLdapTests.PASSWORD)
.put("user_search.pool.enabled", randomBoolean())
.build(), globalSettings);
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService);
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, sslService);
String[] users = new String[] { "cap", "hawkeye", "hulk", "ironman", "thor" };
try {

View File

@ -14,10 +14,9 @@ import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapTestCase;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.junit.Before;
import java.nio.file.Path;
@ -33,7 +32,7 @@ public class OpenLdapTests extends ESTestCase {
public static final String PASSWORD = "NickFuryHeartsES";
private boolean useGlobalSSL;
private ClientSSLService clientSSLService;
private SSLService sslService;
private Settings globalSettings;
@Before
@ -49,10 +48,14 @@ public class OpenLdapTests extends ESTestCase {
if (useGlobalSSL) {
builder.put("xpack.security.ssl.keystore.path", keystore)
.put("xpack.security.ssl.keystore.password", "changeit");
} else {
// fake a realm so ssl will get loaded
builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", keystore);
builder.put("xpack.security.authc.realms.foo.ssl.truststore.password", "changeit");
}
globalSettings = builder.build();
Environment environment = new Environment(globalSettings);
clientSSLService = new ClientSSLService(globalSettings, environment, new Global(globalSettings));
sslService = new SSLService(globalSettings, environment);
}
public void testConnect() throws Exception {
@ -61,7 +64,7 @@ public class OpenLdapTests extends ESTestCase {
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
LdapSearchScope.ONE_LEVEL), globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
String[] users = new String[] { "blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor" };
for (String user : users) {
@ -78,7 +81,7 @@ public class OpenLdapTests extends ESTestCase {
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
LdapSearchScope.BASE), globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
String[] users = new String[] { "blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor" };
for (String user : users) {
@ -97,7 +100,7 @@ public class OpenLdapTests extends ESTestCase {
.put("group_search.user_attribute", "uid")
.build();
RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
try (LdapSession ldap = sessionFactory.session("selvig", SecuredStringTests.build(PASSWORD))){
assertThat(ldap.groups(), hasItem(containsString("Geniuses")));
@ -115,7 +118,7 @@ public class OpenLdapTests extends ESTestCase {
.put(SessionFactory.TIMEOUT_TCP_READ_SETTING, "1ms") //1 millisecond
.build();
RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
LDAPException expected = expectThrows(LDAPException.class,
() -> sessionFactory.session("thor", SecuredStringTests.build(PASSWORD)).groups());
@ -132,7 +135,7 @@ public class OpenLdapTests extends ESTestCase {
.build();
RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService);
String user = "blackwidow";
LDAPException expected = expectThrows(LDAPException.class, () -> sessionFactory.session(user, SecuredStringTests.build(PASSWORD)));

View File

@ -10,7 +10,7 @@ import com.unboundid.ldap.sdk.LDAPConnection;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import java.util.ArrayList;
import java.util.Arrays;
@ -171,7 +171,7 @@ public class SessionFactoryLoadBalancingTests extends LdapTestCase {
static class TestSessionFactory extends SessionFactory {
protected TestSessionFactory(RealmConfig config, ClientSSLService sslService) {
protected TestSessionFactory(RealmConfig config, SSLService sslService) {
super(config, sslService);
}

View File

@ -14,14 +14,10 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.ssl.KeyConfig.ReloadableX509KeyManager;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.TrustConfig.ReloadableTrustManager;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
@ -44,7 +40,6 @@ import java.util.function.BiFunction;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
import static org.hamcrest.core.Is.is;
@ -301,40 +296,24 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = randomBoolean() ? null : new Environment(settings);
final Global config = new Global(settings);
final ServerSSLService serverSSLService = new ServerSSLService(settings, env, config) {
final SSLService sslService = new SSLService(settings, env);
final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY);
new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [keystore reload exception]");
return super.getSSLContext(configuration);
void reloadSSLContext(SSLConfiguration configuration) {
fail("reload should not be called! [keystore reload exception]");
}
};
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, config) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [keystore reload exception]");
return super.getSSLContext(configuration);
}
};
final SSLConfigurationReloader reloader =
new SSLConfigurationReloader(settings, env, serverSSLService, clientSSLService, resourceWatcherService);
reloader.onSSLContextLoaded(config);
// key manager checks
X509ExtendedKeyManager keyManager = ((ReloadableX509KeyManager)config.keyConfig().keyManagers(env)[0]).getKeyManager();
String[] aliases = keyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("testnode"));
PrivateKey privateKey = keyManager.getPrivateKey("testnode");
final X509ExtendedKeyManager keyManager = sslService.sslContextHolder(config).keyManager().getKeyManager();
// truncate the keystore
try (OutputStream out = Files.newOutputStream(keystorePath, StandardOpenOption.TRUNCATE_EXISTING)) {
}
// we intentionally don't wait here as we rely on concurrency to catch a failure
assertThat(keyManager.getServerAliases("RSA", null), equalTo(aliases));
assertThat(keyManager.getPrivateKey("testnode"), is(equalTo(privateKey)));
assertThat(sslService.sslContextHolder(config).keyManager().getKeyManager(), sameInstance(keyManager));
}
/**
@ -357,39 +336,23 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = randomBoolean() ? null : new Environment(settings);
final Global config = new Global(settings);
final ServerSSLService serverSSLService = new ServerSSLService(settings, env, config) {
final SSLService sslService = new SSLService(settings, env);
final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY);
new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [pem key reload exception]");
return super.getSSLContext(configuration);
void reloadSSLContext(SSLConfiguration configuration) {
fail("reload should not be called! [pem key reload exception]");
}
};
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, config) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [pem key reload exception]");
return super.getSSLContext(configuration);
}
};
final SSLConfigurationReloader reloader =
new SSLConfigurationReloader(settings, env, serverSSLService, clientSSLService, resourceWatcherService);
reloader.onSSLContextLoaded(config);
X509ExtendedKeyManager keyManager = ((ReloadableX509KeyManager)config.keyConfig().keyManagers(env)[0]).getKeyManager();
String[] aliases = keyManager.getServerAliases("RSA", null);
assertNotNull(aliases);
assertThat(aliases.length, is(1));
assertThat(aliases[0], is("key"));
PrivateKey privateKey = keyManager.getPrivateKey("key");
final X509ExtendedKeyManager keyManager = sslService.sslContextHolder(config).keyManager().getKeyManager();
// truncate the file
try (OutputStream os = Files.newOutputStream(keyPath, StandardOpenOption.TRUNCATE_EXISTING)) {
}
// we intentionally don't wait here as we rely on concurrency to catch a failure
assertThat(keyManager.getServerAliases("RSA", null), equalTo(aliases));
assertThat(keyManager.getPrivateKey("key"), is(equalTo(privateKey)));
assertThat(sslService.sslContextHolder(config).keyManager().getKeyManager(), sameInstance(keyManager));
}
/**
@ -406,36 +369,23 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = randomBoolean() ? null : new Environment(settings);
final Global config = new Global(settings);
final ServerSSLService serverSSLService = new ServerSSLService(settings, env, config) {
final SSLService sslService = new SSLService(settings, env);
final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY);
new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [truststore reload exception]");
return super.getSSLContext(configuration);
void reloadSSLContext(SSLConfiguration configuration) {
fail("reload should not be called! [truststore reload exception]");
}
};
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, config) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [truststore reload exception]");
return super.getSSLContext(configuration);
}
};
final SSLConfigurationReloader reloader =
new SSLConfigurationReloader(settings, env, serverSSLService, clientSSLService, resourceWatcherService);
reloader.onSSLContextLoaded(config);
X509ExtendedTrustManager trustManager = ((ReloadableTrustManager)config.trustConfig().trustManagers(env)[0]).getTrustManager();
final Certificate[] certificates = trustManager.getAcceptedIssuers();
assertContainsCertificateWithMatchingName(certificates, containsString("Test Node"));
final X509ExtendedTrustManager trustManager = sslService.sslContextHolder(config).trustManager().getTrustManager();
// truncate the truststore
try (OutputStream os = Files.newOutputStream(trustStorePath, StandardOpenOption.TRUNCATE_EXISTING)) {
}
// we intentionally don't wait here as we rely on concurrency to catch a failure
assertThat(trustManager.getAcceptedIssuers(), equalTo(certificates));
assertContainsCertificateWithMatchingName(trustManager.getAcceptedIssuers(), containsString("Test Node"));
assertThat(sslService.sslContextHolder(config).trustManager().getTrustManager(), sameInstance(trustManager));
}
/**
@ -451,28 +401,16 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
.put("path.home", createTempDir())
.build();
Environment env = randomBoolean() ? null : new Environment(settings);
final Global config = new Global(settings);
final ServerSSLService serverSSLService = new ServerSSLService(settings, env, config) {
final SSLService sslService = new SSLService(settings, env);
final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY);
new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [pem trust reload exception]");
return super.getSSLContext(configuration);
void reloadSSLContext(SSLConfiguration configuration) {
fail("reload should not be called! [pem trust reload exception]");
}
};
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, config) {
@Override
SSLContext getSSLContext(SSLConfiguration configuration) {
fail("get should not be called! [pem trust reload exception]");
return super.getSSLContext(configuration);
}
};
final SSLConfigurationReloader reloader =
new SSLConfigurationReloader(settings, env, serverSSLService, clientSSLService, resourceWatcherService);
reloader.onSSLContextLoaded(config);
X509ExtendedTrustManager trustManager = ((ReloadableTrustManager)config.trustConfig().trustManagers(env)[0]).getTrustManager();
final Certificate[] certificates = trustManager.getAcceptedIssuers();
assertContainsCertificateWithMatchingName(certificates, containsString("Test Client"));
final X509ExtendedTrustManager trustManager = sslService.sslContextHolder(config).trustManager().getTrustManager();
// write bad file
Path updatedCert = tempDir.resolve("updated.crt");
@ -482,8 +420,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
atomicMoveIfPossible(updatedCert, clientCertPath);
// we intentionally don't wait here as we rely on concurrency to catch a failure
assertThat(trustManager.getAcceptedIssuers(), equalTo(certificates));
assertContainsCertificateWithMatchingName(trustManager.getAcceptedIssuers(), containsString("Test Client"));
assertThat(sslService.sslContextHolder(config).trustManager().getTrustManager(), sameInstance(trustManager));
}
/**
@ -532,116 +469,62 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
BiFunction<X509ExtendedTrustManager, SSLConfiguration, Void> trustManagerPostChecks)
throws Exception {
final AtomicInteger serverCounter = new AtomicInteger(0);
final AtomicInteger clientCounter = new AtomicInteger(0);
final Global config = new Global(settings);
final ServerSSLService serverSSLService = new ServerSSLService(settings, env, config) {
final AtomicInteger counter = new AtomicInteger(0);
final SSLService sslService = new SSLService(settings, env);
final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY);
new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) {
@Override
SSLContext getSSLContext(SSLConfiguration sslConfiguration) {
serverCounter.incrementAndGet();
return super.getSSLContext(sslConfiguration);
void reloadSSLContext(SSLConfiguration configuration) {
counter.incrementAndGet();
super.reloadSSLContext(configuration);
}
};
final ClientSSLService clientSSLService = new ClientSSLService(settings, env, config) {
@Override
SSLContext getSSLContext(SSLConfiguration sslConfiguration) {
clientCounter.incrementAndGet();
return super.getSSLContext(sslConfiguration);
}
};
final SSLConfigurationReloader reloader =
new SSLConfigurationReloader(settings, env, serverSSLService, clientSSLService, resourceWatcherService);
final X509ExtendedKeyManager keyManager;
final X509ExtendedKeyManager[] originalKeyManagers;
if (checkKeys) {
originalKeyManagers = config.keyConfig().keyManagers(env);
assertThat(originalKeyManagers.length, is(1));
keyManager = ((ReloadableX509KeyManager) originalKeyManagers[0]).getKeyManager();
keyManager = sslService.sslContextHolder(config).keyManager().getKeyManager();
} else {
originalKeyManagers = null;
keyManager = null;
}
final X509ExtendedTrustManager[] originalTrustManagers;
final X509ExtendedTrustManager trustManager;
if (checkTrust) {
originalTrustManagers = config.trustConfig().trustManagers(env);
assertThat(originalTrustManagers.length, is(1));
trustManager = ((ReloadableTrustManager) originalTrustManagers[0]).getTrustManager();
trustManager = sslService.sslContextHolder(config).trustManager().getTrustManager();
} else {
originalTrustManagers = null;
trustManager = null;
}
// register configuration with reloader
reloader.onSSLContextLoaded(config);
// key manager checks
if (checkKeys) {
assertKeyManagersSame(keyManager, config.keyConfig().keyManagers(env));
keyManagerPreChecks.apply(keyManager, config);
}
// trust manager checks
if (checkTrust) {
assertTrustManagersSame(trustManager, config.trustConfig().trustManagers(env));
trustManagerPreChecks.apply(trustManager, config);
}
assertEquals(0, clientSSLService.getLoadedSSLConfigurations().size());
assertEquals(0, serverSSLService.getLoadedSSLConfigurations().size());
assertEquals("nothing should have called get", 0, clientCounter.get());
assertEquals("nothing should have called get", 0, serverCounter.get());
assertEquals("nothing should have called get", 0, counter.get());
// modify
modificationFunction.run();
assertTrue(awaitBusy(() -> clientCounter.get() > 0 && serverCounter.get() > 0));
assertTrue(awaitBusy(() -> counter.get() > 0));
// check key manager
if (checkKeys) {
final X509ExtendedKeyManager[] updatedKeyManagers = config.keyConfig().keyManagers(env);
assertThat(updatedKeyManagers, sameInstance(originalKeyManagers));
final X509ExtendedKeyManager updatedKeyManager = ((ReloadableX509KeyManager) updatedKeyManagers[0]).getKeyManager();
final X509ExtendedKeyManager updatedKeyManager = sslService.sslContextHolder(config).keyManager().getKeyManager();
assertThat(updatedKeyManager, not(sameInstance(keyManager)));
keyManagerPostChecks.apply(updatedKeyManager, config);
}
// check trust manager
if (checkTrust) {
final X509ExtendedTrustManager[] updatedTrustManagers = config.trustConfig().trustManagers(env);
assertThat(updatedTrustManagers, sameInstance(originalTrustManagers));
final X509ExtendedTrustManager updatedTrustManager = ((ReloadableTrustManager) updatedTrustManagers[0]).getTrustManager();
final X509ExtendedTrustManager updatedTrustManager = sslService.sslContextHolder(config).trustManager().getTrustManager();
assertThat(updatedTrustManager, not(sameInstance(trustManager)));
trustManagerPostChecks.apply(updatedTrustManager, config);
}
}
private void assertContainsCertificateWithMatchingName(Certificate[] certificates, Matcher<String> matcher) {
for (Certificate certificate : certificates) {
if (certificate instanceof X509Certificate) {
if (matcher.matches(((X509Certificate) certificate).getSubjectX500Principal().getName())) {
return;
}
}
}
fail("no matching certificate could be found");
}
private void assertKeyManagersSame(X509ExtendedKeyManager original, X509ExtendedKeyManager[] other) {
assertEquals(1, other.length);
assertThat(other[0], instanceOf(ReloadableX509KeyManager.class));
X509ExtendedKeyManager otherKeyManager = ((ReloadableX509KeyManager) other[0]).getKeyManager();
assertThat(otherKeyManager, sameInstance(original));
}
private void assertTrustManagersSame(X509ExtendedTrustManager original, X509ExtendedTrustManager[] other) {
assertEquals(1, other.length);
assertThat(other[0], instanceOf(ReloadableTrustManager.class));
X509ExtendedTrustManager otherTrustManager = ((ReloadableTrustManager) other[0]).getTrustManager();
assertThat(otherTrustManager, sameInstance(original));
}
private static void atomicMoveIfPossible(Path source, Path target) throws IOException {
try {
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);

View File

@ -21,8 +21,6 @@ import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
public class SSLConfigurationTests extends ESTestCase {
@ -44,24 +42,6 @@ public class SSLConfigurationTests extends ESTestCase {
assertThat(globalConfig.protocol(), is(equalTo(Global.DEFAULT_PROTOCOL)));
}
public void testThatSSLConfigurationWithoutAutoGenHasCorrectDefaults() {
SSLConfiguration globalSettings = new Global(Settings.EMPTY);
SSLConfiguration scopedSettings = new Custom(Settings.EMPTY, globalSettings);
for (SSLConfiguration sslConfiguration : Arrays.asList(globalSettings, scopedSettings)) {
assertThat(sslConfiguration.keyConfig(), sameInstance(KeyConfig.NONE));
assertThat(sslConfiguration.sessionCacheSize(), is(equalTo(Global.DEFAULT_SESSION_CACHE_SIZE)));
assertThat(sslConfiguration.sessionCacheTimeout(), is(equalTo(Global.DEFAULT_SESSION_CACHE_TIMEOUT)));
assertThat(sslConfiguration.protocol(), is(equalTo(Global.DEFAULT_PROTOCOL)));
assertThat(sslConfiguration.trustConfig(), notNullValue());
assertThat(sslConfiguration.trustConfig(), is(instanceOf(StoreTrustConfig.class)));
StoreTrustConfig ksTrustInfo = (StoreTrustConfig) sslConfiguration.trustConfig();
assertThat(ksTrustInfo.trustStorePath, is(nullValue()));
assertThat(ksTrustInfo.trustStorePassword, is(nullValue()));
assertThat(ksTrustInfo.trustStoreAlgorithm, is(nullValue()));
}
}
public void testThatOnlyKeystoreInSettingsSetsTruststoreSettings() {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", "path")
@ -246,11 +226,11 @@ public class SSLConfigurationTests extends ESTestCase {
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class));
PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig();
KeyManager[] keyManagers = keyConfig.keyManagers(env);
assertThat(keyManagers.length, is(1));
KeyManager keyManager = keyConfig.createKeyManager(env);
assertNotNull(keyManager);
assertThat(config.trustConfig(), sameInstance(keyConfig));
TrustManager[] trustManagers = keyConfig.trustManagers(env);
assertThat(trustManagers.length, is(1));
TrustManager trustManager = keyConfig.createTrustManager(env);
assertNotNull(trustManager);
}
public void testConfigurationUsingPEMKeyAndTrustFiles() {
@ -269,11 +249,11 @@ public class SSLConfigurationTests extends ESTestCase {
SSLConfiguration config = new Global(settings);
assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class));
PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig();
KeyManager[] keyManagers = keyConfig.keyManagers(env);
assertThat(keyManagers.length, is(1));
KeyManager keyManager = keyConfig.createKeyManager(env);
assertNotNull(keyManager);
assertThat(config.trustConfig(), not(sameInstance(keyConfig)));
assertThat(config.trustConfig(), instanceOf(PEMTrustConfig.class));
TrustManager[] trustManagers = keyConfig.trustManagers(env);
assertThat(trustManagers.length, is(1));
TrustManager trustManager = keyConfig.createTrustManager(env);
assertNotNull(trustManager);
}
}

View File

@ -19,6 +19,7 @@ import org.elasticsearch.common.network.InetAddressHelper;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
@ -104,13 +105,14 @@ public class SSLReloadIntegTests extends SecurityIntegTestCase {
}
Settings settings = Settings.builder()
.put("keystore.path", keystorePath)
.put("keystore.password", "changeme")
.put("truststore.path", nodeStorePath)
.put("truststore.password", "testnode")
.put("path.home", createTempDir())
.put("xpack.security.ssl.keystore.path", keystorePath)
.put("xpack.security.ssl.keystore.password", "changeme")
.put("xpack.security.ssl.truststore.path", nodeStorePath)
.put("xpack.security.ssl.truststore.password", "testnode")
.build();
String node = randomFrom(internalCluster().getNodeNames());
ServerSSLService sslService = internalCluster().getInstance(ServerSSLService.class, node);
SSLService sslService = new SSLService(settings, new Environment(settings));
SSLSocketFactory sslSocketFactory = sslService.sslSocketFactory(settings);
InetSocketTransportAddress address = (InetSocketTransportAddress) internalCluster()
.getInstance(Transport.class, node).boundAddress().publishAddress();
@ -119,6 +121,7 @@ public class SSLReloadIntegTests extends SecurityIntegTestCase {
socket.startHandshake();
fail("handshake should not have been successful!");
} catch (SSLHandshakeException | SocketException expected) {
logger.trace("expected exception", expected);
}
KeyStore nodeStore = KeyStore.getInstance("jks");
@ -139,6 +142,7 @@ public class SSLReloadIntegTests extends SecurityIntegTestCase {
CountDownLatch latch = new CountDownLatch(1);
assertBusy(() -> {
try (SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(address.getAddress(), address.getPort())) {
logger.info("opened socket for reloading [{}]", socket);
socket.addHandshakeCompletedListener(event -> {
try {
assertThat(event.getPeerPrincipal().getName(), containsString("Test Node"));

View File

@ -9,12 +9,13 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.Before;
import javax.net.ssl.SSLContext;
@ -39,28 +40,29 @@ import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
public class ClientSSLServiceTests extends ESTestCase {
public class SSLServiceTests extends ESTestCase {
private Environment env;
private Path testnodeStore;
private Path testclientStore;
private Environment env;
@Before
public void setup() throws Exception {
testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
testclientStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks");
env = randomBoolean() ? new Environment(Settings.builder().put("path.home", createTempDir()).build()) : null;
env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
}
public void testThatInvalidProtocolThrowsException() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.protocol", "non-existing")
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.truststore.path", testnodeStore)
.put("xpack.security.ssl.truststore.password", "testnode")
.build();
try {
Settings settings = Settings.builder()
.put("xpack.security.ssl.protocol", "non-existing")
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.put("xpack.security.ssl.truststore.path", testclientStore)
.put("xpack.security.ssl.truststore.password", "testclient")
.build();
ClientSSLService clientSSLService = new ClientSSLService(settings, null, new Global(settings));
clientSSLService.createSSLEngine();
new SSLService(settings, env);
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize the SSLContext"));
@ -68,29 +70,34 @@ public class ClientSSLServiceTests extends ESTestCase {
}
public void testThatCustomTruststoreCanBeSpecified() throws Exception {
Path testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
Path testClientStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks");
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("transport.profiles.foo.xpack.security.ssl.truststore.path", testClientStore)
.put("transport.profiles.foo.xpack.security.ssl.truststore.password", "testclient")
.build();
SSLService sslService = new SSLService(settings, env);
Settings.Builder settingsBuilder = Settings.builder()
.put("truststore.path", testnodeStore)
.put("truststore.password", "testnode");
Settings customTruststoreSettings = Settings.builder()
.put("ssl.truststore.path", testClientStore)
.put("ssl.truststore.password", "testclient")
.build();
SSLEngine sslEngineWithTruststore = sslService.createSSLEngine(settingsBuilder.build());
SSLEngine sslEngineWithTruststore = sslService.createSSLEngine(customTruststoreSettings);
assertThat(sslEngineWithTruststore, is(not(nullValue())));
SSLEngine sslEngine = sslService.createSSLEngine();
SSLEngine sslEngine = sslService.createSSLEngine(Settings.EMPTY);
assertThat(sslEngineWithTruststore, is(not(sameInstance(sslEngine))));
}
public void testThatSslContextCachingWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
SSLContext sslContext = sslService.sslContext();
SSLContext cachedSslContext = sslService.sslContext();
@ -101,21 +108,23 @@ public class ClientSSLServiceTests extends ESTestCase {
public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
Path differentPasswordsStore =
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-different-passwords.jks");
createClientSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", differentPasswordsStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.keystore.key_password", "testnode1")
.build()).createSSLEngine();
.build();
new SSLService(settings, env).createSSLEngine(Settings.EMPTY);
}
public void testIncorrectKeyPasswordThrowsException() throws Exception {
Path differentPasswordsStore =
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-different-passwords.jks");
try {
createClientSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", differentPasswordsStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build()).createSSLEngine();
.build();
new SSLService(settings, env).createSSLEngine(Settings.EMPTY);
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize a KeyManagerFactory"));
@ -123,63 +132,145 @@ public class ClientSSLServiceTests extends ESTestCase {
}
public void testThatSSLv3IsNotEnabled() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
SSLEngine engine = sslService.createSSLEngine();
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY);
assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3")));
}
public void testThatSSLSessionCacheHasDefaultLimits() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(1000));
assertThat(context.getSessionTimeout(), equalTo((int) TimeValue.timeValueHours(24).seconds()));
}
public void testThatSettingSSLSessionCacheLimitsWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.session.cache_size", "300")
.put("xpack.security.ssl.session.cache_timeout", "600s")
.build());
.build();
SSLService sslService = new SSLService(settings, env);
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(300));
assertThat(context.getSessionTimeout(), equalTo(600));
}
public void testCreateWithoutAnySettingsNotValidForServer() throws Exception {
SSLService sslService = new SSLService(Settings.EMPTY, env);
assertFalse(sslService.isConfigurationValidForServerUsage(Settings.EMPTY));
}
public void testCreateWithOnlyTruststoreNotValidForServer() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path", testnodeStore)
.put("xpack.security.ssl.truststore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
assertFalse(sslService.isConfigurationValidForServerUsage(Settings.EMPTY));
}
public void testCreateWithKeystoreIsValidForServer() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
assertTrue(sslService.isConfigurationValidForServerUsage(Settings.EMPTY));
}
public void testThatCreateClientSSLEngineWithoutAnySettingsWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.EMPTY);
SSLEngine sslEngine = sslService.createSSLEngine();
SSLService sslService = new SSLService(Settings.EMPTY, env);
SSLEngine sslEngine = sslService.createSSLEngine(Settings.EMPTY);
assertThat(sslEngine, notNullValue());
}
public void testThatCreateSSLEngineWithOnlyTruststoreWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path", testclientStore)
.put("xpack.security.ssl.truststore.password", "testclient")
.build());
SSLEngine sslEngine = sslService.createSSLEngine();
.build();
SSLService sslService = new SSLService(settings, env);
SSLEngine sslEngine = sslService.createSSLEngine(Settings.EMPTY);
assertThat(sslEngine, notNullValue());
}
public void testThatCreateSSLEngineWithOnlyKeystoreWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
SSLEngine sslEngine = sslService.createSSLEngine();
assertThat(sslEngine, notNullValue());
public void testThatTruststorePasswordIsRequired() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.truststore.path", testnodeStore)
.build();
IllegalArgumentException e =
expectThrows(IllegalArgumentException.class, () -> new SSLService(settings, env));
assertThat(e.getMessage(), is("no truststore password configured"));
}
public void testThatKeystorePasswordIsRequired() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.build();
IllegalArgumentException e =
expectThrows(IllegalArgumentException.class, () -> new SSLService(settings, env));
assertThat(e.getMessage(), is("no keystore password configured"));
}
public void testCiphersAndInvalidCiphersWork() throws Exception {
List<String> ciphers = new ArrayList<>(Global.DEFAULT_CIPHERS);
ciphers.add("foo");
ciphers.add("bar");
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.putArray("xpack.security.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
.build();
SSLService sslService = new SSLService(settings, env);
SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY);
assertThat(engine, is(notNullValue()));
String[] enabledCiphers = engine.getEnabledCipherSuites();
assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar")));
}
public void testInvalidCiphersOnlyThrowsException() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.putArray("xpack.security.ssl.ciphers", new String[] { "foo", "bar" })
.build();
IllegalArgumentException e =
expectThrows(IllegalArgumentException.class, () -> new SSLService(settings, env));
assertThat(e.getMessage(), is("none of the ciphers [foo, bar] are supported by this JVM"));
}
public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
SSLService sslService = new SSLService(settings, env);
SSLSocketFactory factory = sslService.sslSocketFactory(Settings.EMPTY);
SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY);
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), config.ciphers(), false);
assertThat(factory.getDefaultCipherSuites(), is(ciphers));
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
assertThat(socket.getEnabledCipherSuites(), is(ciphers));
assertThat(socket.getEnabledProtocols(), is(config.supportedProtocols().toArray(Strings.EMPTY_ARRAY)));
}
}
@Network
public void testThatSSLContextWithoutSettingsWorks() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.EMPTY);
SSLService sslService = new SSLService(Settings.EMPTY, env);
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
@ -191,23 +282,23 @@ public class ClientSSLServiceTests extends ESTestCase {
@Network
public void testThatSSLContextTrustsJDKTrustedCAs() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
SSLContext sslContext = sslService.sslContext();
.build();
SSLContext sslContext = new SSLService(settings, env).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 which will succeed because the JDK
// certs are trusted by default
client.execute(new HttpGet("https://www.elastic.co/")).close();
}
sslService = createClientSSLService(Settings.builder()
settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), "false")
.build());
sslContext = sslService.sslContext();
.build();
sslContext = new SSLService(settings, env).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
@ -218,72 +309,4 @@ public class ClientSSLServiceTests extends ESTestCase {
assertThat(e, instanceOf(SSLHandshakeException.class));
}
}
public void testThatTruststorePasswordIsRequired() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.truststore.path", testclientStore)
.build());
try {
sslService.sslContext();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("no truststore password configured"));
}
}
public void testThatKeystorePasswordIsRequired() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.build());
try {
sslService.sslContext();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("no keystore password configured"));
}
}
public void testValidCiphersAndInvalidCiphersWork() throws Exception {
List<String> ciphers = new ArrayList<>(Global.DEFAULT_CIPHERS);
ciphers.add("foo");
ciphers.add("bar");
ClientSSLService sslService = createClientSSLService(Settings.builder()
.putArray("xpack.security.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
.build());
SSLEngine engine = sslService.createSSLEngine();
assertThat(engine, is(notNullValue()));
String[] enabledCiphers = engine.getEnabledCipherSuites();
assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar")));
}
public void testInvalidCiphersOnlyThrowsException() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.putArray("xpack.security.ssl.ciphers", new String[] { "foo", "bar" })
.build());
try {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("none of the ciphers [foo, bar] are supported by this JVM"));
}
}
public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Exception {
ClientSSLService sslService = createClientSSLService(Settings.builder()
.put("xpack.security.ssl.keystore.path", testclientStore)
.put("xpack.security.ssl.keystore.password", "testclient")
.build());
SSLSocketFactory factory = sslService.sslSocketFactory(Settings.EMPTY);
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), sslService.ciphers(), false);
assertThat(factory.getDefaultCipherSuites(), is(ciphers));
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
assertThat(socket.getEnabledCipherSuites(), is(ciphers));
assertThat(socket.getEnabledProtocols(), is(sslService.supportedProtocols()));
}
}
private ClientSSLService createClientSSLService(Settings settings) {
return new ClientSSLService(settings, env, new Global(settings));
}
}

View File

@ -1,254 +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.xpack.security.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
public class ServerSSLServiceTests extends ESTestCase {
private Path testnodeStore;
private Environment env;
@Before
public void setup() throws Exception {
testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
}
public void testThatInvalidProtocolThrowsException() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.protocol", "non-existing")
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.truststore.path", testnodeStore)
.put("xpack.security.ssl.truststore.password", "testnode")
.build();
try {
new ServerSSLService(settings, env, new Global(settings)).createSSLEngine();
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize the SSLContext"));
}
}
public void testThatCustomTruststoreCanBeSpecified() throws Exception {
Path testClientStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks");
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
Settings.Builder settingsBuilder = Settings.builder()
.put("truststore.path", testClientStore)
.put("truststore.password", "testclient");
SSLEngine sslEngineWithTruststore = sslService.createSSLEngine(settingsBuilder.build());
assertThat(sslEngineWithTruststore, is(not(nullValue())));
SSLEngine sslEngine = sslService.createSSLEngine();
assertThat(sslEngineWithTruststore, is(not(sameInstance(sslEngine))));
}
public void testThatSslContextCachingWorks() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
SSLContext sslContext = sslService.sslContext();
SSLContext cachedSslContext = sslService.sslContext();
assertThat(sslContext, is(sameInstance(cachedSslContext)));
}
public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
Path differentPasswordsStore =
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-different-passwords.jks");
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", differentPasswordsStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.keystore.key_password", "testnode1")
.build();
new ServerSSLService(settings, env, new Global(settings)).createSSLEngine();
}
public void testIncorrectKeyPasswordThrowsException() throws Exception {
Path differentPasswordsStore =
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-different-passwords.jks");
try {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", differentPasswordsStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
new ServerSSLService(settings, env, new Global(settings)).createSSLEngine();
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize a KeyManagerFactory"));
}
}
public void testThatSSLv3IsNotEnabled() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
SSLEngine engine = sslService.createSSLEngine();
assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3")));
}
public void testThatSSLSessionCacheHasDefaultLimits() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(1000));
assertThat(context.getSessionTimeout(), equalTo((int) TimeValue.timeValueHours(24).seconds()));
}
public void testThatSettingSSLSessionCacheLimitsWorks() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.session.cache_size", "300")
.put("xpack.security.ssl.session.cache_timeout", "600s")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
assertThat(context.getSessionCacheSize(), equalTo(300));
assertThat(context.getSessionTimeout(), equalTo(600));
}
public void testThatCreateSSLEngineWithoutAnySettingsDoesNotWork() throws Exception {
ServerSSLService sslService = new ServerSSLService(Settings.EMPTY, env, new Global(Settings.EMPTY));
try {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("a key must be configured to act as a server"));
}
}
public void testThatCreateSSLEngineWithOnlyTruststoreDoesNotWork() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path", testnodeStore)
.put("xpack.security.ssl.truststore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
try {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("a key must be configured to act as a server"));
}
}
public void testThatTruststorePasswordIsRequired() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("xpack.security.ssl.truststore.path", testnodeStore)
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
try {
sslService.sslContext();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("no truststore password configured"));
}
}
public void testThatKeystorePasswordIsRequired() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
try {
sslService.sslContext();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("no keystore password configured"));
}
}
public void testCiphersAndInvalidCiphersWork() throws Exception {
List<String> ciphers = new ArrayList<>(Global.DEFAULT_CIPHERS);
ciphers.add("foo");
ciphers.add("bar");
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.putArray("xpack.security.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
SSLEngine engine = sslService.createSSLEngine();
assertThat(engine, is(notNullValue()));
String[] enabledCiphers = engine.getEnabledCipherSuites();
assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar")));
}
public void testInvalidCiphersOnlyThrowsException() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.putArray("xpack.security.ssl.ciphers", new String[] { "foo", "bar" })
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
try {
sslService.createSSLEngine();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("none of the ciphers [foo, bar] are supported by this JVM"));
}
}
public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
SSLSocketFactory factory = sslService.sslSocketFactory(Settings.EMPTY);
final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), sslService.ciphers(), false);
assertThat(factory.getDefaultCipherSuites(), is(ciphers));
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
assertThat(socket.getEnabledCipherSuites(), is(ciphers));
assertThat(socket.getEnabledProtocols(), is(sslService.supportedProtocols()));
}
}
}

View File

@ -8,9 +8,8 @@ package org.elasticsearch.xpack.security.transport.netty3;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
@ -30,7 +29,6 @@ import org.jboss.netty.handler.ssl.SslHandler;
import org.junit.After;
import org.junit.Before;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import java.io.PrintWriter;
@ -60,7 +58,7 @@ public class Netty3HandshakeWaitingHandlerTests extends ESTestCase {
private ServerBootstrap serverBootstrap;
private ClientBootstrap clientBootstrap;
private SSLContext sslContext;
private SSLService sslService;
private final AtomicReference<Throwable> failureCause = new AtomicReference<>();
private ExecutorService threadPoolExecutor;
@ -76,9 +74,7 @@ public class Netty3HandshakeWaitingHandlerTests extends ESTestCase {
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings));
sslContext = sslService.sslContext();
sslService = new SSLService(settings, env);
startBootstrap();
@ -104,7 +100,7 @@ public class Netty3HandshakeWaitingHandlerTests extends ESTestCase {
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
final SSLEngine engine = sslContext.createSSLEngine();
final SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY);
engine.setUseClientMode(true);
return Channels.pipeline(
new SslHandler(engine));
@ -141,7 +137,7 @@ public class Netty3HandshakeWaitingHandlerTests extends ESTestCase {
@Override
public ChannelPipeline getPipeline() throws Exception {
final SSLEngine engine = sslContext.createSSLEngine();
final SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY);
engine.setUseClientMode(true);
return Channels.pipeline(
new SslHandler(engine),
@ -212,7 +208,7 @@ public class Netty3HandshakeWaitingHandlerTests extends ESTestCase {
return new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
final SSLEngine sslEngine = sslContext.createSSLEngine();
final SSLEngine sslEngine = sslService.createSSLEngine(Settings.EMPTY);
sslEngine.setUseClientMode(false);
return Channels.pipeline(new SslHandler(sslEngine),
new SimpleChannelHandler() {

View File

@ -11,8 +11,7 @@ import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.env.Environment;
import org.elasticsearch.http.HttpTransportSettings;
import org.elasticsearch.http.netty3.Netty3HttpMockUtil;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.test.ESTestCase;
@ -26,6 +25,7 @@ import java.nio.file.Path;
import java.util.Locale;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
@ -33,7 +33,8 @@ import static org.mockito.Mockito.mock;
public class SecurityNetty3HttpServerTransportTests extends ESTestCase {
private ServerSSLService serverSSLService;
private SSLService sslService;
private Environment env;
@Before
public void createSSLService() throws Exception {
@ -41,15 +42,16 @@ public class SecurityNetty3HttpServerTransportTests extends ESTestCase {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("path.home", createTempDir())
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
serverSSLService = new ServerSSLService(settings, env, new Global(settings));
env = new Environment(settings);
sslService = new SSLService(settings, env);
}
public void testDefaultClientAuth() throws Exception {
Settings settings = Settings.builder().put(SecurityNetty3HttpServerTransport.SSL_SETTING.getKey(), true).build();
SecurityNetty3HttpServerTransport transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty3HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
@ -62,7 +64,7 @@ public class SecurityNetty3HttpServerTransportTests extends ESTestCase {
.put(SecurityNetty3HttpServerTransport.SSL_SETTING.getKey(), true)
.put(SecurityNetty3HttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty3HttpServerTransport transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty3HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
@ -75,7 +77,7 @@ public class SecurityNetty3HttpServerTransportTests extends ESTestCase {
.put(SecurityNetty3HttpServerTransport.SSL_SETTING.getKey(), true)
.put(SecurityNetty3HttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty3HttpServerTransport transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty3HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(true));
@ -88,7 +90,7 @@ public class SecurityNetty3HttpServerTransportTests extends ESTestCase {
.put(SecurityNetty3HttpServerTransport.SSL_SETTING.getKey(), true)
.put(SecurityNetty3HttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty3HttpServerTransport transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty3HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
@ -99,17 +101,19 @@ public class SecurityNetty3HttpServerTransportTests extends ESTestCase {
Settings settings = Settings.builder()
.put(SecurityNetty3HttpServerTransport.SSL_SETTING.getKey(), true).build();
SecurityNetty3HttpServerTransport transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty3HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory();
SSLEngine defaultEngine = factory.getPipeline().get(SslHandler.class).getEngine();
settings = Settings.builder()
.put(env.settings())
.put(SecurityNetty3HttpServerTransport.SSL_SETTING.getKey(), true)
.put("xpack.security.http.ssl.supported_protocols", "TLSv1.2")
.build();
sslService = new SSLService(settings, new Environment(settings));
transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty3HttpMockUtil.setOpenChannelsHandlerToMock(transport);
factory = transport.configureServerChannelPipelineFactory();
SSLEngine customEngine = factory.getPipeline().get(SslHandler.class).getEngine();
@ -144,4 +148,34 @@ public class SecurityNetty3HttpServerTransportTests extends ESTestCase {
SecurityNetty3HttpServerTransport.overrideSettings(pluginSettingsBuilder, settings);
assertThat(pluginSettingsBuilder.build().isEmpty(), is(true));
}
public void testThatExceptionIsThrownWhenConfiguredWithoutSslKey() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put(SecurityNetty3HttpServerTransport.SSL_SETTING.getKey(), true)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty3HttpServerTransport transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, transport::configureServerChannelPipelineFactory);
assertThat(e.getMessage(), containsString("key must be provided"));
}
public void testNoExceptionWhenConfiguredWithoutSslKeySSLDisabled() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty3HttpServerTransport transport = new SecurityNetty3HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
assertNotNull(transport.configureServerChannelPipelineFactory());
}
}

View File

@ -11,9 +11,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.env.Environment;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
@ -25,14 +23,16 @@ import org.junit.Before;
import java.nio.file.Path;
import java.util.Locale;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.mock;
public class SecurityNetty3TransportTests extends ESTestCase {
private ServerSSLService serverSSLService;
private ClientSSLService clientSSLService;
private Environment env;
private SSLService sslService;
@Before
public void createSSLService() throws Exception {
@ -41,16 +41,14 @@ public class SecurityNetty3TransportTests extends ESTestCase {
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
Global globalSSLConfiguration = new Global(settings);
serverSSLService = new ServerSSLService(settings, env, globalSSLConfiguration);
clientSSLService = new ClientSSLService(settings, env, globalSSLConfiguration);
env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
sslService = new SSLService(settings, env);
}
public void testThatSSLCanBeDisabledByProfile() throws Exception {
Settings settings = Settings.builder().put(SecurityNetty3Transport.SSL_SETTING.getKey(), true).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
@ -61,7 +59,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
public void testThatSSLCanBeEnabledByProfile() throws Exception {
Settings settings = Settings.builder().put(SecurityNetty3Transport.SSL_SETTING.getKey(), false).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
@ -72,7 +70,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
public void testThatProfileTakesDefaultSSLSetting() throws Exception {
Settings settings = Settings.builder().put(SecurityNetty3Transport.SSL_SETTING.getKey(), true).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
@ -82,7 +80,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
public void testDefaultClientAuth() throws Exception {
Settings settings = Settings.builder().put(SecurityNetty3Transport.SSL_SETTING.getKey(), true).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
@ -96,7 +94,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
.put(SecurityNetty3Transport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
@ -110,7 +108,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
.put(SecurityNetty3Transport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
@ -124,7 +122,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
.put(SecurityNetty3Transport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", Settings.EMPTY);
@ -136,7 +134,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.REQUIRED.name(), SSLClientAuth.REQUIRED.name().toLowerCase(Locale.ROOT), "true", "TRUE");
Settings settings = Settings.builder().put(SecurityNetty3Transport.SSL_SETTING.getKey(), true).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
@ -149,7 +147,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.NO.name(), "false", "FALSE", SSLClientAuth.NO.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder().put(SecurityNetty3Transport.SSL_SETTING.getKey(), true).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), null, serverSSLService, clientSSLService, mock(NamedWriteableRegistry.class),
mock(BigArrays.class), null, sslService, mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
@ -162,7 +160,7 @@ public class SecurityNetty3TransportTests extends ESTestCase {
String value = randomFrom(SSLClientAuth.OPTIONAL.name(), SSLClientAuth.OPTIONAL.name().toLowerCase(Locale.ROOT));
Settings settings = Settings.builder().put(SecurityNetty3Transport.SSL_SETTING.getKey(), true).build();
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class),
mock(NetworkService.class), mock(BigArrays.class), null, serverSSLService, clientSSLService,
mock(NetworkService.class), mock(BigArrays.class), null, sslService,
mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class));
Netty3MockUtil.setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client",
@ -170,4 +168,38 @@ public class SecurityNetty3TransportTests extends ESTestCase {
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getNeedClientAuth(), is(false));
assertThat(factory.getPipeline().get(SslHandler.class).getEngine().getWantClientAuth(), is(true));
}
public void testThatExceptionIsThrownWhenConfiguredWithoutSslKey() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class),
mock(NetworkService.class), mock(BigArrays.class), null, sslService,
mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class));
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> transport.configureServerChannelPipelineFactory(randomAsciiOfLength(6), Settings.EMPTY));
assertThat(e.getMessage(), containsString("key must be provided"));
}
public void testNoExceptionWhenConfiguredWithoutSslKeySSLDisabled() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), false)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty3Transport transport = new SecurityNetty3Transport(settings, mock(ThreadPool.class),
mock(NetworkService.class), mock(BigArrays.class), null, sslService,
mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class));
assertNotNull(transport.configureServerChannelPipelineFactory(randomAsciiOfLength(6), Settings.EMPTY));
}
}

View File

@ -16,8 +16,7 @@ import org.elasticsearch.http.HttpTransportSettings;
import org.elasticsearch.http.netty4.Netty4HttpMockUtil;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.junit.Before;
@ -28,6 +27,7 @@ import java.nio.file.Path;
import java.util.Locale;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
@ -35,7 +35,8 @@ import static org.mockito.Mockito.mock;
public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
private ServerSSLService serverSSLService;
private SSLService sslService;
private Environment env;
@Before
public void createSSLService() throws Exception {
@ -43,15 +44,16 @@ public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testNodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("path.home", createTempDir())
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
serverSSLService = new ServerSSLService(settings, env, new Global(settings));
env = new Environment(settings);
sslService = new SSLService(settings, env);
}
public void testDefaultClientAuth() throws Exception {
Settings settings = Settings.builder().put(SecurityNetty4HttpServerTransport.SSL_SETTING.getKey(), true).build();
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty4HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -65,7 +67,7 @@ public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
.put(SecurityNetty4HttpServerTransport.SSL_SETTING.getKey(), true)
.put(SecurityNetty4HttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty4HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -79,7 +81,7 @@ public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
.put(SecurityNetty4HttpServerTransport.SSL_SETTING.getKey(), true)
.put(SecurityNetty4HttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty4HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -93,7 +95,7 @@ public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
.put(SecurityNetty4HttpServerTransport.SSL_SETTING.getKey(), true)
.put(SecurityNetty4HttpServerTransport.CLIENT_AUTH_SETTING.getKey(), value).build();
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty4HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.configureServerChannelHandler();
final EmbeddedChannel ch = new EmbeddedChannel(handler);
@ -105,18 +107,20 @@ public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
Settings settings = Settings.builder()
.put(SecurityNetty4HttpServerTransport.SSL_SETTING.getKey(), true).build();
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty4HttpMockUtil.setOpenChannelsHandlerToMock(transport);
ChannelHandler handler = transport.configureServerChannelHandler();
EmbeddedChannel ch = new EmbeddedChannel(handler);
SSLEngine defaultEngine = ch.pipeline().get(SslHandler.class).engine();
settings = Settings.builder()
.put(env.settings())
.put(SecurityNetty4HttpServerTransport.SSL_SETTING.getKey(), true)
.put("xpack.security.http.ssl.supported_protocols", "TLSv1.2")
.build();
sslService = new SSLService(settings, new Environment(settings));
transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), serverSSLService, mock(ThreadPool.class));
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
Netty4HttpMockUtil.setOpenChannelsHandlerToMock(transport);
handler = transport.configureServerChannelHandler();
ch = new EmbeddedChannel(handler);
@ -152,4 +156,34 @@ public class SecurityNetty4HttpServerTransportTests extends ESTestCase {
SecurityNetty4HttpServerTransport.overrideSettings(pluginSettingsBuilder, settings);
assertThat(pluginSettingsBuilder.build().isEmpty(), is(true));
}
public void testThatExceptionIsThrownWhenConfiguredWithoutSslKey() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put(SecurityNetty4HttpServerTransport.SSL_SETTING.getKey(), true)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, transport::configureServerChannelHandler);
assertThat(e.getMessage(), containsString("key must be provided"));
}
public void testNoExceptionWhenConfiguredWithoutSslKeySSLDisabled() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty4HttpServerTransport transport = new SecurityNetty4HttpServerTransport(settings, mock(NetworkService.class),
mock(BigArrays.class), mock(IPFilter.class), sslService, mock(ThreadPool.class));
assertNotNull(transport.configureServerChannelHandler());
}
}

View File

@ -17,23 +17,23 @@ import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.netty4.Netty4MockUtil;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.ServerSSLService;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.junit.Before;
import java.nio.file.Path;
import java.util.Locale;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.mock;
public class SecurityNetty4TransportTests extends ESTestCase {
private ServerSSLService serverSSLService;
private ClientSSLService clientSSLService;
private Environment env;
private SSLService sslService;
@Before
public void createSSLService() throws Exception {
@ -41,11 +41,10 @@ public class SecurityNetty4TransportTests extends ESTestCase {
Settings settings = Settings.builder()
.put("xpack.security.ssl.keystore.path", testnodeStore)
.put("xpack.security.ssl.keystore.password", "testnode")
.put("path.home", createTempDir())
.build();
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
Global globalSSLConfiguration = new Global(settings);
serverSSLService = new ServerSSLService(settings, env, globalSSLConfiguration);
clientSSLService = new ClientSSLService(settings, env, globalSSLConfiguration);
env = new Environment(settings);
sslService = new SSLService(settings, env);
}
private SecurityNetty4Transport createTransport(boolean sslEnabled) {
@ -66,8 +65,7 @@ public class SecurityNetty4TransportTests extends ESTestCase {
mock(NamedWriteableRegistry.class),
mock(CircuitBreakerService.class),
null,
serverSSLService,
clientSSLService);
sslService);
}
public void testThatSSLCanBeDisabledByProfile() throws Exception {
@ -171,4 +169,35 @@ public class SecurityNetty4TransportTests extends ESTestCase {
assertThat(ch.pipeline().get(SslHandler.class).engine().getWantClientAuth(), is(true));
}
public void testThatExceptionIsThrownWhenConfiguredWithoutSslKey() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put(SecurityNetty4Transport.SSL_SETTING.getKey(), true)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = new SecurityNetty4Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class), null, sslService);
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> transport.getServerChannelInitializer(randomAsciiOfLength(6), Settings.EMPTY));
assertThat(e.getMessage(), containsString("key must be provided"));
}
public void testNoExceptionWhenConfiguredWithoutSslKeySSLDisabled() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.ssl.truststore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"))
.put("xpack.security.ssl.truststore.password", "testnode")
.put(SecurityNetty4Transport.SSL_SETTING.getKey(), false)
.put("path.home", createTempDir())
.build();
env = new Environment(settings);
sslService = new SSLService(settings, env);
SecurityNetty4Transport transport = new SecurityNetty4Transport(settings, mock(ThreadPool.class), mock(NetworkService.class),
mock(BigArrays.class), mock(NamedWriteableRegistry.class), mock(CircuitBreakerService.class), null, sslService);
assertNotNull(transport.getServerChannelInitializer(randomAsciiOfLength(6), Settings.EMPTY));
}
}

View File

@ -22,18 +22,21 @@ import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.XPackTransportClient;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3HttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertPathBuilderException;
import static org.elasticsearch.test.SecuritySettingsSource.getSSLSettingsForStore;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@ -71,11 +74,7 @@ public class SslClientAuthTests extends SecurityIntegTestCase {
}
public void testThatHttpWorksWithSslClientAuth() throws IOException {
Settings settings = Settings.builder()
.put(getSSLSettingsForStore("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks", "testclient"))
.build();
ClientSSLService sslService = new ClientSSLService(settings, null, new Global(settings));
SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sslService.sslContext(), NoopHostnameVerifier.INSTANCE);
SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(getSSLContext(), NoopHostnameVerifier.INSTANCE);
try (RestClient restClient = createRestClient(httpClientBuilder -> httpClientBuilder.setSSLStrategy(sessionStrategy), "https")) {
Response response = restClient.performRequest("GET", "/",
new BasicHeader("Authorization", basicAuthHeaderValue(transportClientUsername(), transportClientPassword())));
@ -108,4 +107,21 @@ public class SslClientAuthTests extends SecurityIntegTestCase {
assertGreenClusterState(client);
}
}
private SSLContext getSSLContext() {
try (InputStream in =
Files.newInputStream(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks"))) {
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(in, "testclient".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "testclient".toCharArray());
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
return context;
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
}
}
}

View File

@ -23,8 +23,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.ssl.SSLService;
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3HttpServerTransport;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.transport.Transport;
@ -99,12 +98,14 @@ public class SslIntegrationTests extends SecurityIntegTestCase {
Settings settings = Settings.builder()
.put(getSSLSettingsForStore("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks", "testclient"))
.build();
ClientSSLService service = new ClientSSLService(settings, null, new Global(settings));
SSLService service = new SSLService(settings, null);
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nodeClientUsername(),
new String(nodeClientPassword().internalChars())));
try (CloseableHttpClient client = HttpClients.custom().setSSLContext(service.sslContext())
try (CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(new SSLConnectionSocketFactory(service.sslSocketFactory(Settings.EMPTY),
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.setDefaultCredentialsProvider(provider).build();
CloseableHttpResponse response = client.execute(new HttpGet(getNodeUrl()))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));