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:
parent
20041446f3
commit
af44ea0440
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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."));
|
||||
|
|
|
@ -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) -> {};
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue