diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 4beb83f873c..c6942f0008e 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -111,6 +111,7 @@ 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.support.OptionalSettings; import org.elasticsearch.xpack.security.transport.SecurityClientTransportService; @@ -185,8 +186,7 @@ public class Security implements ActionPlugin, IngestPlugin { modules.add(b -> { // for transport client we still must inject these ssl classes with guice b.bind(ServerSSLService.class).toProvider(Providers.of(null)); - b.bind(ClientSSLService.class).toInstance( - new ClientSSLService(settings, null, new SSLConfiguration.Global(settings), null)); + b.bind(ClientSSLService.class).toInstance(new ClientSSLService(settings, null, new SSLConfiguration.Global(settings))); }); return modules; @@ -232,8 +232,12 @@ public class Security implements ActionPlugin, IngestPlugin { components.add(securityContext); final SSLConfiguration.Global globalSslConfig = new SSLConfiguration.Global(settings); - final ClientSSLService clientSSLService = new ClientSSLService(settings, env, globalSslConfig, resourceWatcherService); - final ServerSSLService serverSSLService = new ServerSSLService(settings, env, globalSslConfig, resourceWatcherService); + 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); diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java index d100396bf47..517791211c7 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java @@ -6,44 +6,31 @@ package org.elasticsearch.xpack.security.authc.esnative; import com.google.common.base.Charsets; -import com.google.common.base.Joiner; import javax.net.ssl.HttpsURLConnection; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.SettingCommand; import org.elasticsearch.cli.Terminal; -import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.SuppressForbidden; -import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.env.Environment; -import org.elasticsearch.node.internal.InternalSettingsPreparer; -import org.elasticsearch.xpack.security.action.role.PutRoleRequest; -import org.elasticsearch.xpack.security.action.user.PutUserRequest; import org.elasticsearch.xpack.security.authc.Realms; import org.elasticsearch.xpack.security.authc.file.FileUserPasswdStore; import org.elasticsearch.xpack.security.authc.file.FileUserRolesStore; -import org.elasticsearch.xpack.security.authc.support.Hasher; 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.permission.Permission; 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.support.NoOpLogger; -import org.elasticsearch.xpack.security.support.Validation; import java.io.BufferedReader; import java.io.IOException; @@ -52,16 +39,13 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; -import java.nio.file.Files; import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.regex.Pattern; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xpack.security.Security.setting; @@ -151,7 +135,7 @@ public class ESNativeRealmMigrateTool extends MultiCommand { 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, null); + final ClientSSLService sslService = new ClientSSLService(sslSettings, env, globalConfig); final HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection(); AccessController.doPrivileged(new PrivilegedAction() { @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/AbstractSSLService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/AbstractSSLService.java index 69f44f75b32..80595be17d5 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/AbstractSSLService.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/AbstractSSLService.java @@ -13,13 +13,10 @@ 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 org.elasticsearch.xpack.security.ssl.TrustConfig.Reloadable.Listener; -import org.elasticsearch.watcher.ResourceWatcherService; import javax.net.ssl.KeyManager; 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.TrustManager; @@ -28,9 +25,12 @@ import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; import java.util.Arrays; -import java.util.Enumeration; +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; /** @@ -39,19 +39,18 @@ import java.util.concurrent.ConcurrentHashMap; */ public abstract class AbstractSSLService extends AbstractComponent { - private final ConcurrentHashMap sslContexts = new ConcurrentHashMap<>(); private final SSLContextCacheLoader cacheLoader = new SSLContextCacheLoader(); + private final ConcurrentHashMap sslContexts = new ConcurrentHashMap<>(); protected final SSLConfiguration globalSSLConfiguration; protected final Environment env; - protected final ResourceWatcherService resourceWatcherService; - public AbstractSSLService(Settings settings, Environment environment, Global globalSSLConfiguration, - ResourceWatcherService resourceWatcherService) { + private Listener listener = Listener.NOOP; + + AbstractSSLService(Settings settings, Environment environment, Global globalSSLConfiguration) { super(settings); this.env = environment; this.globalSSLConfiguration = globalSSLConfiguration; - this.resourceWatcherService = resourceWatcherService; } public String[] supportedProtocols() { @@ -167,6 +166,27 @@ public abstract class AbstractSSLService extends AbstractComponent { 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 getLoadedSSLConfigurations() { + return Collections.unmodifiableSet(new HashSet<>(sslContexts.keySet())); + } + private class SSLContextCacheLoader { public SSLContext load(SSLConfiguration sslConfiguration) { @@ -175,15 +195,15 @@ public abstract class AbstractSSLService extends AbstractComponent { logger.debug("using ssl settings [{}]", sslConfiguration); } - ConfigRefreshListener configRefreshListener = new ConfigRefreshListener(sslConfiguration); - TrustManager[] trustManagers = sslConfiguration.trustConfig().trustManagers(env, resourceWatcherService, configRefreshListener); - KeyManager[] keyManagers = sslConfiguration.keyConfig().keyManagers(env, resourceWatcherService, configRefreshListener); + 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; } @@ -202,37 +222,6 @@ public abstract class AbstractSSLService extends AbstractComponent { } } - class ConfigRefreshListener implements Listener { - - private final SSLConfiguration sslConfiguration; - - ConfigRefreshListener(SSLConfiguration sslConfiguration) { - this.sslConfiguration = sslConfiguration; - } - - @Override - public void onReload() { - SSLContext context = sslContexts.get(sslConfiguration); - if (context != null) { - invalidateSessions(context.getClientSessionContext()); - invalidateSessions(context.getServerSessionContext()); - } - } - - void invalidateSessions(SSLSessionContext sslSessionContext) { - Enumeration sessionIds = sslSessionContext.getIds(); - while (sessionIds.hasMoreElements()) { - byte[] sessionId = sessionIds.nextElement(); - sslSessionContext.getSession(sessionId).invalidate(); - } - } - - @Override - public void onFailure(Exception e) { - logger.error("failed to load updated ssl context for [{}]", e, sslConfiguration); - } - } - /** * This socket factory set the protocols and ciphers on each SSLSocket after it is created */ @@ -305,4 +294,14 @@ public abstract class AbstractSSLService extends AbstractComponent { 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) -> {}; + } } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/CertUtils.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/CertUtils.java index 5d85c447f68..ab0817079d7 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/CertUtils.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/CertUtils.java @@ -30,6 +30,7 @@ import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.SuppressForbidden; @@ -48,6 +49,7 @@ import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; import javax.security.auth.x500.X500Principal; import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.io.Reader; import java.math.BigInteger; import java.net.InetAddress; @@ -84,7 +86,7 @@ class CertUtils { return PathUtils.get(Strings.cleanPath(path)); } - static X509ExtendedKeyManager[] keyManagers(Certificate[] certificateChain, PrivateKey privateKey, char[] password) throws Exception { + static X509ExtendedKeyManager keyManagers(Certificate[] certificateChain, PrivateKey privateKey, char[] password) throws Exception { KeyStore keyStore = KeyStore.getInstance("jks"); keyStore.load(null, null); // password must be non-null for keystore... @@ -92,19 +94,19 @@ class CertUtils { return keyManagers(keyStore, password, KeyManagerFactory.getDefaultAlgorithm()); } - static X509ExtendedKeyManager[] keyManagers(KeyStore keyStore, char[] password, String algorithm) throws Exception { + static X509ExtendedKeyManager keyManagers(KeyStore keyStore, char[] password, String algorithm) throws Exception { KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); kmf.init(keyStore, password); KeyManager[] keyManagers = kmf.getKeyManagers(); for (KeyManager keyManager : keyManagers) { if (keyManager instanceof X509ExtendedKeyManager) { - return new X509ExtendedKeyManager[] { (X509ExtendedKeyManager) keyManager }; + return (X509ExtendedKeyManager) keyManager; } } throw new IllegalStateException("failed to find a X509ExtendedKeyManager"); } - static X509ExtendedTrustManager[] trustManagers(Certificate[] certificates) throws Exception { + static X509ExtendedTrustManager trustManagers(Certificate[] certificates) throws Exception { KeyStore store = KeyStore.getInstance("jks"); store.load(null, null); int counter = 0; @@ -115,13 +117,26 @@ class CertUtils { return trustManagers(store, TrustManagerFactory.getDefaultAlgorithm()); } - static X509ExtendedTrustManager[] trustManagers(KeyStore keyStore, String algorithm) throws Exception { + static X509ExtendedTrustManager trustManagers(String trustStorePath, String trustStorePassword, String trustStoreAlgorithm, + Environment env) throws Exception { + try (InputStream in = Files.newInputStream(resolvePath(trustStorePath, env))) { + // TODO remove reliance on JKS since we can PKCS12 stores... + KeyStore trustStore = KeyStore.getInstance("jks"); + assert trustStorePassword != null; + trustStore.load(in, trustStorePassword.toCharArray()); + return CertUtils.trustManagers(trustStore, trustStoreAlgorithm); + } catch (Exception e) { + throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); + } + } + + static X509ExtendedTrustManager trustManagers(KeyStore keyStore, String algorithm) throws Exception { TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); tmf.init(keyStore); TrustManager[] trustManagers = tmf.getTrustManagers(); for (TrustManager trustManager : trustManagers) { if (trustManager instanceof X509ExtendedTrustManager) { - return new X509ExtendedTrustManager[] { (X509ExtendedTrustManager) trustManager }; + return (X509ExtendedTrustManager) trustManager ; } } throw new IllegalStateException("failed to find a X509ExtendedTrustManager"); diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ClientSSLService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ClientSSLService.java index 79b03c4444c..8805703c1f1 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ClientSSLService.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ClientSSLService.java @@ -7,14 +7,12 @@ package org.elasticsearch.xpack.security.ssl; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global; public class ClientSSLService extends AbstractSSLService { - public ClientSSLService(Settings settings, Environment env, Global globalSSLConfiguration, - ResourceWatcherService resourceWatcherService) { - super(settings, env, globalSSLConfiguration, resourceWatcherService); + public ClientSSLService(Settings settings, Environment env, Global globalSSLConfiguration) { + super(settings, env, globalSSLConfiguration); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/KeyConfig.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/KeyConfig.java index d62b8aa772b..31c46bc312a 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/KeyConfig.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/KeyConfig.java @@ -5,19 +5,12 @@ */ package org.elasticsearch.xpack.security.ssl; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Nullable; import org.elasticsearch.env.Environment; -import org.elasticsearch.xpack.security.ssl.TrustConfig.Reloadable.Listener; -import org.elasticsearch.watcher.FileWatcher; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.watcher.ResourceWatcherService.Frequency; -import javax.net.ssl.KeyManager; import javax.net.ssl.SSLEngine; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; -import java.io.IOException; import java.net.Socket; import java.nio.file.Path; import java.security.Principal; @@ -28,18 +21,20 @@ import java.util.List; abstract class KeyConfig extends TrustConfig { - KeyConfig(boolean includeSystem, boolean reloadEnabled) { - super(includeSystem, reloadEnabled); + private X509ExtendedKeyManager[] keyManagers = null; + + KeyConfig(boolean includeSystem) { + super(includeSystem); } - static final KeyConfig NONE = new KeyConfig(false, false) { + static final KeyConfig NONE = new KeyConfig(false) { @Override - X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment) { + X509ExtendedKeyManager loadKeyManager(@Nullable Environment environment) { return null; } @Override - X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) { + X509ExtendedTrustManager nonSystemTrustManager(@Nullable Environment environment) { return null; } @@ -58,39 +53,48 @@ abstract class KeyConfig extends TrustConfig { } }; - final KeyManager[] keyManagers(@Nullable Environment environment, @Nullable ResourceWatcherService resourceWatcherService, - @Nullable Listener listener) { - X509ExtendedKeyManager[] keyManagers = loadKeyManagers(environment); - if (reloadEnabled && resourceWatcherService != null && listener != null) { - ReloadableX509KeyManager reloadableX509KeyManager = new ReloadableX509KeyManager(keyManagers[0], environment); - List filesToMonitor = filesToMonitor(environment); - if (filesToMonitor.isEmpty() == false) { - ChangeListener changeListener = new ChangeListener(filesToMonitor, reloadableX509KeyManager, listener); - try { - for (Path dir : directoriesToMonitor(filesToMonitor)) { - FileWatcher fileWatcher = new FileWatcher(dir); - fileWatcher.addListener(changeListener); - resourceWatcherService.add(fileWatcher, Frequency.HIGH); - } - return new X509ExtendedKeyManager[]{reloadableX509KeyManager}; - } catch (IOException e) { - throw new ElasticsearchException("failed to add file watcher", e); - } - } + final synchronized X509ExtendedKeyManager[] keyManagers(@Nullable Environment environment) { + if (keyManagers == null) { + X509ExtendedKeyManager keyManager = loadKeyManager(environment); + setKeyManagers(keyManager); } return keyManagers; } - abstract X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment); + @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; + } - final class ReloadableX509KeyManager extends X509ExtendedKeyManager implements Reloadable { + 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 final Environment environment; private volatile X509ExtendedKeyManager keyManager; - ReloadableX509KeyManager(X509ExtendedKeyManager keyManager, @Nullable Environment environment) { + ReloadableX509KeyManager(X509ExtendedKeyManager keyManager) { this.keyManager = keyManager; - this.environment = environment; } @Override @@ -133,13 +137,13 @@ abstract class KeyConfig extends TrustConfig { return keyManager.chooseEngineServerAlias(s, principals, engine); } - public synchronized void reload() { - X509ExtendedKeyManager[] keyManagers = loadKeyManagers(environment); - this.keyManager = keyManagers[0]; - } - synchronized void setKeyManager(X509ExtendedKeyManager x509ExtendedKeyManager) { this.keyManager = x509ExtendedKeyManager; } + + // pkg-private accessor for testing + X509ExtendedKeyManager getKeyManager() { + return keyManager; + } } } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMKeyConfig.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMKeyConfig.java index 7a5f7c03c02..f0867e446cc 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMKeyConfig.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMKeyConfig.java @@ -28,23 +28,27 @@ class PEMKeyConfig extends KeyConfig { final String keyPassword; final List certPaths; - PEMKeyConfig(boolean includeSystem, boolean reloadEnabled, String keyPath, String keyPassword, List certPaths) { - super(includeSystem, reloadEnabled); + PEMKeyConfig(boolean includeSystem, String keyPath, String keyPassword, List certPaths) { + super(includeSystem); this.keyPath = keyPath; this.keyPassword = keyPassword; this.certPaths = certPaths; } @Override - X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment) { + X509ExtendedKeyManager loadKeyManager(@Nullable Environment environment) { + // password must be non-null for keystore... + char[] password = keyPassword == null ? new char[0] : keyPassword.toCharArray(); try { PrivateKey privateKey = readPrivateKey(CertUtils.resolvePath(keyPath, environment)); Certificate[] certificateChain = CertUtils.readCertificates(certPaths, environment); - // password must be non-null for keystore... - char[] password = keyPassword == null ? new char[0] : keyPassword.toCharArray(); return CertUtils.keyManagers(certificateChain, privateKey, password); } catch (Exception e) { throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e); + } finally { + if (password != null) { + Arrays.fill(password, (char) 0); + } } } @@ -60,7 +64,7 @@ class PEMKeyConfig extends KeyConfig { } @Override - X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) { + X509ExtendedTrustManager nonSystemTrustManager(@Nullable Environment environment) { try { Certificate[] certificates = CertUtils.readCertificates(certPaths, environment); return CertUtils.trustManagers(certificates); diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMTrustConfig.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMTrustConfig.java index b9b1473a70e..5e5f42ba45c 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMTrustConfig.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/PEMTrustConfig.java @@ -20,13 +20,13 @@ class PEMTrustConfig extends TrustConfig { final List caPaths; - PEMTrustConfig(boolean includeSystem, boolean reloadEnabled, List caPaths) { - super(includeSystem, reloadEnabled); + PEMTrustConfig(boolean includeSystem, List caPaths) { + super(includeSystem); this.caPaths = caPaths; } @Override - X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) { + X509ExtendedTrustManager nonSystemTrustManager(@Nullable Environment environment) { try { Certificate[] certificates = CertUtils.readCertificates(caPaths, environment); return CertUtils.trustManagers(certificates); diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/SSLConfiguration.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/SSLConfiguration.java index 84aa8a853eb..a61eceb3def 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/SSLConfiguration.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/SSLConfiguration.java @@ -7,6 +7,8 @@ package org.elasticsearch.xpack.security.ssl; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -14,10 +16,12 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Function; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.env.Environment; import static org.elasticsearch.xpack.security.Security.setting; import static org.elasticsearch.xpack.security.support.OptionalSettings.createInt; @@ -43,6 +47,42 @@ public abstract class SSLConfiguration { public abstract List supportedProtocols(); + /** + * Provides the list of paths to files that back this configuration + */ + public List filesToMonitor(@Nullable Environment environment) { + if (keyConfig() == trustConfig()) { + return keyConfig().filesToMonitor(environment); + } + List paths = new ArrayList<>(keyConfig().filesToMonitor(environment)); + paths.addAll(trustConfig().filesToMonitor(environment)); + 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; @@ -258,14 +298,14 @@ public abstract class SSLConfiguration { if (certPaths == null) { throw new IllegalArgumentException("you must specify the certificates to use with the key"); } - return new PEMKeyConfig(includeSystem, reloadEnabled, keyPath, keyPassword, certPaths); + return new PEMKeyConfig(includeSystem, keyPath, keyPassword, certPaths); } else { assert keyStorePath != null; String keyStorePassword = KEYSTORE_PASSWORD_SETTING.get(settings).orElse(null); String keyStoreAlgorithm = KEYSTORE_ALGORITHM_SETTING.get(settings); String keyStoreKeyPassword = KEYSTORE_KEY_PASSWORD_SETTING.get(settings).orElse(keyStorePassword); String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings); - return new StoreKeyConfig(includeSystem, reloadEnabled, keyStorePath, keyStorePassword, keyStoreKeyPassword, + return new StoreKeyConfig(includeSystem, keyStorePath, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, trustStoreAlgorithm); } } @@ -274,19 +314,18 @@ public abstract class SSLConfiguration { String trustStorePath = TRUSTSTORE_PATH_SETTING.get(settings).orElse(null); List caPaths = getListOrNull(CA_PATHS_SETTING, settings); boolean includeSystem = INCLUDE_JDK_CERTS_SETTING.get(settings); - boolean reloadEnabled = RELOAD_ENABLED_SETTING.get(settings); if (trustStorePath != null && caPaths != null) { throw new IllegalArgumentException("you cannot specify a truststore and ca files"); } else if (caPaths != null) { - return new PEMTrustConfig(includeSystem, reloadEnabled, caPaths); + return new PEMTrustConfig(includeSystem, caPaths); } else if (trustStorePath != null) { String trustStorePassword = TRUSTSTORE_PASSWORD_SETTING.get(settings).orElse(null); String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings); - return new StoreTrustConfig(includeSystem, reloadEnabled, trustStorePath, trustStorePassword, trustStoreAlgorithm); + return new StoreTrustConfig(includeSystem, trustStorePath, trustStorePassword, trustStoreAlgorithm); } else if (keyInfo != KeyConfig.NONE) { return keyInfo; } else { - return new StoreTrustConfig(includeSystem, reloadEnabled, null, null, null); + return new StoreTrustConfig(includeSystem, null, null, null); } } } @@ -413,14 +452,14 @@ public abstract class SSLConfiguration { if (certPaths == null) { throw new IllegalArgumentException("you must specify the certificates to use with the key"); } - return new PEMKeyConfig(includeSystem, reloadEnabled, keyPath, keyPassword, certPaths); + return new PEMKeyConfig(includeSystem, keyPath, keyPassword, certPaths); } else { assert keyStorePath != null; String keyStorePassword = KEYSTORE_PASSWORD_SETTING.get(settings).orElse(null); String keyStoreAlgorithm = KEYSTORE_ALGORITHM_SETTING.get(settings); String keyStoreKeyPassword = KEYSTORE_KEY_PASSWORD_SETTING.get(settings).orElse(keyStorePassword); String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings); - return new StoreKeyConfig(includeSystem, reloadEnabled, keyStorePath, keyStorePassword, keyStoreKeyPassword, + return new StoreKeyConfig(includeSystem, keyStorePath, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, trustStoreAlgorithm); } } @@ -431,11 +470,11 @@ public abstract class SSLConfiguration { if (trustStorePath != null && caPaths != null) { throw new IllegalArgumentException("you cannot specify a truststore and ca files"); } else if (caPaths != null) { - return new PEMTrustConfig(INCLUDE_JDK_CERTS_SETTING.get(settings), RELOAD_ENABLED_SETTING.get(settings), caPaths); + return new PEMTrustConfig(INCLUDE_JDK_CERTS_SETTING.get(settings), caPaths); } else if (trustStorePath != null) { String trustStorePassword = TRUSTSTORE_PASSWORD_SETTING.get(settings).orElse(null); String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings); - return new StoreTrustConfig(INCLUDE_JDK_CERTS_SETTING.get(settings), RELOAD_ENABLED_SETTING.get(settings), + return new StoreTrustConfig(INCLUDE_JDK_CERTS_SETTING.get(settings), trustStorePath, trustStorePassword, trustStoreAlgorithm); } else if (keyConfig == global.keyConfig()) { return global.trustConfig(); diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationReloader.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationReloader.java new file mode 100644 index 00000000000..49c6da03bfb --- /dev/null +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationReloader.java @@ -0,0 +1,150 @@ +/* + * 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.component.AbstractComponent; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.watcher.FileChangesListener; +import org.elasticsearch.watcher.FileWatcher; +import org.elasticsearch.watcher.ResourceWatcherService; +import org.elasticsearch.watcher.ResourceWatcherService.Frequency; + +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; +import java.util.concurrent.ConcurrentHashMap; +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 { + + private final ConcurrentHashMap pathToChangeListenerMap = new ConcurrentHashMap<>(); + private final Environment environment; + private final ResourceWatcherService resourceWatcherService; + private final ServerSSLService serverSSLService; + private final ClientSSLService clientSSLService; + + public SSLConfigurationReloader(Settings settings, Environment env, ServerSSLService serverSSLService, + ClientSSLService clientSSLService, 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)); + } + + /** + * Collects all of the directories that need to be monitored for the provided {@link SSLConfiguration} instances and ensures that + * they are being watched for changes + */ + private void startWatching(Collection sslConfigurations) { + for (SSLConfiguration sslConfiguration : sslConfigurations) { + for (Path directory : directoriesToMonitor(sslConfiguration.filesToMonitor(environment))) { + pathToChangeListenerMap.compute(directory, (path, listener) -> { + if (listener != null) { + listener.addSSLConfiguration(sslConfiguration); + return listener; + } + + ChangeListener changeListener = new ChangeListener(); + changeListener.addSSLConfiguration(sslConfiguration); + FileWatcher fileWatcher = new FileWatcher(path); + fileWatcher.addListener(changeListener); + try { + resourceWatcherService.add(fileWatcher, Frequency.HIGH); + return changeListener; + } catch (IOException e) { + logger.error("failed to start watching directory [{}] for ssl configuration [{}]", path, sslConfiguration); + } + return null; + }); + } + } + } + + /** + * Invalidates all of the sessions in the provided {@link SSLContext} + */ + 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 sessionIds = sslSessionContext.getIds(); + while (sessionIds.hasMoreElements()) { + byte[] sessionId = sessionIds.nextElement(); + sslSessionContext.getSession(sessionId).invalidate(); + } + } + + /** + * Returns a unique set of directories that need to be monitored based on the provided file paths + */ + private static Set directoriesToMonitor(List filePaths) { + Set paths = new HashSet<>(); + for (Path path : filePaths) { + paths.add(path.getParent()); + } + return paths; + } + + private class ChangeListener implements FileChangesListener { + + private final CopyOnWriteArraySet sslConfigurations = new CopyOnWriteArraySet<>(); + + /** + * Adds the given ssl configuration to those that have files within the directory watched by this change listener + */ + private void addSSLConfiguration(SSLConfiguration sslConfiguration) { + sslConfigurations.add(sslConfiguration); + } + + @Override + public void onFileCreated(Path file) { + onFileChanged(file); + } + + @Override + public void onFileDeleted(Path file) { + onFileChanged(file); + } + + @Override + 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)); + } + } + } + } +} diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ServerSSLService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ServerSSLService.java index 254f6870e4a..f8c0e78e2ba 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ServerSSLService.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/ServerSSLService.java @@ -7,14 +7,12 @@ package org.elasticsearch.xpack.security.ssl; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global; public class ServerSSLService extends AbstractSSLService { - public ServerSSLService(Settings settings, Environment environment, Global globalSSLConfiguration, - ResourceWatcherService resourceWatcherService) { - super(settings, environment, globalSSLConfiguration, resourceWatcherService); + public ServerSSLService(Settings settings, Environment environment, Global globalSSLConfiguration) { + super(settings, environment, globalSSLConfiguration); } @Override diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreKeyConfig.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreKeyConfig.java index 1e762dc6fed..24bf267b0d8 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreKeyConfig.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreKeyConfig.java @@ -26,9 +26,9 @@ class StoreKeyConfig extends KeyConfig { final String keyPassword; final String trustStoreAlgorithm; - StoreKeyConfig(boolean includeSystem, boolean reloadEnabled, String keyStorePath, String keyStorePassword, String keyPassword, + StoreKeyConfig(boolean includeSystem, String keyStorePath, String keyStorePassword, String keyPassword, String keyStoreAlgorithm, String trustStoreAlgorithm) { - super(includeSystem, reloadEnabled); + super(includeSystem); this.keyStorePath = keyStorePath; this.keyStorePassword = keyStorePassword; this.keyPassword = keyPassword; @@ -37,7 +37,7 @@ class StoreKeyConfig extends KeyConfig { } @Override - X509ExtendedKeyManager[] loadKeyManagers(@Nullable Environment environment) { + X509ExtendedKeyManager loadKeyManager(@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"); @@ -50,14 +50,9 @@ class StoreKeyConfig extends KeyConfig { } @Override - X509ExtendedTrustManager[] nonSystemTrustManagers(@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"); - assert keyStorePassword != null; - ks.load(in, keyStorePassword.toCharArray()); - - return CertUtils.trustManagers(ks, trustStoreAlgorithm); + X509ExtendedTrustManager nonSystemTrustManager(@Nullable Environment environment) { + try { + return CertUtils.trustManagers(keyStorePath, keyStorePassword, trustStoreAlgorithm, environment); } catch (Exception e) { throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreTrustConfig.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreTrustConfig.java index e9ac5bffc2b..db1a8ef40ea 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreTrustConfig.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/StoreTrustConfig.java @@ -23,25 +23,20 @@ class StoreTrustConfig extends TrustConfig { final String trustStorePassword; final String trustStoreAlgorithm; - StoreTrustConfig(boolean includeSystem, boolean reloadEnabled, String trustStorePath, String trustStorePassword, - String trustStoreAlgorithm) { - super(includeSystem, reloadEnabled); + StoreTrustConfig(boolean includeSystem, String trustStorePath, String trustStorePassword, String trustStoreAlgorithm) { + super(includeSystem); this.trustStorePath = trustStorePath; this.trustStorePassword = trustStorePassword; this.trustStoreAlgorithm = trustStoreAlgorithm; } @Override - X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment) { + X509ExtendedTrustManager nonSystemTrustManager(@Nullable Environment environment) { if (trustStorePath == null) { return null; } - try (InputStream in = Files.newInputStream(CertUtils.resolvePath(trustStorePath, environment))) { - // TODO remove reliance on JKS since we can PKCS12 stores... - KeyStore trustStore = KeyStore.getInstance("jks"); - assert trustStorePassword != null; - trustStore.load(in, trustStorePassword.toCharArray()); - return CertUtils.trustManagers(trustStore, trustStoreAlgorithm); + try { + return CertUtils.trustManagers(trustStorePath, trustStorePassword, trustStoreAlgorithm, environment); } catch (Exception e) { throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/TrustConfig.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/TrustConfig.java index 6c67d6f6309..1a3268b0027 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/TrustConfig.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/ssl/TrustConfig.java @@ -8,60 +8,53 @@ package org.elasticsearch.xpack.security.ssl; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Nullable; import org.elasticsearch.env.Environment; -import org.elasticsearch.xpack.security.ssl.TrustConfig.Reloadable.Listener; -import org.elasticsearch.watcher.FileChangesListener; -import org.elasticsearch.watcher.FileWatcher; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.watcher.ResourceWatcherService.Frequency; import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; -import java.io.IOException; import java.net.Socket; -import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.HashSet; import java.util.List; -import java.util.Set; abstract class TrustConfig { protected final boolean includeSystem; - protected final boolean reloadEnabled; - TrustConfig(boolean includeSystem, boolean reloadEnabled) { + X509ExtendedTrustManager[] trustManagers = null; + + TrustConfig(boolean includeSystem) { this.includeSystem = includeSystem; - this.reloadEnabled = reloadEnabled; } - final TrustManager[] trustManagers(@Nullable Environment environment, @Nullable ResourceWatcherService resourceWatcherService, - @Nullable Listener listener) { - X509ExtendedTrustManager[] trustManagers = loadAndMergeIfNecessary(environment); - if (reloadEnabled && resourceWatcherService != null && listener != null) { - ReloadableTrustManager reloadableTrustManager = new ReloadableTrustManager(trustManagers[0], environment); - try { - List filesToMonitor = filesToMonitor(environment); - ChangeListener changeListener = new ChangeListener(filesToMonitor, reloadableTrustManager, listener); - for (Path path : directoriesToMonitor(filesToMonitor)) { - FileWatcher fileWatcher = new FileWatcher(path); - fileWatcher.addListener(changeListener); - resourceWatcherService.add(fileWatcher, Frequency.HIGH); - } - return new X509ExtendedTrustManager[] { reloadableTrustManager }; - } catch (IOException e) { - throw new ElasticsearchException("failed to add file watcher", e); - } + final synchronized X509ExtendedTrustManager[] trustManagers(@Nullable Environment environment) { + if (trustManagers == null) { + X509ExtendedTrustManager loadedTrustManager = loadAndMergeIfNecessary(environment); + setTrustManagers(loadedTrustManager); } return trustManagers; } - abstract X509ExtendedTrustManager[] nonSystemTrustManagers(@Nullable Environment environment); + 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); + } + } + + abstract X509ExtendedTrustManager nonSystemTrustManager(@Nullable Environment environment); abstract void validate(); @@ -69,56 +62,47 @@ abstract class TrustConfig { public abstract String toString(); - private X509ExtendedTrustManager[] loadAndMergeIfNecessary(@Nullable Environment environment) { - X509ExtendedTrustManager[] nonSystemTrustManagers = nonSystemTrustManagers(environment); + final X509ExtendedTrustManager loadAndMergeIfNecessary(@Nullable Environment environment) { + X509ExtendedTrustManager trustManager = nonSystemTrustManager(environment); if (includeSystem) { - return mergeWithSystem(nonSystemTrustManagers); - } else if (nonSystemTrustManagers == null || nonSystemTrustManagers.length == 0) { - return new X509ExtendedTrustManager[0]; + trustManager = mergeWithSystem(trustManager); + } else if (trustManager == null) { + return null; } - return nonSystemTrustManagers; + return trustManager; } - private X509ExtendedTrustManager[] mergeWithSystem(X509ExtendedTrustManager[] nonSystemTrustManagers) { + private X509ExtendedTrustManager mergeWithSystem(X509ExtendedTrustManager nonSystemTrustManager) { try { TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init((KeyStore) null); TrustManager[] systemTrustManagers = tmf.getTrustManagers(); - X509ExtendedTrustManager system = findFirstX509TrustManager(systemTrustManagers); - if (nonSystemTrustManagers == null || nonSystemTrustManagers.length == 0) { - return new X509ExtendedTrustManager[] { system }; + X509ExtendedTrustManager system = findFirstX509ExtendedTrustManager(systemTrustManagers); + if (nonSystemTrustManager == null) { + return system; } - return new X509ExtendedTrustManager[] { new CombiningX509TrustManager(nonSystemTrustManagers[0], system) }; + return new CombiningX509TrustManager(nonSystemTrustManager, system); } catch (Exception e) { throw new ElasticsearchException("failed to initialize a trust managers", e); } } - private static X509ExtendedTrustManager findFirstX509TrustManager(TrustManager[] trustManagers) { + private static X509ExtendedTrustManager findFirstX509ExtendedTrustManager(TrustManager[] trustManagers) { X509ExtendedTrustManager x509TrustManager = null; for (TrustManager trustManager : trustManagers) { - if (trustManager instanceof X509TrustManager) { + if (trustManager instanceof X509ExtendedTrustManager) { // first one wins like in the JDK x509TrustManager = (X509ExtendedTrustManager) trustManager; break; } } if (x509TrustManager == null) { - throw new IllegalArgumentException("did not find a X509TrustManager"); + throw new IllegalArgumentException("did not find a X509ExtendedTrustManager"); } return x509TrustManager; } - static Set directoriesToMonitor(List filePaths) { - Set paths = new HashSet<>(); - for (Path path : filePaths) { - assert Files.isDirectory(path) == false; - paths.add(path.getParent()); - } - return paths; - } - private static class CombiningX509TrustManager extends X509ExtendedTrustManager { private final X509ExtendedTrustManager first; @@ -196,14 +180,12 @@ abstract class TrustConfig { } } - final class ReloadableTrustManager extends X509ExtendedTrustManager implements Reloadable { + final class ReloadableTrustManager extends X509ExtendedTrustManager { - private final Environment environment; private volatile X509ExtendedTrustManager trustManager; - ReloadableTrustManager(X509ExtendedTrustManager trustManager, @Nullable Environment environment) { + ReloadableTrustManager(X509ExtendedTrustManager trustManager) { this.trustManager = trustManager; - this.environment = environment; } @Override @@ -241,59 +223,12 @@ abstract class TrustConfig { return trustManager.getAcceptedIssuers(); } - public synchronized void reload() { - X509ExtendedTrustManager[] array = loadAndMergeIfNecessary(environment); - this.trustManager = array[0]; - } - synchronized void setTrustManager(X509ExtendedTrustManager trustManager) { this.trustManager = trustManager; } - } - interface Reloadable { - - void reload(); - - interface Listener { - - void onReload(); - - void onFailure(Exception e); - - } - } - - protected static class ChangeListener implements FileChangesListener { - - private final List paths; - private final Reloadable reloadable; - private final Listener listener; - - protected ChangeListener(List paths, Reloadable reloadable, Listener listener) { - this.paths = paths; - this.reloadable = reloadable; - this.listener = listener; - } - - @Override - public void onFileDeleted(Path file) { - onFileChanged(file); - } - - @Override - public void onFileChanged(Path file) { - for (Path path : paths) { - if (file.equals(path)) { - try { - reloadable.reload(); - listener.onReload(); - } catch (Exception e) { - listener.onFailure(e); - } - break; - } - } + X509ExtendedTrustManager getTrustManager() { + return trustManager; } } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/activedirectory/AbstractActiveDirectoryIntegTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/activedirectory/AbstractActiveDirectoryIntegTests.java index ddac569df87..c17e6c5baa3 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/activedirectory/AbstractActiveDirectoryIntegTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/activedirectory/AbstractActiveDirectoryIntegTests.java @@ -43,7 +43,7 @@ public class AbstractActiveDirectoryIntegTests extends ESTestCase { } globalSettings = builder.build(); Environment environment = new Environment(globalSettings); - clientSSLService = new ClientSSLService(globalSettings, environment, new Global(globalSettings), null); + clientSSLService = new ClientSSLService(globalSettings, environment, new Global(globalSettings)); } Settings buildAdSettings(String ldapUrl, String adDomainName, String userSearchDN, LdapSearchScope scope, diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/GroupsResolverTestCase.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/GroupsResolverTestCase.java index d08e1758074..dde4ba5f99c 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/GroupsResolverTestCase.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/GroupsResolverTestCase.java @@ -40,7 +40,7 @@ public abstract class GroupsResolverTestCase extends ESTestCase { } Settings settings = builder.build(); Environment env = new Environment(settings); - ClientSSLService clientSSLService = new ClientSSLService(settings, env, new Global(settings), null); + ClientSSLService clientSSLService = new ClientSSLService(settings, env, new Global(settings)); LDAPURL ldapurl = new LDAPURL(ldapUrl()); LDAPConnectionOptions options = new LDAPConnectionOptions(); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactoryTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactoryTests.java index 60dfe8bbbe3..1ce62427315 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactoryTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactoryTests.java @@ -62,7 +62,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase { .put("xpack.security.ssl.keystore.path", keystore) .put("xpack.security.ssl.keystore.password", "changeit") .build(); - clientSSLService = new ClientSSLService(settings, env, new Global(settings), null); + clientSSLService = new ClientSSLService(settings, env, new Global(settings)); globalSettings = Settings.builder().put("path.home", createTempDir()).build(); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapTests.java index f746da1c6f6..57963d11341 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapTests.java @@ -52,7 +52,7 @@ public class OpenLdapTests extends ESTestCase { } globalSettings = builder.build(); Environment environment = new Environment(globalSettings); - clientSSLService = new ClientSSLService(globalSettings, environment, new Global(globalSettings), null); + clientSSLService = new ClientSSLService(globalSettings, environment, new Global(globalSettings)); } public void testConnect() throws Exception { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java index 0ba9bb9a680..826c2e42699 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ClientSSLServiceTests.java @@ -59,7 +59,7 @@ public class ClientSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.truststore.path", testclientStore) .put("xpack.security.ssl.truststore.password", "testclient") .build(); - ClientSSLService clientSSLService = new ClientSSLService(settings, null, new Global(settings), null); + ClientSSLService clientSSLService = new ClientSSLService(settings, null, new Global(settings)); clientSSLService.createSSLEngine(); fail("expected an exception"); } catch (ElasticsearchException e) { @@ -284,7 +284,6 @@ public class ClientSSLServiceTests extends ESTestCase { } private ClientSSLService createClientSSLService(Settings settings) { - ClientSSLService clientSSLService = new ClientSSLService(settings, env, new Global(settings), null); - return clientSSLService; + return new ClientSSLService(settings, env, new Global(settings)); } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationReloaderTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationReloaderTests.java new file mode 100644 index 00000000000..23b162b06d1 --- /dev/null +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationReloaderTests.java @@ -0,0 +1,652 @@ +/* + * 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.apache.lucene.util.SetOnce; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +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; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.concurrent.atomic.AtomicInteger; +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; + +/** + * Unit tests for the reloading of SSL configuration + */ +public class SSLConfigurationReloaderTests extends ESTestCase { + + private ThreadPool threadPool; + private ResourceWatcherService resourceWatcherService; + + @Before + public void setup() { + threadPool = new TestThreadPool("reload tests"); + resourceWatcherService = + new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); + resourceWatcherService.start(); + } + + @After + public void cleanup() throws Exception { + if (threadPool != null) { + terminate(threadPool); + } + } + + /** + * Tests reloading a keystore. The contents of the keystore is used for both keystore and truststore material, so both key + * config and trust config is checked. + */ + public void testReloadingKeyStore() throws Exception { + final Path tempDir = createTempDir(); + final Path keystorePath = tempDir.resolve("testnode.jks"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), keystorePath); + final Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .put("xpack.security.ssl.keystore.path", keystorePath) + .put("xpack.security.ssl.keystore.password", "testnode") + .build(); + final Environment env = randomBoolean() ? null : new Environment(settings); + + final BiFunction keyManagerPreChecks = (keyManager, config) -> { + // key manager checks + String[] aliases = keyManager.getServerAliases("RSA", null); + assertNotNull(aliases); + assertThat(aliases.length, is(1)); + assertThat(aliases[0], is("testnode")); + return null; + }; + + final SetOnce trustedCount = new SetOnce<>(); + final BiFunction trustManagerPreChecks = (trustManager, config) -> { + // trust manager checks + Certificate[] certificates = trustManager.getAcceptedIssuers(); + trustedCount.set(certificates.length); + return null; + }; + + final Runnable modifier = () -> { + try { + // modify it + KeyStore keyStore = KeyStore.getInstance("jks"); + keyStore.load(null, null); + Path updated = tempDir.resolve("updated.jks"); + try (OutputStream out = Files.newOutputStream(updated)) { + keyStore.store(out, "testnode".toCharArray()); + } + atomicMoveIfPossible(updated, keystorePath); + } catch (Exception e) { + throw new RuntimeException("modification failed", e); + } + }; + + final BiFunction trustManagerPostChecks = (updatedTrustManager, config) -> { + assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(5)); + return null; + }; + validateSSLConfigurationIsReloaded(settings, env, keyManagerPreChecks, trustManagerPreChecks, modifier, (k, c) -> null, + trustManagerPostChecks); + } + + /** + * Tests the reloading of a PEM key config when the key is overwritten. The trust portion is not tested as it is not modified by this + * test. + */ + public void testPEMKeyConfigReloading() throws Exception { + Path tempDir = createTempDir(); + Path keyPath = tempDir.resolve("testnode.pem"); + Path certPath = tempDir.resolve("testnode.crt"); + Path clientCertPath = tempDir.resolve("testclient.crt"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem"), keyPath); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"), certPath); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); + final Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .put("xpack.security.ssl.key.path", keyPath) + .put("xpack.security.ssl.key.password", "testnode") + .put("xpack.security.ssl.cert", certPath) + .putArray("xpack.security.ssl.ca", certPath.toString(), clientCertPath.toString()) + .build(); + final Environment env = randomBoolean() ? null : + new Environment(Settings.builder().put("path.home", createTempDir()).build()); + + final SetOnce privateKey = new SetOnce<>(); + final BiFunction keyManagerPreChecks = (keyManager, config) -> { + String[] aliases = keyManager.getServerAliases("RSA", null); + assertNotNull(aliases); + assertThat(aliases.length, is(1)); + assertThat(aliases[0], is("key")); + privateKey.set(keyManager.getPrivateKey("key")); + assertNotNull(privateKey.get()); + return null; + }; + + final KeyPair keyPair = CertUtils.generateKeyPair(randomFrom(1024, 2048)); + final Runnable modifier = () -> { + try { + // make sure we wait long enough to see a change. if time is within a second the file may not be seen as modified since the + // size is the same! + assertTrue(awaitBusy(() -> { + try { + BasicFileAttributes attributes = Files.readAttributes(keyPath, BasicFileAttributes.class); + return System.currentTimeMillis() - attributes.lastModifiedTime().toMillis() >= 1000L; + } catch (IOException e) { + throw new RuntimeException("io exception while checking time", e); + } + })); + Path updatedKeyPath = tempDir.resolve("updated.pem"); + try (OutputStream os = Files.newOutputStream(updatedKeyPath); + OutputStreamWriter osWriter = new OutputStreamWriter(os, StandardCharsets.UTF_8); + JcaPEMWriter writer = new JcaPEMWriter(osWriter)) { + writer.writeObject(keyPair, + new JcePEMEncryptorBuilder("DES-EDE3-CBC").setProvider(CertUtils.BC_PROV).build("testnode".toCharArray())); + } + atomicMoveIfPossible(updatedKeyPath, keyPath); + } catch (Exception e) { + throw new RuntimeException("failed to modify file", e); + } + }; + + final BiFunction keyManagerPostChecks = (keyManager, config) -> { + String[] aliases = keyManager.getServerAliases("RSA", null); + assertNotNull(aliases); + assertThat(aliases.length, is(1)); + assertThat(aliases[0], is("key")); + assertThat(keyManager.getPrivateKey(aliases[0]), not(equalTo(privateKey))); + assertThat(keyManager.getPrivateKey(aliases[0]), is(equalTo(keyPair.getPrivate()))); + return null; + }; + validateKeyConfigurationIsReloaded(settings, env, keyManagerPreChecks, modifier, keyManagerPostChecks); + } + + /** + * Tests the reloading of the trust config when the trust store is modified. The key config is not tested as part of this test. + */ + public void testReloadingTrustStore() throws Exception { + Path tempDir = createTempDir(); + Path trustStorePath = tempDir.resolve("testnode.jks"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), trustStorePath); + Settings settings = Settings.builder() + .put("xpack.security.ssl.truststore.path", trustStorePath) + .put("xpack.security.ssl.truststore.password", "testnode") + .put("path.home", createTempDir()) + .build(); + Environment env = randomBoolean() ? null : new Environment(settings); + + final SetOnce trustedCount = new SetOnce<>(); + final BiFunction trustManagerPreChecks = (trustManager, config) -> { + // trust manager checks + Certificate[] certificates = trustManager.getAcceptedIssuers(); + trustedCount.set(certificates.length); + return null; + }; + + + final Runnable modifier = () -> { + try { + Path updatedTruststore = tempDir.resolve("updated.jks"); + KeyStore keyStore = KeyStore.getInstance("jks"); + keyStore.load(null, null); + try (OutputStream out = Files.newOutputStream(updatedTruststore)) { + keyStore.store(out, "testnode".toCharArray()); + } + atomicMoveIfPossible(updatedTruststore, trustStorePath); + } catch (Exception e) { + throw new RuntimeException("failed to modify file", e); + } + }; + + final BiFunction trustManagerPostChecks = (updatedTrustManager, config) -> { + assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(5)); + return null; + }; + + validateTrustConfigurationIsReloaded(settings, env, trustManagerPreChecks, modifier, trustManagerPostChecks); + } + + /** + * Test the reloading of a trust config that is backed by PEM certificate files. The key config is not tested as we only care about the + * trust config in this test. + */ + public void testReloadingPEMTrustConfig() throws Exception { + Path tempDir = createTempDir(); + Path clientCertPath = tempDir.resolve("testclient.crt"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); + Settings settings = Settings.builder() + .putArray("xpack.security.ssl.ca", clientCertPath.toString()) + .put("path.home", createTempDir()) + .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), false) + .build(); + Environment env = randomBoolean() ? null : new Environment(settings); + + final BiFunction trustManagerPreChecks = (trustManager, config) -> { + // trust manager checks + Certificate[] certificates = trustManager.getAcceptedIssuers(); + assertThat(certificates.length, is(1)); + assertThat(((X509Certificate)certificates[0]).getSubjectX500Principal().getName(), containsString("Test Client")); + return null; + }; + + final Runnable modifier = () -> { + try { + Path updatedCert = tempDir.resolve("updated.crt"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"), updatedCert, + StandardCopyOption.REPLACE_EXISTING); + atomicMoveIfPossible(updatedCert, clientCertPath); + } catch (Exception e) { + throw new RuntimeException("failed to modify file", e); + } + }; + + final BiFunction trustManagerPostChecks = (updatedTrustManager, config) -> { + Certificate[] updatedCerts = updatedTrustManager.getAcceptedIssuers(); + assertThat(updatedCerts.length, is(1)); + assertThat(((X509Certificate)updatedCerts[0]).getSubjectX500Principal().getName(), containsString("Test Node")); + return null; + }; + + validateTrustConfigurationIsReloaded(settings, env, trustManagerPreChecks, modifier, trustManagerPostChecks); + } + + /** + * Tests the reloading of a keystore when there is an exception during reloading. An exception is caused by truncating the keystore + * that is being monitored + */ + public void testReloadingKeyStoreException() throws Exception { + Path tempDir = createTempDir(); + Path keystorePath = tempDir.resolve("testnode.jks"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), keystorePath); + Settings settings = Settings.builder() + .put("xpack.security.ssl.keystore.path", keystorePath) + .put("xpack.security.ssl.keystore.password", "testnode") + .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) { + @Override + SSLContext getSSLContext(SSLConfiguration configuration) { + fail("get should not be called! [keystore reload exception]"); + return super.getSSLContext(configuration); + } + }; + 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"); + + // 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))); + } + + /** + * Tests the reloading of a key config backed by pem files when there is an exception during reloading. An exception is caused by + * truncating the key file that is being monitored + */ + public void testReloadingPEMKeyConfigException() throws Exception { + Path tempDir = createTempDir(); + Path keyPath = tempDir.resolve("testnode.pem"); + Path certPath = tempDir.resolve("testnode.crt"); + Path clientCertPath = tempDir.resolve("testclient.crt"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem"), keyPath); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"), certPath); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); + Settings settings = Settings.builder() + .put("xpack.security.ssl.key.path", keyPath) + .put("xpack.security.ssl.key.password", "testnode") + .put("xpack.security.ssl.cert", certPath) + .putArray("xpack.security.ssl.ca", certPath.toString(), clientCertPath.toString()) + .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) { + @Override + SSLContext getSSLContext(SSLConfiguration configuration) { + fail("get should not be called! [pem key reload exception]"); + return super.getSSLContext(configuration); + } + }; + 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"); + + // 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))); + } + + /** + * Tests the reloading of a truststore when there is an exception during reloading. An exception is caused by truncating the truststore + * that is being monitored + */ + public void testTrustStoreReloadException() throws Exception { + Path tempDir = createTempDir(); + Path trustStorePath = tempDir.resolve("testnode.jks"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), trustStorePath); + Settings settings = Settings.builder() + .put("xpack.security.ssl.truststore.path", trustStorePath) + .put("xpack.security.ssl.truststore.password", "testnode") + .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) { + @Override + SSLContext getSSLContext(SSLConfiguration configuration) { + fail("get should not be called! [truststore reload exception]"); + return super.getSSLContext(configuration); + } + }; + 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")); + + // 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")); + } + + /** + * Tests the reloading of a trust config backed by pem files when there is an exception during reloading. An exception is caused by + * truncating the certificate file that is being monitored + */ + public void testPEMTrustReloadException() throws Exception { + Path tempDir = createTempDir(); + Path clientCertPath = tempDir.resolve("testclient.crt"); + Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); + Settings settings = Settings.builder() + .putArray("xpack.security.ssl.ca", clientCertPath.toString()) + .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) { + @Override + SSLContext getSSLContext(SSLConfiguration configuration) { + fail("get should not be called! [pem trust reload exception]"); + return super.getSSLContext(configuration); + } + }; + 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")); + + // write bad file + Path updatedCert = tempDir.resolve("updated.crt"); + try (OutputStream os = Files.newOutputStream(updatedCert)) { + os.write(randomByte()); + } + 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")); + } + + /** + * Validates the trust configuration aspect of the SSLConfiguration is reloaded + */ + private void validateTrustConfigurationIsReloaded(Settings settings, Environment env, + BiFunction trustManagerPreChecks, + Runnable modificationFunction, + BiFunction trustManagerPostChecks) + throws Exception { + validateSSLConfigurationIsReloaded(settings, env, false, true, null, trustManagerPreChecks, modificationFunction, null, + trustManagerPostChecks); + } + + /** + * Validates the trust configuration aspect of the SSLConfiguration is reloaded + */ + private void validateKeyConfigurationIsReloaded(Settings settings, Environment env, + BiFunction keyManagerPreChecks, + Runnable modificationFunction, + BiFunction keyManagerPostChecks) + throws Exception { + validateSSLConfigurationIsReloaded(settings, env, true, false, keyManagerPreChecks, null, modificationFunction, + keyManagerPostChecks, null); + } + + /** + * Validates that both the key and trust configuration aspects of the SSLConfiguration are reloaded + */ + private void validateSSLConfigurationIsReloaded(Settings settings, Environment env, + BiFunction keyManagerPreChecks, + BiFunction trustManagerPreChecks, + Runnable modificationFunction, + BiFunction keyManagerPostChecks, + BiFunction trustManagerPostChecks) + throws Exception { + validateSSLConfigurationIsReloaded(settings, env, true, true, keyManagerPreChecks, trustManagerPreChecks, modificationFunction, + keyManagerPostChecks, trustManagerPostChecks); + } + + private void validateSSLConfigurationIsReloaded(Settings settings, Environment env, boolean checkKeys, boolean checkTrust, + BiFunction keyManagerPreChecks, + BiFunction trustManagerPreChecks, + Runnable modificationFunction, + BiFunction keyManagerPostChecks, + BiFunction 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) { + @Override + SSLContext getSSLContext(SSLConfiguration sslConfiguration) { + serverCounter.incrementAndGet(); + return super.getSSLContext(sslConfiguration); + } + }; + 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(); + } 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(); + } 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()); + + // modify + modificationFunction.run(); + assertTrue(awaitBusy(() -> clientCounter.get() > 0 && serverCounter.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(); + 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(); + assertThat(updatedTrustManager, not(sameInstance(trustManager))); + trustManagerPostChecks.apply(updatedTrustManager, config); + } + } + + private void assertContainsCertificateWithMatchingName(Certificate[] certificates, Matcher 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); + } catch (AtomicMoveNotSupportedException e) { + Files.move(source, target, StandardCopyOption.REPLACE_EXISTING); + } + } +} diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationTests.java index cc5cc32cdf8..5f417c0a5b2 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/SSLConfigurationTests.java @@ -5,45 +5,18 @@ */ package org.elasticsearch.xpack.security.ssl; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder; -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.Custom; import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global; -import org.elasticsearch.xpack.security.ssl.TrustConfig.Reloadable.Listener; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.threadpool.TestThreadPool; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509ExtendedTrustManager; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.AtomicMoveNotSupportedException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.attribute.BasicFileAttributes; -import java.security.KeyPair; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; @@ -273,10 +246,10 @@ 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, null, null); + KeyManager[] keyManagers = keyConfig.keyManagers(env); assertThat(keyManagers.length, is(1)); assertThat(config.trustConfig(), sameInstance(keyConfig)); - TrustManager[] trustManagers = keyConfig.trustManagers(env, null, null); + TrustManager[] trustManagers = keyConfig.trustManagers(env); assertThat(trustManagers.length, is(1)); } @@ -296,464 +269,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, null, null); + KeyManager[] keyManagers = keyConfig.keyManagers(env); assertThat(keyManagers.length, is(1)); assertThat(config.trustConfig(), not(sameInstance(keyConfig))); assertThat(config.trustConfig(), instanceOf(PEMTrustConfig.class)); - TrustManager[] trustManagers = keyConfig.trustManagers(env, null, null); + TrustManager[] trustManagers = keyConfig.trustManagers(env); assertThat(trustManagers.length, is(1)); } - - public void testReloadingKeyStore() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path keystorePath = tempDir.resolve("testnode.jks"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), keystorePath); - Settings settings = Settings.builder() - .put("xpack.security.ssl.keystore.path", keystorePath) - .put("xpack.security.ssl.keystore.password", "testnode") - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean()) - .build(); - - SSLConfiguration config = new Global(settings); - assertThat(config.keyConfig(), instanceOf(StoreKeyConfig.class)); - StoreKeyConfig keyConfig = (StoreKeyConfig) config.keyConfig(); - - CountDownLatch latch = new CountDownLatch(2); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener); - assertThat(keyManagers.length, is(1)); - assertThat(keyManagers[0], instanceOf(X509ExtendedKeyManager.class)); - X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0]; - String[] aliases = keyManager.getServerAliases("RSA", null); - assertNotNull(aliases); - assertThat(aliases.length, is(1)); - assertThat(aliases[0], is("testnode")); - TrustManager[] trustManagers = keyConfig.trustManagers(env, resourceWatcherService, listener); - assertThat(trustManagers.length, is(1)); - assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class)); - X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0]; - Certificate[] certificates = trustManager.getAcceptedIssuers(); - final int trustedCount = certificates.length; - assertThat(latch.getCount(), is(2L)); - - KeyStore keyStore = KeyStore.getInstance("jks"); - keyStore.load(null, null); - Path updated = tempDir.resolve("updated.jks"); - try (OutputStream out = Files.newOutputStream(updated)) { - keyStore.store(out, "testnode".toCharArray()); - } - atomicMoveIfPossible(updated, keystorePath); - latch.await(); - assertThat(exceptionRef.get(), is(nullValue())); - aliases = keyManager.getServerAliases("RSA", null); - assertThat(aliases, is(nullValue())); - certificates = trustManager.getAcceptedIssuers(); - assertThat(trustedCount - certificates.length, is(5)); - } finally { - threadPool.shutdown(); - } - } - - public void testReloadingPEMKeyConfig() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path keyPath = tempDir.resolve("testnode.pem"); - Path certPath = tempDir.resolve("testnode.crt"); - Path clientCertPath = tempDir.resolve("testclient.crt"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem"), keyPath); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"), certPath); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); - Settings settings = Settings.builder() - .put("xpack.security.ssl.key.path", keyPath) - .put("xpack.security.ssl.key.password", "testnode") - .put("xpack.security.ssl.cert", certPath) - .putArray("xpack.security.ssl.ca", certPath.toString(), clientCertPath.toString()) - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean()) - .build(); - SSLConfiguration config = new Global(settings); - assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class)); - PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig(); - - CountDownLatch latch = new CountDownLatch(2); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload pem"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener); - assertThat(keyManagers.length, is(1)); - assertThat(keyManagers[0], instanceOf(X509ExtendedKeyManager.class)); - X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0]; - String[] aliases = keyManager.getServerAliases("RSA", null); - assertThat(aliases, is(notNullValue())); - assertThat(aliases.length, is(1)); - assertThat(aliases[0], is("key")); - PrivateKey privateKey = keyManager.getPrivateKey(aliases[0]); - TrustManager[] trustManagers = keyConfig.trustManagers(env, resourceWatcherService, listener); - assertThat(trustManagers.length, is(1)); - assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class)); - X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0]; - Certificate[] certificates = trustManager.getAcceptedIssuers(); - final int trustedCount = certificates.length; - assertThat(latch.getCount(), is(2L)); - - // make sure we wait enough to see a change. if time is within a second the file may not be seen as modified since the size is - // the same! - awaitBusy(() -> { - try { - BasicFileAttributes attributes = Files.readAttributes(keyPath, BasicFileAttributes.class); - return System.currentTimeMillis() - attributes.lastModifiedTime().toMillis() >= 1000L; - } catch (IOException e) { - throw new ElasticsearchException("io exception while checking time", e); - } - }); - Path updatedKeyPath = tempDir.resolve("updated.pem"); - KeyPair keyPair = CertUtils.generateKeyPair(randomFrom(1024, 2048)); - try (OutputStream os = Files.newOutputStream(updatedKeyPath); - OutputStreamWriter osWriter = new OutputStreamWriter(os, StandardCharsets.UTF_8); - JcaPEMWriter writer = new JcaPEMWriter(osWriter)) { - writer.writeObject(keyPair, - new JcePEMEncryptorBuilder("DES-EDE3-CBC").setProvider(CertUtils.BC_PROV).build("testnode".toCharArray())); - } - atomicMoveIfPossible(updatedKeyPath, keyPath); - - latch.await(); - assertThat(exceptionRef.get(), is(nullValue())); - aliases = keyManager.getServerAliases("RSA", null); - assertThat(aliases, is(notNullValue())); - assertThat(aliases.length, is(1)); - assertThat(aliases[0], is("key")); - assertThat(keyManager.getPrivateKey(aliases[0]), not(equalTo(privateKey))); - assertThat(keyManager.getPrivateKey(aliases[0]), is(equalTo(keyPair.getPrivate()))); - certificates = trustManager.getAcceptedIssuers(); - assertThat(trustedCount - certificates.length, is(0)); - } finally { - threadPool.shutdown(); - } - } - - public void testReloadingTrustStore() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path trustStorePath = tempDir.resolve("testnode.jks"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), trustStorePath); - Settings settings = Settings.builder() - .put("xpack.security.ssl.truststore.path", trustStorePath) - .put("xpack.security.ssl.truststore.password", "testnode") - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean()) - .build(); - - SSLConfiguration config = new Global(settings); - assertThat(config.trustConfig(), instanceOf(StoreTrustConfig.class)); - StoreTrustConfig trustConfig = (StoreTrustConfig) config.trustConfig(); - - CountDownLatch latch = new CountDownLatch(1); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener); - assertThat(trustManagers.length, is(1)); - assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class)); - X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0]; - Certificate[] certificates = trustManager.getAcceptedIssuers(); - final int trustedCount = certificates.length; - - assertThat(latch.getCount(), is(1L)); - - Path updatedTruststore = tempDir.resolve("updated.jks"); - KeyStore keyStore = KeyStore.getInstance("jks"); - keyStore.load(null, null); - try (OutputStream out = Files.newOutputStream(updatedTruststore)) { - keyStore.store(out, "testnode".toCharArray()); - } - atomicMoveIfPossible(updatedTruststore, trustStorePath); - latch.await(); - assertThat(exceptionRef.get(), is(nullValue())); - certificates = trustManager.getAcceptedIssuers(); - assertThat(trustedCount - certificates.length, is(5)); - } finally { - threadPool.shutdown(); - } - } - - public void testReloadingPEMTrustConfig() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path clientCertPath = tempDir.resolve("testclient.crt"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); - Settings settings = Settings.builder() - .putArray("xpack.security.ssl.ca", clientCertPath.toString()) - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), false) - .build(); - SSLConfiguration config = new Global(settings); - assertThat(config.trustConfig(), instanceOf(PEMTrustConfig.class)); - PEMTrustConfig trustConfig = (PEMTrustConfig) config.trustConfig(); - CountDownLatch latch = new CountDownLatch(1); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener); - assertThat(trustManagers.length, is(1)); - assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class)); - X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0]; - Certificate[] certificates = trustManager.getAcceptedIssuers(); - assertThat(certificates.length, is(1)); - assertThat(((X509Certificate)certificates[0]).getSubjectX500Principal().getName(), containsString("Test Client")); - assertThat(latch.getCount(), is(1L)); - - Path updatedCert = tempDir.resolve("updated.crt"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"), updatedCert, - StandardCopyOption.REPLACE_EXISTING); - atomicMoveIfPossible(updatedCert, clientCertPath); - latch.await(); - assertThat(exceptionRef.get(), is(nullValue())); - Certificate[] updatedCerts = trustManager.getAcceptedIssuers(); - assertThat(updatedCerts.length, is(1)); - assertThat(((X509Certificate)updatedCerts[0]).getSubjectX500Principal().getName(), containsString("Test Node")); - assertThat(updatedCerts[0], not(equalTo(certificates[0]))); - } finally { - threadPool.shutdown(); - } - } - - public void testReloadingKeyStoreException() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path keystorePath = tempDir.resolve("testnode.jks"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), keystorePath); - Settings settings = Settings.builder() - .put("xpack.security.ssl.keystore.path", keystorePath) - .put("xpack.security.ssl.keystore.password", "testnode") - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean()) - .build(); - - SSLConfiguration config = new Global(settings); - assertThat(config.keyConfig(), instanceOf(StoreKeyConfig.class)); - StoreKeyConfig keyConfig = (StoreKeyConfig) config.keyConfig(); - - CountDownLatch latch = new CountDownLatch(1); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener); - X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0]; - String[] aliases = keyManager.getServerAliases("RSA", null); - assertNotNull(aliases); - assertThat(aliases.length, is(1)); - assertThat(aliases[0], is("testnode")); - assertThat(latch.getCount(), is(1L)); - - // truncate the keystore - try (OutputStream out = Files.newOutputStream(keystorePath)) { - } - latch.await(); - assertThat(exceptionRef.get(), notNullValue()); - assertThat(exceptionRef.get(), instanceOf(ElasticsearchException.class)); - assertThat(keyManager.getServerAliases("RSA", null), equalTo(aliases)); - } finally { - threadPool.shutdown(); - } - } - - public void testReloadingPEMKeyConfigException() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path keyPath = tempDir.resolve("testnode.pem"); - Path certPath = tempDir.resolve("testnode.crt"); - Path clientCertPath = tempDir.resolve("testclient.crt"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem"), keyPath); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"), certPath); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); - Settings settings = Settings.builder() - .put("xpack.security.ssl.key.path", keyPath) - .put("xpack.security.ssl.key.password", "testnode") - .put("xpack.security.ssl.cert", certPath) - .putArray("xpack.security.ssl.ca", certPath.toString(), clientCertPath.toString()) - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean()) - .build(); - SSLConfiguration config = new Global(settings); - assertThat(config.keyConfig(), instanceOf(PEMKeyConfig.class)); - PEMKeyConfig keyConfig = (PEMKeyConfig) config.keyConfig(); - - CountDownLatch latch = new CountDownLatch(1); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload pem"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - KeyManager[] keyManagers = keyConfig.keyManagers(env, resourceWatcherService, listener); - assertThat(keyManagers.length, is(1)); - assertThat(keyManagers[0], instanceOf(X509ExtendedKeyManager.class)); - X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0]; - String[] aliases = keyManager.getServerAliases("RSA", null); - assertThat(aliases, is(notNullValue())); - assertThat(aliases.length, is(1)); - assertThat(aliases[0], is("key")); - PrivateKey privateKey = keyManager.getPrivateKey(aliases[0]); - assertThat(latch.getCount(), is(1L)); - - // pick a random file to truncate - Path toTruncate = randomFrom(keyPath, certPath); - - // truncate the file - try (OutputStream os = Files.newOutputStream(toTruncate)) { - } - - latch.await(); - assertThat(exceptionRef.get(), is(instanceOf(ElasticsearchException.class))); - assertThat(keyManager.getServerAliases("RSA", null), equalTo(aliases)); - assertThat(keyManager.getPrivateKey(aliases[0]), is(equalTo(privateKey))); - } finally { - threadPool.shutdown(); - } - } - - public void testTrustStoreReloadException() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path trustStorePath = tempDir.resolve("testnode.jks"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"), trustStorePath); - Settings settings = Settings.builder() - .put("xpack.security.ssl.truststore.path", trustStorePath) - .put("xpack.security.ssl.truststore.password", "testnode") - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), randomBoolean()) - .build(); - - SSLConfiguration config = new Global(settings); - assertThat(config.trustConfig(), instanceOf(StoreTrustConfig.class)); - StoreTrustConfig trustConfig = (StoreTrustConfig) config.trustConfig(); - - CountDownLatch latch = new CountDownLatch(1); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener); - assertThat(trustManagers.length, is(1)); - assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class)); - X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0]; - Certificate[] certificates = trustManager.getAcceptedIssuers(); - - // truncate the truststore - try (OutputStream os = Files.newOutputStream(trustStorePath)) { - } - - latch.await(); - assertThat(exceptionRef.get(), instanceOf(ElasticsearchException.class)); - assertThat(trustManager.getAcceptedIssuers(), equalTo(certificates)); - } finally { - threadPool.shutdown(); - } - } - - public void testPEMTrustReloadException() throws Exception { - Environment env = randomBoolean() ? null : - new Environment(Settings.builder().put("path.home", createTempDir()).build()); - Path tempDir = createTempDir(); - Path clientCertPath = tempDir.resolve("testclient.crt"); - Files.copy(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"), clientCertPath); - Settings settings = Settings.builder() - .putArray("xpack.security.ssl.ca", clientCertPath.toString()) - .put(Global.INCLUDE_JDK_CERTS_SETTING.getKey(), false) - .build(); - SSLConfiguration config = new Global(settings); - assertThat(config.trustConfig(), instanceOf(PEMTrustConfig.class)); - PEMTrustConfig trustConfig = (PEMTrustConfig) config.trustConfig(); - CountDownLatch latch = new CountDownLatch(1); - AtomicReference exceptionRef = new AtomicReference<>(); - Listener listener = createRefreshListener(latch, exceptionRef); - - ThreadPool threadPool = new TestThreadPool("reload"); - try { - ResourceWatcherService resourceWatcherService = - new ResourceWatcherService(Settings.builder().put("resource.reload.interval.high", "1s").build(), threadPool); - resourceWatcherService.start(); - TrustManager[] trustManagers = trustConfig.trustManagers(env, resourceWatcherService, listener); - assertThat(trustManagers.length, is(1)); - assertThat(trustManagers[0], instanceOf(X509ExtendedTrustManager.class)); - X509ExtendedTrustManager trustManager = (X509ExtendedTrustManager) trustManagers[0]; - Certificate[] certificates = trustManager.getAcceptedIssuers(); - assertThat(certificates.length, is(1)); - assertThat(((X509Certificate) certificates[0]).getSubjectX500Principal().getName(), containsString("Test Client")); - assertThat(latch.getCount(), is(1L)); - - // write bad file - Path updatedCert = tempDir.resolve("updated.crt"); - try (OutputStream os = Files.newOutputStream(updatedCert)) { - os.write(randomByte()); - } - atomicMoveIfPossible(updatedCert, clientCertPath); - - latch.await(); - assertThat(exceptionRef.get(), instanceOf(ElasticsearchException.class)); - assertThat(trustManager.getAcceptedIssuers(), equalTo(certificates)); - } finally { - threadPool.shutdown(); - } - } - - private Listener createRefreshListener(CountDownLatch latch, AtomicReference exceptionRef) { - return new Listener() { - @Override - public void onReload() { - logger.info("refresh called"); - latch.countDown(); - } - - @Override - public void onFailure(Exception e) { - logger.error("exception " + e); - exceptionRef.set(e); - latch.countDown(); - } - }; - } - - private void atomicMoveIfPossible(Path source, Path target) throws IOException { - try { - Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - } catch (AtomicMoveNotSupportedException e) { - Files.move(source, target, StandardCopyOption.REPLACE_EXISTING); - } - } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java index 6d633279f52..d179604291f 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/ssl/ServerSSLServiceTests.java @@ -53,7 +53,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.truststore.password", "testnode") .build(); try { - new ServerSSLService(settings, env, new Global(settings), null).createSSLEngine(); + new ServerSSLService(settings, env, new Global(settings)).createSSLEngine(); fail("expected an exception"); } catch (ElasticsearchException e) { assertThat(e.getMessage(), containsString("failed to initialize the SSLContext")); @@ -67,7 +67,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.path", testnodeStore) .put("xpack.security.ssl.keystore.password", "testnode") .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); Settings.Builder settingsBuilder = Settings.builder() .put("truststore.path", testClientStore) @@ -85,7 +85,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.path", testnodeStore) .put("xpack.security.ssl.keystore.password", "testnode") .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); SSLContext sslContext = sslService.sslContext(); SSLContext cachedSslContext = sslService.sslContext(); @@ -101,7 +101,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.password", "testnode") .put("xpack.security.ssl.keystore.key_password", "testnode1") .build(); - new ServerSSLService(settings, env, new Global(settings), null).createSSLEngine(); + new ServerSSLService(settings, env, new Global(settings)).createSSLEngine(); } public void testIncorrectKeyPasswordThrowsException() throws Exception { @@ -112,7 +112,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.path", differentPasswordsStore) .put("xpack.security.ssl.keystore.password", "testnode") .build(); - new ServerSSLService(settings, env, new Global(settings), null).createSSLEngine(); + new ServerSSLService(settings, env, new Global(settings)).createSSLEngine(); fail("expected an exception"); } catch (ElasticsearchException e) { assertThat(e.getMessage(), containsString("failed to initialize a KeyManagerFactory")); @@ -124,7 +124,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.path", testnodeStore) .put("xpack.security.ssl.keystore.password", "testnode") .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); SSLEngine engine = sslService.createSSLEngine(); assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3"))); } @@ -134,7 +134,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.path", testnodeStore) .put("xpack.security.ssl.keystore.password", "testnode") .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + 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())); @@ -147,14 +147,14 @@ public class ServerSSLServiceTests extends ESTestCase { .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), null); + 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), null); + ServerSSLService sslService = new ServerSSLService(Settings.EMPTY, env, new Global(Settings.EMPTY)); try { sslService.createSSLEngine(); fail("Expected IllegalArgumentException"); @@ -168,7 +168,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.truststore.path", testnodeStore) .put("xpack.security.ssl.truststore.password", "testnode") .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); try { sslService.createSSLEngine(); fail("Expected IllegalArgumentException"); @@ -183,7 +183,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.password", "testnode") .put("xpack.security.ssl.truststore.path", testnodeStore) .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); try { sslService.sslContext(); fail("Expected IllegalArgumentException"); @@ -196,7 +196,7 @@ public class ServerSSLServiceTests extends ESTestCase { Settings settings = Settings.builder() .put("xpack.security.ssl.keystore.path", testnodeStore) .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); try { sslService.sslContext(); fail("Expected IllegalArgumentException"); @@ -214,7 +214,7 @@ public class ServerSSLServiceTests extends ESTestCase { .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), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); SSLEngine engine = sslService.createSSLEngine(); assertThat(engine, is(notNullValue())); String[] enabledCiphers = engine.getEnabledCipherSuites(); @@ -227,7 +227,7 @@ public class ServerSSLServiceTests extends ESTestCase { .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), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); try { sslService.createSSLEngine(); fail("Expected IllegalArgumentException"); @@ -241,7 +241,7 @@ public class ServerSSLServiceTests extends ESTestCase { .put("xpack.security.ssl.keystore.path", testnodeStore) .put("xpack.security.ssl.keystore.password", "testnode") .build(); - ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings), null); + 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)); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/HandshakeWaitingHandlerTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/HandshakeWaitingHandlerTests.java index 7f40fff068d..8eed8b303b0 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/HandshakeWaitingHandlerTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/HandshakeWaitingHandlerTests.java @@ -76,7 +76,7 @@ public class HandshakeWaitingHandlerTests 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), null); + ServerSSLService sslService = new ServerSSLService(settings, env, new Global(settings)); sslContext = sslService.sslContext(); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3HttpServerTransportTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3HttpServerTransportTests.java index 235ad157c7d..e81725bb5ec 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3HttpServerTransportTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3HttpServerTransportTests.java @@ -43,7 +43,7 @@ public class SecurityNetty3HttpServerTransportTests extends ESTestCase { .put("xpack.security.ssl.keystore.password", "testnode") .build(); Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build()); - serverSSLService = new ServerSSLService(settings, env, new Global(settings), null); + serverSSLService = new ServerSSLService(settings, env, new Global(settings)); } public void testDefaultClientAuth() throws Exception { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3TransportTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3TransportTests.java index ad55149de6f..22a4b86da80 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3TransportTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/SecurityNetty3TransportTests.java @@ -43,8 +43,8 @@ public class SecurityNetty3TransportTests extends ESTestCase { .build(); Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build()); Global globalSSLConfiguration = new Global(settings); - serverSSLService = new ServerSSLService(settings, env, globalSSLConfiguration, null); - clientSSLService = new ClientSSLService(settings, env, globalSSLConfiguration, null); + serverSSLService = new ServerSSLService(settings, env, globalSSLConfiguration); + clientSSLService = new ClientSSLService(settings, env, globalSSLConfiguration); } public void testThatSSLCanBeDisabledByProfile() throws Exception { diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java index 5c5a42c36a6..fee11261e0e 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslClientAuthTests.java @@ -74,7 +74,7 @@ public class SslClientAuthTests extends SecurityIntegTestCase { 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), null); + ClientSSLService sslService = new ClientSSLService(settings, null, new Global(settings)); SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sslService.sslContext(), NoopHostnameVerifier.INSTANCE); try (RestClient restClient = createRestClient(httpClientBuilder -> httpClientBuilder.setSSLStrategy(sessionStrategy), "https")) { Response response = restClient.performRequest("GET", "/", diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java index 1fb2bd8f71a..817a44e0b9d 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java @@ -99,7 +99,7 @@ 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), null); + ClientSSLService service = new ClientSSLService(settings, null, new Global(settings)); CredentialsProvider provider = new BasicCredentialsProvider(); provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nodeClientUsername(),