diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4Transport.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4Transport.java index b761439b15b6a..f828a82d95f6c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4Transport.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4Transport.java @@ -28,12 +28,12 @@ import org.elasticsearch.xpack.core.ssl.SSLService; import javax.net.ssl.SSLEngine; - import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; import static org.elasticsearch.xpack.core.security.SecurityField.setting; @@ -58,22 +58,9 @@ public SecurityNetty4Transport( super(settings, threadPool, networkService, bigArrays, namedWriteableRegistry, circuitBreakerService); this.sslService = sslService; this.sslEnabled = XPackSettings.TRANSPORT_SSL_ENABLED.get(settings); - final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl.")); if (sslEnabled) { - this.sslConfiguration = sslService.sslConfiguration(transportSSLSettings, Settings.EMPTY); - Map profileSettingsMap = settings.getGroups("transport.profiles.", true); - Map profileConfiguration = new HashMap<>(profileSettingsMap.size() + 1); - for (Map.Entry entry : profileSettingsMap.entrySet()) { - Settings profileSettings = entry.getValue(); - final Settings profileSslSettings = profileSslSettings(profileSettings); - SSLConfiguration configuration = sslService.sslConfiguration(profileSslSettings, transportSSLSettings); - profileConfiguration.put(entry.getKey(), configuration); - } - - if (profileConfiguration.containsKey(TcpTransport.DEFAULT_PROFILE) == false) { - profileConfiguration.put(TcpTransport.DEFAULT_PROFILE, sslConfiguration); - } - + this.sslConfiguration = sslService.getSSLConfiguration(setting("transport.ssl.")); + Map profileConfiguration = getTransportProfileConfigurations(settings, sslService, sslConfiguration); this.profileConfiguration = Collections.unmodifiableMap(profileConfiguration); } else { this.profileConfiguration = Collections.emptyMap(); @@ -81,6 +68,21 @@ public SecurityNetty4Transport( } } + public static Map getTransportProfileConfigurations(Settings settings, SSLService sslService, + SSLConfiguration defaultConfiguration) { + Set profileNames = settings.getGroups("transport.profiles.", true).keySet(); + Map profileConfiguration = new HashMap<>(profileNames.size() + 1); + for (String profileName : profileNames) { + SSLConfiguration configuration = sslService.getSSLConfiguration("transport.profiles." + profileName + "." + setting("ssl")); + profileConfiguration.put(profileName, configuration); + } + + if (profileConfiguration.containsKey(TcpTransport.DEFAULT_PROFILE) == false) { + profileConfiguration.put(TcpTransport.DEFAULT_PROFILE, defaultConfiguration); + } + return profileConfiguration; + } + @Override protected void doStart() { super.doStart(); @@ -209,8 +211,4 @@ public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, super.connect(ctx, remoteAddress, localAddress, promise); } } - - public static Settings profileSslSettings(Settings profileSettings) { - return profileSettings.getByPrefix(setting("ssl.")); - } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationSettings.java index 8952619408b4a..0cc01270ced53 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationSettings.java @@ -22,6 +22,8 @@ import java.util.Locale; import java.util.Optional; import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Bridges SSLConfiguration into the {@link Settings} framework, using {@link Setting} objects. @@ -221,4 +223,10 @@ public static Collection> getProfileSettings() { CLIENT_AUTH_SETTING_PROFILES, VERIFICATION_MODE_SETTING_PROFILES); } + public List> getSecureSettingsInUse(Settings settings) { + return Stream.of(this.truststorePassword, this.x509KeyPair.keystorePassword, + this.x509KeyPair.keystoreKeyPassword, this.x509KeyPair.keyPassword) + .filter(s -> s.exists(settings)) + .collect(Collectors.toList()); + } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java index 9cb285b939429..07d438b243b5b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java @@ -30,7 +30,6 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; - import java.io.IOException; import java.net.InetAddress; import java.net.Socket; @@ -49,8 +48,10 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; /** * Provides access to {@link SSLEngine} and {@link SSLSocketFactory} objects based on a provided configuration. All @@ -58,7 +59,23 @@ */ public class SSLService extends AbstractComponent { + /** + * This is a mapping from "context name" (in general use, the name of a setting key) + * to a configuration. + * This allows us to easily answer the question "What is the configuration for ssl in realm XYZ?" + * Multiple "context names" may map to the same configuration (either by object-identity or by object-equality). + * For example "xpack.http.ssl" may exist as a name in this map and have the global ssl configuration as a value + */ + private final Map sslConfigurations; + + /** + * A mapping from a SSLConfiguration to a pre-built context. + *

+ * This is managed separately to the {@link #sslConfigurations} map, so that a single configuration (by object equality) + * always maps to the same {@link SSLContextHolder}, even if it is being used within a different context-name. + */ private final Map sslContexts; + private final SSLConfiguration globalSSLConfiguration; private final SetOnce transportSSLConfiguration = new SetOnce<>(); private final Environment env; @@ -71,14 +88,16 @@ public SSLService(Settings settings, Environment environment) { super(settings); this.env = environment; this.globalSSLConfiguration = new SSLConfiguration(settings.getByPrefix(XPackSettings.GLOBAL_SSL_PREFIX)); + this.sslConfigurations = new HashMap<>(); this.sslContexts = loadSSLConfigurations(); } private SSLService(Settings settings, Environment environment, SSLConfiguration globalSSLConfiguration, - Map sslContexts) { + Map sslConfigurations, Map sslContexts) { super(settings); this.env = environment; this.globalSSLConfiguration = globalSSLConfiguration; + this.sslConfigurations = sslConfigurations; this.sslContexts = sslContexts; } @@ -88,7 +107,7 @@ private SSLService(Settings settings, Environment environment, SSLConfiguration * have been created during initialization */ public SSLService createDynamicSSLService() { - return new SSLService(settings, env, globalSSLConfiguration, sslContexts) { + return new SSLService(settings, env, globalSSLConfiguration, sslConfigurations, sslContexts) { @Override Map loadSSLConfigurations() { @@ -119,9 +138,17 @@ SSLContextHolder sslContextHolder(SSLConfiguration sslConfiguration) { * @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. An empty settings will return * a context created from the default configuration * @return Never {@code null}. + * @deprecated This method will fail if the SSL configuration uses a {@link org.elasticsearch.common.settings.SecureSetting} but the + * {@link org.elasticsearch.common.settings.SecureSettings} have been closed. Use {@link #getSSLConfiguration(String)} + * and {@link #sslIOSessionStrategy(SSLConfiguration)} (Deprecated, but not removed because monitoring uses dynamic SSL settings) */ + @Deprecated public SSLIOSessionStrategy sslIOSessionStrategy(Settings settings) { SSLConfiguration config = sslConfiguration(settings); + return sslIOSessionStrategy(config); + } + + public SSLIOSessionStrategy sslIOSessionStrategy(SSLConfiguration config) { SSLContext sslContext = sslContext(config); String[] ciphers = supportedCiphers(sslParameters(sslContext).getCipherSuites(), config.cipherSuites(), false); String[] supportedProtocols = config.supportedProtocols().toArray(Strings.EMPTY_ARRAY); @@ -163,51 +190,15 @@ SSLIOSessionStrategy sslIOSessionStrategy(SSLContext sslContext, String[] protoc } /** - * Create a new {@link SSLSocketFactory} based on the provided settings. The settings are used to identify the ssl configuration that - * should be used to create the socket factory. The socket factory will also properly configure the ciphers and protocols on each - * socket that is created - * @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. An empty settings will return - * a factory created from the default configuration + * Create a new {@link SSLSocketFactory} based on the provided configuration. + * The socket factory will also properly configure the ciphers and protocols on each socket that is created + * @param configuration The SSL configuration to use. Typically obtained from {@link #getSSLConfiguration(String)} * @return Never {@code null}. */ - public SSLSocketFactory sslSocketFactory(Settings settings) { - SSLConfiguration sslConfiguration = sslConfiguration(settings); - SSLSocketFactory socketFactory = sslContext(sslConfiguration).getSocketFactory(); - return new SecuritySSLSocketFactory(socketFactory, sslConfiguration.supportedProtocols().toArray(Strings.EMPTY_ARRAY), - supportedCiphers(socketFactory.getSupportedCipherSuites(), sslConfiguration.cipherSuites(), false)); - } - - /** - * Creates an {@link SSLEngine} based on the provided settings. The settings are used to identify the ssl configuration that should be - * used to create the engine. This SSLEngine cannot be used for hostname verification since the engine will not be created with the - * host and port. This method is useful to obtain an SSLEngine that will be used for server connections or client connections that - * will not use hostname verification. - * @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. An empty settings will return - * a SSLEngine created from the default configuration - * @param fallbackSettings the settings that should be used for the fallback of the SSLConfiguration. Using {@link Settings#EMPTY} - * results in a fallback to the global configuration - * @return {@link SSLEngine} - */ - public SSLEngine createSSLEngine(Settings settings, Settings fallbackSettings) { - return createSSLEngine(settings, fallbackSettings, null, -1); - } - - /** - * Creates an {@link SSLEngine} based on the provided settings. The settings are used to identify the ssl configuration that should be - * used to create the engine. This SSLEngine can be used for a connection that requires hostname verification assuming the provided - * host and port are correct. The SSLEngine created by this method is most useful for clients with hostname verification enabled - * @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. An empty settings will return - * a SSLEngine created from the default configuration - * @param fallbackSettings the settings that should be used for the fallback of the SSLConfiguration. Using {@link Settings#EMPTY} - * results in a fallback to the global configuration - * @param host the host of the remote endpoint. If using hostname verification, this should match what is in the remote endpoint's - * certificate - * @param port the port of the remote endpoint - * @return {@link SSLEngine} - */ - public SSLEngine createSSLEngine(Settings settings, Settings fallbackSettings, String host, int port) { - SSLConfiguration configuration = sslConfiguration(settings, fallbackSettings); - return createSSLEngine(configuration, host, port); + public SSLSocketFactory sslSocketFactory(SSLConfiguration configuration) { + SSLSocketFactory socketFactory = sslContext(configuration).getSocketFactory(); + return new SecuritySSLSocketFactory(socketFactory, configuration.supportedProtocols().toArray(Strings.EMPTY_ARRAY), + supportedCiphers(socketFactory.getSupportedCipherSuites(), configuration.cipherSuites(), false)); } /** @@ -219,7 +210,7 @@ public SSLEngine createSSLEngine(Settings settings, Settings fallbackSettings, S * certificate * @param port the port of the remote endpoint * @return {@link SSLEngine} - * @see #sslConfiguration(Settings, Settings) + * @see #getSSLConfiguration(String) */ public SSLEngine createSSLEngine(SSLConfiguration configuration, String host, int port) { SSLContext sslContext = sslContext(configuration); @@ -249,47 +240,18 @@ public SSLEngine createSSLEngine(SSLConfiguration configuration, String host, in * @param sslConfiguration the configuration to check */ public boolean isConfigurationValidForServerUsage(SSLConfiguration sslConfiguration) { + Objects.requireNonNull(sslConfiguration, "SSLConfiguration cannot be null"); return sslConfiguration.keyConfig() != KeyConfig.NONE; } - /** - * Indicates whether client authentication is enabled for a particular configuration - * @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix. The global configuration - * will be used for fallback - */ - public boolean isSSLClientAuthEnabled(Settings settings) { - return isSSLClientAuthEnabled(settings, Settings.EMPTY); - } - - /** - * Indicates whether client authentication is enabled for a particular configuration - * @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix - * @param fallback the settings that should be used for the fallback of the SSLConfiguration. Using {@link Settings#EMPTY} - * results in a fallback to the global configuration - */ - public boolean isSSLClientAuthEnabled(Settings settings, Settings fallback) { - SSLConfiguration sslConfiguration = sslConfiguration(settings, fallback); - return isSSLClientAuthEnabled(sslConfiguration); - } - /** * Indicates whether client authentication is enabled for a particular configuration */ public boolean isSSLClientAuthEnabled(SSLConfiguration sslConfiguration) { + Objects.requireNonNull(sslConfiguration, "SSLConfiguration cannot be null"); return sslConfiguration.sslClientAuth().enabled(); } - /** - * Returns the {@link VerificationMode} that is specified in the settings (or the default) - * @param settings the settings used to identify the ssl configuration, typically under a *.ssl. prefix - * @param fallback the settings that should be used for the fallback of the SSLConfiguration. Using {@link Settings#EMPTY} - * results in a fallback to the global configuration - */ - public VerificationMode getVerificationMode(Settings settings, Settings fallback) { - SSLConfiguration sslConfiguration = sslConfiguration(settings, fallback); - return sslConfiguration.verificationMode(); - } - /** * Returns the {@link SSLContext} for the global configuration. Mainly used for testing */ @@ -309,6 +271,7 @@ SSLContext sslContext(SSLConfiguration configuration) { * @throws IllegalArgumentException if not found */ SSLContextHolder sslContextHolder(SSLConfiguration sslConfiguration) { + Objects.requireNonNull(sslConfiguration, "SSL Configuration cannot be null"); SSLContextHolder holder = sslContexts.get(sslConfiguration); if (holder == null) { throw new IllegalArgumentException("did not find a SSLContext for [" + sslConfiguration.toString() + "]"); @@ -328,20 +291,11 @@ SSLConfiguration sslConfiguration(Settings settings) { return new SSLConfiguration(settings, globalSSLConfiguration); } - /** - * Returns the existing {@link SSLConfiguration} for the given settings and applies the provided fallback settings instead of the global - * configuration - * @param settings the settings for the ssl configuration - * @param fallbackSettings the settings that should be used for the fallback of the SSLConfiguration. Using {@link Settings#EMPTY} - * results in a fallback to the global configuration - * @return the ssl configuration for the provided settings. If the settings are empty, the global configuration is returned - */ - public SSLConfiguration sslConfiguration(Settings settings, Settings fallbackSettings) { - if (settings.isEmpty() && fallbackSettings.isEmpty()) { - return globalSSLConfiguration; - } - SSLConfiguration fallback = sslConfiguration(fallbackSettings); - return new SSLConfiguration(settings, fallback); + public Set getTransportProfileContextNames() { + return Collections.unmodifiableSet(this.sslConfigurations + .keySet().stream() + .filter(k -> k.startsWith("transport.profiles.")) + .collect(Collectors.toSet())); } /** @@ -430,27 +384,46 @@ private SSLContextHolder createSslContext(X509ExtendedKeyManager keyManager, X50 * Parses the settings to load all SSLConfiguration objects that will be used. */ Map loadSSLConfigurations() { - Map sslConfigurations = new HashMap<>(); - sslConfigurations.put(globalSSLConfiguration, createSslContext(globalSSLConfiguration)); + Map sslContextHolders = new HashMap<>(); + sslContextHolders.put(globalSSLConfiguration, createSslContext(globalSSLConfiguration)); + this.sslConfigurations.put("xpack.ssl", globalSSLConfiguration); + + Map sslSettingsMap = new HashMap<>(); + sslSettingsMap.put(XPackSettings.HTTP_SSL_PREFIX, getHttpTransportSSLSettings(settings)); + sslSettingsMap.put("xpack.http.ssl", settings.getByPrefix("xpack.http.ssl.")); + sslSettingsMap.putAll(getRealmsSSLSettings(settings)); + sslSettingsMap.putAll(getMonitoringExporterSettings(settings)); + + sslSettingsMap.forEach((key, sslSettings) -> { + if (sslSettings.isEmpty()) { + storeSslConfiguration(key, globalSSLConfiguration); + } else { + final SSLConfiguration configuration = new SSLConfiguration(sslSettings, globalSSLConfiguration); + storeSslConfiguration(key, configuration); + sslContextHolders.computeIfAbsent(configuration, this::createSslContext); + } + }); final Settings transportSSLSettings = settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX); - List sslSettingsList = new ArrayList<>(); - sslSettingsList.add(getHttpTransportSSLSettings(settings)); - sslSettingsList.add(settings.getByPrefix("xpack.http.ssl.")); - sslSettingsList.addAll(getRealmsSSLSettings(settings)); - sslSettingsList.addAll(getMonitoringExporterSettings(settings)); - - sslSettingsList.forEach((sslSettings) -> - sslConfigurations.computeIfAbsent(new SSLConfiguration(sslSettings, globalSSLConfiguration), this::createSslContext)); - - // transport is special because we want to use a auto-generated key when there isn't one final SSLConfiguration transportSSLConfiguration = new SSLConfiguration(transportSSLSettings, globalSSLConfiguration); this.transportSSLConfiguration.set(transportSSLConfiguration); - List profileSettings = getTransportProfileSSLSettings(settings); - sslConfigurations.computeIfAbsent(transportSSLConfiguration, this::createSslContext); - profileSettings.forEach((profileSetting) -> - sslConfigurations.computeIfAbsent(new SSLConfiguration(profileSetting, transportSSLConfiguration), this::createSslContext)); - return Collections.unmodifiableMap(sslConfigurations); + storeSslConfiguration(XPackSettings.TRANSPORT_SSL_PREFIX, transportSSLConfiguration); + Map profileSettings = getTransportProfileSSLSettings(settings); + sslContextHolders.computeIfAbsent(transportSSLConfiguration, this::createSslContext); + profileSettings.forEach((key, profileSetting) -> { + final SSLConfiguration configuration = new SSLConfiguration(profileSetting, transportSSLConfiguration); + storeSslConfiguration(key, configuration); + sslContextHolders.computeIfAbsent(configuration, this::createSslContext); + }); + + return Collections.unmodifiableMap(sslContextHolders); + } + + private void storeSslConfiguration(String key, SSLConfiguration configuration) { + if (key.endsWith(".")) { + key = key.substring(0, key.length() - 1); + } + sslConfigurations.put(key, configuration); } @@ -619,31 +592,32 @@ X509ExtendedTrustManager getEmptyTrustManager() throws GeneralSecurityException, } } - private static List getRealmsSSLSettings(Settings settings) { - List sslSettings = new ArrayList<>(); - Settings realmsSettings = settings.getByPrefix(SecurityField.setting("authc.realms.")); + /** + * @return A map of Settings prefix to Settings object + */ + private static Map getRealmsSSLSettings(Settings settings) { + Map sslSettings = new HashMap<>(); + final String prefix = SecurityField.setting("authc.realms."); + Settings realmsSettings = settings.getByPrefix(prefix); for (String name : realmsSettings.names()) { Settings realmSSLSettings = realmsSettings.getAsSettings(name).getByPrefix("ssl."); - if (realmSSLSettings.isEmpty() == false) { - sslSettings.add(realmSSLSettings); - } + // Put this even if empty, so that the name will be mapped to the global SSL configuration + sslSettings.put(prefix + name + ".ssl", realmSSLSettings); } return sslSettings; } - private static List getTransportProfileSSLSettings(Settings settings) { - List sslSettings = new ArrayList<>(); + private static Map getTransportProfileSSLSettings(Settings settings) { + Map sslSettings = new HashMap<>(); Map profiles = settings.getGroups("transport.profiles.", true); for (Entry entry : profiles.entrySet()) { Settings profileSettings = entry.getValue().getByPrefix("xpack.security.ssl."); - if (profileSettings.isEmpty() == false) { - sslSettings.add(profileSettings); - } + sslSettings.put("transport.profiles." + entry.getKey() + ".xpack.security.ssl", profileSettings); } return sslSettings; } - public static Settings getHttpTransportSSLSettings(Settings settings) { + private Settings getHttpTransportSSLSettings(Settings settings) { Settings httpSSLSettings = settings.getByPrefix(XPackSettings.HTTP_SSL_PREFIX); if (httpSSLSettings.isEmpty()) { return httpSSLSettings; @@ -656,18 +630,33 @@ public static Settings getHttpTransportSSLSettings(Settings settings) { return builder.build(); } - private static List getMonitoringExporterSettings(Settings settings) { - List sslSettings = new ArrayList<>(); + public SSLConfiguration getHttpTransportSSLConfiguration() { + return getSSLConfiguration(XPackSettings.HTTP_SSL_PREFIX); + } + + private static Map getMonitoringExporterSettings(Settings settings) { + Map sslSettings = new HashMap<>(); Map exportersSettings = settings.getGroups("xpack.monitoring.exporters."); for (Entry entry : exportersSettings.entrySet()) { Settings exporterSSLSettings = entry.getValue().getByPrefix("ssl."); - if (exporterSSLSettings.isEmpty() == false) { - sslSettings.add(exporterSSLSettings); - } + // Put this even if empty, so that the name will be mapped to the global SSL configuration + sslSettings.put("xpack.monitoring.exporters." + entry.getKey() + ".ssl", exporterSSLSettings); } return sslSettings; } + public SSLConfiguration getSSLConfiguration(String contextName) { + if (contextName.endsWith(".")) { + contextName = contextName.substring(0, contextName.length() - 1); + } + final SSLConfiguration configuration = sslConfigurations.get(contextName); + if (configuration == null) { + logger.warn("Cannot find SSL configuration for context {}. Known contexts are: {}", contextName, + Strings.collectionToCommaDelimitedString(sslConfigurations.keySet())); + } + return configuration; + } + /** * Maps the supported protocols to an appropriate ssl context algorithm. We make an attempt to use the "best" algorithm when * possible. The names in this method are taken from the diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4TransportTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4TransportTests.java new file mode 100644 index 0000000000000..3c4ebee2ac59c --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4TransportTests.java @@ -0,0 +1,43 @@ +/* + * 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.core.security.transport.netty4; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.TestEnvironment; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; +import org.elasticsearch.xpack.core.ssl.SSLService; +import org.elasticsearch.xpack.core.ssl.VerificationMode; +import org.hamcrest.Matchers; + +import java.util.Map; + +import static org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport.getTransportProfileConfigurations; + +public class SecurityNetty4TransportTests extends ESTestCase { + + public void testGetTransportProfileConfigurations() { + final Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .put("xpack.security.transport.ssl.verification_mode", VerificationMode.CERTIFICATE.name()) + .put("transport.profiles.full.xpack.security.ssl.verification_mode", VerificationMode.FULL.name()) + .put("transport.profiles.cert.xpack.security.ssl.verification_mode", VerificationMode.CERTIFICATE.name()) + .put("transport.profiles.none.xpack.security.ssl.verification_mode", VerificationMode.NONE.name()) + .build(); + final Environment env = TestEnvironment.newEnvironment(settings); + SSLService sslService = new SSLService(settings, env); + final SSLConfiguration defaultConfig = sslService.getSSLConfiguration("xpack.security.transport.ssl"); + final Map profileConfigurations = getTransportProfileConfigurations(settings, sslService, defaultConfig); + assertThat(profileConfigurations.size(), Matchers.equalTo(4)); + assertThat(profileConfigurations.keySet(), Matchers.containsInAnyOrder("full", "cert", "none", "default")); + assertThat(profileConfigurations.get("full").verificationMode(), Matchers.equalTo(VerificationMode.FULL)); + assertThat(profileConfigurations.get("cert").verificationMode(), Matchers.equalTo(VerificationMode.CERTIFICATE)); + assertThat(profileConfigurations.get("none").verificationMode(), Matchers.equalTo(VerificationMode.NONE)); + assertThat(profileConfigurations.get("default"), Matchers.sameInstance(defaultConfig)); + } +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java index 0946ad3ac51c0..72cd13471df1f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java @@ -303,7 +303,7 @@ public void testReloadingKeyStoreException() throws Exception { .build(); Environment env = randomBoolean() ? null : TestEnvironment.newEnvironment(settings); final SSLService sslService = new SSLService(settings, env); - final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY); + final SSLConfiguration config = sslService.getSSLConfiguration("xpack.ssl"); new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) { @Override void reloadSSLContext(SSLConfiguration configuration) { @@ -344,7 +344,7 @@ public void testReloadingPEMKeyConfigException() throws Exception { .build(); Environment env = randomBoolean() ? null : TestEnvironment.newEnvironment(settings); final SSLService sslService = new SSLService(settings, env); - final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY); + final SSLConfiguration config = sslService.getSSLConfiguration("xpack.ssl"); new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) { @Override void reloadSSLContext(SSLConfiguration configuration) { @@ -379,7 +379,7 @@ public void testTrustStoreReloadException() throws Exception { .build(); Environment env = randomBoolean() ? null : TestEnvironment.newEnvironment(settings); final SSLService sslService = new SSLService(settings, env); - final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY); + final SSLConfiguration config = sslService.getSSLConfiguration("xpack.ssl"); new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) { @Override void reloadSSLContext(SSLConfiguration configuration) { @@ -411,7 +411,7 @@ public void testPEMTrustReloadException() throws Exception { .build(); Environment env = randomBoolean() ? null : TestEnvironment.newEnvironment(settings); final SSLService sslService = new SSLService(settings, env); - final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY); + final SSLConfiguration config = sslService.getSSLConfiguration("xpack.ssl"); new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) { @Override void reloadSSLContext(SSLConfiguration configuration) { @@ -440,7 +440,7 @@ private void validateSSLConfigurationIsReloaded(Settings settings, Environment e final CountDownLatch reloadLatch = new CountDownLatch(1); final SSLService sslService = new SSLService(settings, env); - final SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY); + final SSLConfiguration config = sslService.getSSLConfiguration("xpack.ssl"); new SSLConfigurationReloader(settings, env, sslService, resourceWatcherService) { @Override void reloadSSLContext(SSLConfiguration configuration) { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java index b583dbb33f9ea..df764bb3f4772 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java @@ -38,7 +38,6 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509ExtendedTrustManager; - import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedActionException; @@ -111,11 +110,18 @@ public void testThatCustomTruststoreCanBeSpecified() throws Exception { .setSecureSettings(secureCustomSettings) .build(); - SSLEngine sslEngineWithTruststore = sslService.createSSLEngine(customTruststoreSettings, Settings.EMPTY); + SSLConfiguration configuration = new SSLConfiguration(customTruststoreSettings, globalConfiguration(sslService)); + SSLEngine sslEngineWithTruststore = sslService.createSSLEngine(configuration, null, -1); assertThat(sslEngineWithTruststore, is(not(nullValue()))); - SSLEngine sslEngine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); + SSLConfiguration globalConfig = globalConfiguration(sslService); + SSLEngine sslEngine = sslService.createSSLEngine(globalConfig, null, -1); assertThat(sslEngineWithTruststore, is(not(sameInstance(sslEngine)))); + + final SSLConfiguration profileConfiguration = sslService.getSSLConfiguration("transport.profiles.foo.xpack.security.ssl"); + assertThat(profileConfiguration, notNullValue()); + assertThat(profileConfiguration.trustConfig(), instanceOf(StoreTrustConfig.class)); + assertThat(((StoreTrustConfig) profileConfiguration.trustConfig()).trustStorePath, equalTo(testClientStore.toString())); } public void testThatSslContextCachingWorks() throws Exception { @@ -132,6 +138,10 @@ public void testThatSslContextCachingWorks() throws Exception { SSLContext cachedSslContext = sslService.sslContext(); assertThat(sslContext, is(sameInstance(cachedSslContext))); + + final SSLConfiguration configuration = sslService.getSSLConfiguration("xpack.ssl"); + final SSLContext configContext = sslService.sslContext(configuration); + assertThat(configContext, is(sameInstance(sslContext))); } public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception { @@ -144,7 +154,9 @@ public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception { .put("xpack.ssl.keystore.path", differentPasswordsStore) .setSecureSettings(secureSettings) .build(); - new SSLService(settings, env).createSSLEngine(Settings.EMPTY, Settings.EMPTY); + final SSLService sslService = new SSLService(settings, env); + SSLConfiguration configuration = globalConfiguration(sslService); + sslService.createSSLEngine(configuration, null, -1); } public void testIncorrectKeyPasswordThrowsException() throws Exception { @@ -157,7 +169,9 @@ public void testIncorrectKeyPasswordThrowsException() throws Exception { .put("xpack.ssl.keystore.path", differentPasswordsStore) .setSecureSettings(secureSettings) .build(); - new SSLService(settings, env).createSSLEngine(Settings.EMPTY, Settings.EMPTY); + final SSLService sslService = new SSLService(settings, env); + SSLConfiguration configuration = globalConfiguration(sslService); + sslService.createSSLEngine(configuration, null, -1); fail("expected an exception"); } catch (ElasticsearchException e) { assertThat(e.getMessage(), containsString("failed to initialize a KeyManagerFactory")); @@ -173,13 +187,15 @@ public void testThatSSLv3IsNotEnabled() throws Exception { .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); - SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); + SSLConfiguration configuration = globalConfiguration(sslService); + SSLEngine engine = sslService.createSSLEngine(configuration, null, -1); assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3"))); } public void testThatCreateClientSSLEngineWithoutAnySettingsWorks() throws Exception { SSLService sslService = new SSLService(Settings.EMPTY, env); - SSLEngine sslEngine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); + SSLConfiguration configuration = globalConfiguration(sslService); + SSLEngine sslEngine = sslService.createSSLEngine(configuration, null, -1); assertThat(sslEngine, notNullValue()); } @@ -191,7 +207,8 @@ public void testThatCreateSSLEngineWithOnlyTruststoreWorks() throws Exception { .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); - SSLEngine sslEngine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); + SSLConfiguration configuration = globalConfiguration(sslService); + SSLEngine sslEngine = sslService.createSSLEngine(configuration, null, -1); assertThat(sslEngine, notNullValue()); } @@ -206,7 +223,7 @@ public void testCreateWithKeystoreIsValidForServer() throws Exception { .build(); SSLService sslService = new SSLService(settings, env); - assertTrue(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration(Settings.EMPTY))); + assertTrue(sslService.isConfigurationValidForServerUsage(globalConfiguration(sslService))); } public void testValidForServerWithFallback() throws Exception { @@ -218,7 +235,7 @@ public void testValidForServerWithFallback() throws Exception { .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); - assertFalse(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration(Settings.EMPTY))); + assertFalse(sslService.isConfigurationValidForServerUsage(globalConfiguration(sslService))); secureSettings.setString("xpack.security.transport.ssl.keystore.secure_password", "testnode"); settings = Settings.builder() @@ -229,15 +246,13 @@ public void testValidForServerWithFallback() throws Exception { .put("xpack.security.transport.ssl.keystore.type", testnodeStoreType) .build(); sslService = new SSLService(settings, env); - assertFalse(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration(Settings.EMPTY))); - assertTrue(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration( - settings.getByPrefix("xpack.security.transport.ssl.")))); - assertFalse(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration(Settings.EMPTY))); + assertFalse(sslService.isConfigurationValidForServerUsage(globalConfiguration(sslService))); + assertTrue(sslService.isConfigurationValidForServerUsage(sslService.getSSLConfiguration("xpack.security.transport.ssl"))); } public void testGetVerificationMode() throws Exception { SSLService sslService = new SSLService(Settings.EMPTY, env); - assertThat(sslService.getVerificationMode(Settings.EMPTY, Settings.EMPTY), is(XPackSettings.VERIFICATION_MODE_DEFAULT)); + assertThat(globalConfiguration(sslService).verificationMode(), is(XPackSettings.VERIFICATION_MODE_DEFAULT)); Settings settings = Settings.builder() .put("xpack.ssl.verification_mode", "none") @@ -245,31 +260,25 @@ public void testGetVerificationMode() throws Exception { .put("transport.profiles.foo.xpack.security.ssl.verification_mode", "full") .build(); sslService = new SSLService(settings, env); - assertThat(sslService.getVerificationMode(Settings.EMPTY, Settings.EMPTY), is(VerificationMode.NONE)); - assertThat(sslService.getVerificationMode(settings.getByPrefix("xpack.security.transport.ssl."), Settings.EMPTY), - is(VerificationMode.CERTIFICATE)); - assertThat(sslService.getVerificationMode(settings.getByPrefix("transport.profiles.foo.xpack.security.ssl."), - settings.getByPrefix("xpack.security.transport.ssl.")), is(VerificationMode.FULL)); - assertThat(sslService.getVerificationMode(Settings.EMPTY, settings.getByPrefix("xpack.security.transport.ssl.")), - is(VerificationMode.CERTIFICATE)); + assertThat(globalConfiguration(sslService).verificationMode(), is(VerificationMode.NONE)); + assertThat(sslService.getSSLConfiguration("xpack.security.transport.ssl.").verificationMode(), is(VerificationMode.CERTIFICATE)); + assertThat(sslService.getSSLConfiguration("transport.profiles.foo.xpack.security.ssl.").verificationMode(), + is(VerificationMode.FULL)); } public void testIsSSLClientAuthEnabled() throws Exception { SSLService sslService = new SSLService(Settings.EMPTY, env); - assertTrue(sslService.isSSLClientAuthEnabled(Settings.EMPTY)); - assertTrue(sslService.isSSLClientAuthEnabled(Settings.EMPTY, Settings.EMPTY)); + assertTrue(globalConfiguration(sslService).sslClientAuth().enabled()); Settings settings = Settings.builder() .put("xpack.ssl.client_authentication", "none") .put("xpack.security.transport.ssl.client_authentication", "optional") + .put("transport.profiles.foo.port", "9400-9410") .build(); sslService = new SSLService(settings, env); - assertFalse(sslService.isSSLClientAuthEnabled(Settings.EMPTY)); - assertFalse(sslService.isSSLClientAuthEnabled(Settings.EMPTY, Settings.EMPTY)); - assertTrue(sslService.isSSLClientAuthEnabled(settings.getByPrefix("xpack.security.transport.ssl."))); - assertTrue(sslService.isSSLClientAuthEnabled(settings.getByPrefix("xpack.security.transport.ssl."), Settings.EMPTY)); - assertTrue(sslService.isSSLClientAuthEnabled(settings.getByPrefix("transport.profiles.foo.xpack.security.ssl."), - settings.getByPrefix("xpack.security.transport.ssl."))); + assertFalse(sslService.isSSLClientAuthEnabled(globalConfiguration(sslService))); + assertTrue(sslService.isSSLClientAuthEnabled(sslService.getSSLConfiguration("xpack.security.transport.ssl"))); + assertTrue(sslService.isSSLClientAuthEnabled(sslService.getSSLConfiguration("transport.profiles.foo.xpack.security.ssl"))); } public void testThatHttpClientAuthDefaultsToNone() throws Exception { @@ -279,11 +288,10 @@ public void testThatHttpClientAuthDefaultsToNone() throws Exception { .build(); final SSLService sslService = new SSLService(globalSettings, env); - final SSLConfiguration globalConfig = sslService.sslConfiguration(Settings.EMPTY); + final SSLConfiguration globalConfig = globalConfiguration(sslService); assertThat(globalConfig.sslClientAuth(), is(SSLClientAuth.OPTIONAL)); - final Settings httpSettings = SSLService.getHttpTransportSSLSettings(globalSettings); - final SSLConfiguration httpConfig = sslService.sslConfiguration(httpSettings); + final SSLConfiguration httpConfig = sslService.getHttpTransportSSLConfiguration(); assertThat(httpConfig.sslClientAuth(), is(SSLClientAuth.NONE)); } @@ -325,7 +333,8 @@ public void testCiphersAndInvalidCiphersWork() throws Exception { .putList("xpack.ssl.ciphers", ciphers.toArray(new String[ciphers.size()])) .build(); SSLService sslService = new SSLService(settings, env); - SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); + SSLConfiguration configuration = globalConfiguration(sslService); + SSLEngine engine = sslService.createSSLEngine(configuration, null, -1); assertThat(engine, is(notNullValue())); String[] enabledCiphers = engine.getEnabledCipherSuites(); assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar"))); @@ -355,7 +364,8 @@ public void testThatSSLEngineHasCipherSuitesOrderSet() throws Exception { .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); - SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); + SSLConfiguration configuration = globalConfiguration(sslService); + SSLEngine engine = sslService.createSSLEngine(configuration, null, -1); assertThat(engine, is(notNullValue())); assertTrue(engine.getSSLParameters().getUseCipherSuitesOrder()); } @@ -369,8 +379,8 @@ public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Except .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); - SSLSocketFactory factory = sslService.sslSocketFactory(Settings.EMPTY); - SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY); + SSLConfiguration config = globalConfiguration(sslService); + final SSLSocketFactory factory = sslService.sslSocketFactory(config); final String[] ciphers = sslService.supportedCiphers(factory.getSupportedCipherSuites(), config.cipherSuites(), false); assertThat(factory.getDefaultCipherSuites(), is(ciphers)); @@ -394,10 +404,10 @@ public void testThatSSLEngineHasProperCiphersAndProtocols() throws Exception { .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); - SSLEngine engine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); - SSLConfiguration config = sslService.sslConfiguration(Settings.EMPTY); - final String[] ciphers = sslService.supportedCiphers(engine.getSupportedCipherSuites(), config.cipherSuites(), false); - final String[] supportedProtocols = config.supportedProtocols().toArray(Strings.EMPTY_ARRAY); + SSLConfiguration configuration = globalConfiguration(sslService); + SSLEngine engine = sslService.createSSLEngine(configuration, null, -1); + final String[] ciphers = sslService.supportedCiphers(engine.getSupportedCipherSuites(), configuration.cipherSuites(), false); + final String[] supportedProtocols = configuration.supportedProtocols().toArray(Strings.EMPTY_ARRAY); assertThat(engine.getEnabledCipherSuites(), is(ciphers)); assertArrayEquals(ciphers, engine.getSSLParameters().getCipherSuites()); // the order we set the protocols in is not going to be what is returned as internally the JDK may sort the versions @@ -433,6 +443,7 @@ public void testSSLStrategy() { // ensure it actually goes through and calls the real method when(sslService.sslIOSessionStrategy(settings)).thenCallRealMethod(); + when(sslService.sslIOSessionStrategy(sslConfig)).thenCallRealMethod(); assertThat(sslService.sslIOSessionStrategy(settings), sameInstance(sslStrategy)); @@ -451,7 +462,70 @@ public void testEmptyTrustManager() throws Exception { assertThat(trustManager.getAcceptedIssuers(), emptyArray()); } - public void testReadCertificateInformation() throws Exception { + + public void testGetConfigurationByContextName() throws Exception { + final SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(null, null, null); + final String[] cipherSuites = sslContext.getSupportedSSLParameters().getCipherSuites(); + + final String[] contextNames = { + "xpack.ssl", + "xpack.http.ssl", + "xpack.security.http.ssl", + "xpack.security.transport.ssl", + "transport.profiles.prof1.xpack.security.ssl", + "transport.profiles.prof2.xpack.security.ssl", + "transport.profiles.prof3.xpack.security.ssl", + "xpack.security.authc.realms.realm1.ssl", + "xpack.security.authc.realms.realm2.ssl", + "xpack.monitoring.exporters.mon1.ssl", + "xpack.monitoring.exporters.mon2.ssl" + }; + + assumeTrue("Not enough cipher suites are available to support this test", cipherSuites.length >= contextNames.length); + + // Here we use a different ciphers for each context, so we can check that the returned SSLConfiguration matches the + // provided settings + final Iterator cipher = Arrays.asList(cipherSuites).iterator(); + + final MockSecureSettings secureSettings = new MockSecureSettings(); + final Settings.Builder builder = Settings.builder(); + for (String prefix : contextNames) { + secureSettings.setString(prefix + ".keystore.secure_password", "testnode"); + builder.put(prefix + ".keystore.path", testnodeStore) + .putList(prefix + ".cipher_suites", cipher.next()); + } + + final Settings settings = builder + // Add a realm without SSL settings. This context name should be mapped to the global configuration + .put("xpack.security.authc.realms.realm3.type", "file") + // Add an exporter without SSL settings. This context name should be mapped to the global configuration + .put("xpack.monitoring.exporters.mon3.type", "http") + .setSecureSettings(secureSettings) + .build(); + SSLService sslService = new SSLService(settings, env); + + for (int i = 0; i < contextNames.length; i++) { + final String name = contextNames[i]; + final SSLConfiguration configuration = sslService.getSSLConfiguration(name); + assertThat("Configuration for " + name, configuration, notNullValue()); + assertThat("KeyStore for " + name, configuration.keyConfig(), instanceOf(StoreKeyConfig.class)); + final StoreKeyConfig keyConfig = (StoreKeyConfig) configuration.keyConfig(); + assertThat("KeyStore Path for " + name, keyConfig.keyStorePath, equalTo(testnodeStore.toString())); + assertThat("Cipher for " + name, configuration.cipherSuites(), contains(cipherSuites[i])); + assertThat("Configuration for " + name + ".", sslService.getSSLConfiguration(name + "."), sameInstance(configuration)); + } + + // These contexts have no SSL settings, but for convenience we want those components to be able to access their context + // by name, and get back the global configuration + final SSLConfiguration realm3Config = sslService.getSSLConfiguration("xpack.security.authc.realms.realm3.ssl"); + final SSLConfiguration mon3Config = sslService.getSSLConfiguration("xpack.monitoring.exporters.mon3.ssl."); + final SSLConfiguration global = globalConfiguration(sslService); + assertThat(realm3Config, sameInstance(global)); + assertThat(mon3Config, sameInstance(global)); + } + + public void testReadCertificateInformation () throws Exception { final Path jksPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"); final Path p12Path = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.p12"); final Path pemPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/active-directory-ca.crt"); @@ -601,7 +675,9 @@ public void testThatSSLContextTrustsJDKTrustedCAs() throws Exception { @Network public void testThatSSLIOSessionStrategyWithoutSettingsWorks() throws Exception { SSLService sslService = new SSLService(Settings.EMPTY, env); - SSLIOSessionStrategy sslStrategy = sslService.sslIOSessionStrategy(Settings.EMPTY); + SSLConfiguration sslConfiguration = globalConfiguration(sslService); + logger.info("SSL Configuration: {}", sslConfiguration); + SSLIOSessionStrategy sslStrategy = sslService.sslIOSessionStrategy(sslConfiguration); try (CloseableHttpAsyncClient client = getAsyncHttpClient(sslStrategy)) { client.start(); @@ -613,14 +689,15 @@ public void testThatSSLIOSessionStrategyWithoutSettingsWorks() throws Exception } @Network - public void testThatSSLIOSessionStrategytTrustsJDKTrustedCAs() throws Exception { + public void testThatSSLIOSessionStrategyTrustsJDKTrustedCAs() throws Exception { MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("xpack.ssl.keystore.secure_password", "testclient"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testclientStore) .setSecureSettings(secureSettings) .build(); - SSLIOSessionStrategy sslStrategy = new SSLService(settings, env).sslIOSessionStrategy(Settings.EMPTY); + final SSLService sslService = new SSLService(settings, env); + SSLIOSessionStrategy sslStrategy = sslService.sslIOSessionStrategy(globalConfiguration(sslService)); try (CloseableHttpAsyncClient client = getAsyncHttpClient(sslStrategy)) { client.start(); @@ -630,6 +707,10 @@ public void testThatSSLIOSessionStrategytTrustsJDKTrustedCAs() throws Exception } } + private static SSLConfiguration globalConfiguration(SSLService sslService) { + return sslService.getSSLConfiguration("xpack.ssl"); + } + class AssertionCallback implements FutureCallback { @Override diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java index 9333a2f81dbeb..40c95384d5349 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java @@ -55,6 +55,7 @@ public Exporters(Settings settings, Map factories, this.licenseState = Objects.requireNonNull(licenseState); clusterService.getClusterSettings().addSettingsUpdateConsumer(this::setExportersSetting, getSettings()); + HttpExporter.registerSettingValidators(clusterService); // this ensures, that logging is happening by adding an empty consumer per affix setting for (Setting.AffixSetting affixSetting : getSettings()) { clusterService.getClusterSettings().addAffixUpdateConsumer(affixSetting, (s, o) -> {}, (s, o) -> {}); diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporter.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporter.java index ee583dd377085..54447c1646370 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporter.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporter.java @@ -34,6 +34,8 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.xpack.core.monitoring.exporter.MonitoringTemplateUtils; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; +import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.monitoring.exporter.ClusterAlertsUtil; import org.elasticsearch.xpack.monitoring.exporter.Exporter; @@ -50,6 +52,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * {@code HttpExporter} uses the low-level {@link RestClient} to connect to a user-specified set of nodes for exporting Monitoring @@ -252,6 +255,30 @@ public HttpExporter(final Config config, final SSLService sslService, final Thre listener.setResource(resource); } + /** + * Adds a validator for the {@link #SSL_SETTING} to prevent dynamic updates when secure settings also exist within that setting + * groups (ssl context). + * Because it is not possible to re-read the secure settings during a dynamic update, we cannot rebuild the {@link SSLIOSessionStrategy} + * (see {@link #configureSecurity(RestClientBuilder, Config, SSLService)} if this exporter has been configured with secure settings + */ + public static void registerSettingValidators(ClusterService clusterService) { + clusterService.getClusterSettings().addAffixUpdateConsumer(SSL_SETTING, + (ignoreKey, ignoreSettings) -> { + // no-op update. We only care about the validator + }, + (namespace, settings) -> { + final List secureSettings = SSLConfigurationSettings.withoutPrefix() + .getSecureSettingsInUse(settings) + .stream() + .map(Setting::getKey) + .collect(Collectors.toList()); + if (secureSettings.isEmpty() == false) { + throw new IllegalStateException("Cannot dynamically update SSL settings for the exporter [" + namespace + + "] as it depends on the secure setting(s) [" + Strings.collectionToCommaDelimitedString(secureSettings) + "]"); + } + }); + } + /** * Create a {@link RestClientBuilder} from the HTTP Exporter's {@code config}. * @@ -443,8 +470,20 @@ private static void configureHeaders(final RestClientBuilder builder, final Conf * @throws SettingsException if any setting causes issues */ private static void configureSecurity(final RestClientBuilder builder, final Config config, final SSLService sslService) { - final Settings sslSettings = SSL_SETTING.getConcreteSettingForNamespace(config.name()).get(config.settings()); - final SSLIOSessionStrategy sslStrategy = sslService.sslIOSessionStrategy(sslSettings); + final Setting concreteSetting = SSL_SETTING.getConcreteSettingForNamespace(config.name()); + final Settings sslSettings = concreteSetting.get(config.settings()); + final SSLIOSessionStrategy sslStrategy; + if (SSLConfigurationSettings.withoutPrefix().getSecureSettingsInUse(sslSettings).isEmpty()) { + // This configuration does not use secure settings, so it is possible that is has been dynamically updated. + // We need to load a new SSL strategy in case these settings differ from the ones that the SSL service was configured with. + sslStrategy = sslService.sslIOSessionStrategy(sslSettings); + } else { + // This configuration uses secure settings. We cannot load a new SSL strategy, as the secure settings have already been closed. + // Due to #registerSettingValidators we know that the settings not been dynamically updated, and the pre-configured strategy + // is still the correct configuration for use in this exporter. + final SSLConfiguration sslConfiguration = sslService.getSSLConfiguration(concreteSetting.getKey()); + sslStrategy = sslService.sslIOSessionStrategy(sslConfiguration); + } final CredentialsProvider credentialsProvider = createCredentialsProvider(config); List hostList = HOST_SETTING.getConcreteSettingForNamespace(config.name()).get(config.settings()); // sending credentials in plaintext! diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterSslIT.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterSslIT.java new file mode 100644 index 0000000000000..a0511dc17aa92 --- /dev/null +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterSslIT.java @@ -0,0 +1,188 @@ +/* + * 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.monitoring.exporter.http; + +import org.elasticsearch.action.ActionFuture; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.elasticsearch.common.settings.MockSecureSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.TestEnvironment; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESIntegTestCase.Scope; +import org.elasticsearch.test.http.MockWebServer; +import org.elasticsearch.xpack.core.ssl.TestsSSLService; +import org.elasticsearch.xpack.core.ssl.VerificationMode; +import org.elasticsearch.xpack.monitoring.exporter.Exporter; +import org.elasticsearch.xpack.monitoring.exporter.Exporters; +import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; +import org.hamcrest.CoreMatchers; +import org.junit.AfterClass; +import org.junit.Before; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.notNullValue; + +@ESIntegTestCase.ClusterScope(scope = Scope.SUITE, + numDataNodes = 1, numClientNodes = 0, transportClientRatio = 0.0, supportsDedicatedMasters = false) +public class HttpExporterSslIT extends MonitoringIntegTestCase { + + private final Settings globalSettings = Settings.builder().put("path.home", createTempDir()).build(); + private final Environment environment = TestEnvironment.newEnvironment(globalSettings); + + private static MockWebServer webServer; + private MockSecureSettings secureSettings; + + + @AfterClass + public static void cleanUpStatics() { + if (webServer != null) { + webServer.close(); + webServer = null; + } + } + + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + final Path truststore = getDataPath("/org/elasticsearch/xpack/monitoring/exporter/http/testnode.jks"); + assertThat(Files.exists(truststore), CoreMatchers.is(true)); + + if (webServer == null) { + try { + webServer = buildWebServer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + final String address = "https://" + webServer.getHostName() + ":" + webServer.getPort(); + final Settings.Builder builder = Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put("xpack.monitoring.exporters.plaintext.type", "http") + .put("xpack.monitoring.exporters.plaintext.enabled", true) + .put("xpack.monitoring.exporters.plaintext.host", address) + .put("xpack.monitoring.exporters.plaintext.ssl.truststore.path", truststore) + .put("xpack.monitoring.exporters.plaintext.ssl.truststore.password", "testnode") + .put("xpack.monitoring.exporters.secure.type", "http") + .put("xpack.monitoring.exporters.secure.enabled", true) + .put("xpack.monitoring.exporters.secure.host", address) + .put("xpack.monitoring.exporters.secure.ssl.truststore.path", truststore); + + secureSettings = new MockSecureSettings(); + secureSettings.setString("xpack.monitoring.exporters.secure.ssl.truststore.secure_password", "testnode"); + builder.setSecureSettings(secureSettings); + + return builder.build(); + } + + private MockWebServer buildWebServer() throws IOException { + final Path cert = getDataPath("/org/elasticsearch/xpack/monitoring/exporter/http/testnode.crt"); + final Path key = getDataPath("/org/elasticsearch/xpack/monitoring/exporter/http/testnode.pem"); + + final Settings sslSettings = Settings.builder() + .put("xpack.ssl.certificate", cert) + .put("xpack.ssl.key", key) + .put("xpack.ssl.key_passphrase", "testnode") + .put(globalSettings) + .build(); + + TestsSSLService sslService = new TestsSSLService(sslSettings, environment); + final SSLContext sslContext = sslService.sslContext(Settings.EMPTY); + MockWebServer server = new MockWebServer(sslContext, false); + server.start(); + return server; + } + + @Before + // Force the exporters to be built from closed secure settings (as they would be in a production environment) + public void closeSecureSettings() throws IOException { + if (secureSettings != null) { + secureSettings.close(); + } + } + + public void testCannotUpdateSslSettingsWithSecureSettings() throws Exception { + // Verify that it was created even though it has a secure setting + assertExporterExists("secure"); + + // Verify that we cannot modify the SSL settings + final ActionFuture future = setVerificationMode("secure", VerificationMode.CERTIFICATE); + final IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, future::actionGet); + assertThat(iae.getCause(), instanceOf(IllegalStateException.class)); + assertThat(iae.getCause().getMessage(), containsString("secure_password")); + } + + public void testCanUpdateSslSettingsWithNoSecureSettings() { + final ActionFuture future = setVerificationMode("plaintext", VerificationMode.CERTIFICATE); + final ClusterUpdateSettingsResponse response = future.actionGet(); + assertThat(response, notNullValue()); + clearTransientSettings("plaintext"); + } + + public void testCanAddNewExporterWithSsl() { + Path truststore = getDataPath("/org/elasticsearch/xpack/monitoring/exporter/http/testnode.jks"); + assertThat(Files.exists(truststore), CoreMatchers.is(true)); + + final ClusterUpdateSettingsRequest updateSettings = new ClusterUpdateSettingsRequest(); + final Settings settings = Settings.builder() + .put("xpack.monitoring.exporters._new.type", "http") + .put("xpack.monitoring.exporters._new.host", "https://" + webServer.getHostName() + ":" + webServer.getPort()) + .put("xpack.monitoring.exporters._new.ssl.truststore.path", truststore) + .put("xpack.monitoring.exporters._new.ssl.truststore.password", "testnode") + .put("xpack.monitoring.exporters._new.ssl.verification_mode", VerificationMode.NONE.name()) + .build(); + updateSettings.transientSettings(settings); + final ActionFuture future = client().admin().cluster().updateSettings(updateSettings); + final ClusterUpdateSettingsResponse response = future.actionGet(); + assertThat(response, notNullValue()); + + assertExporterExists("_new"); + clearTransientSettings("_new"); + } + + private void assertExporterExists(String secure) { + final Exporter httpExporter = getExporter(secure); + assertThat(httpExporter, notNullValue()); + assertThat(httpExporter, instanceOf(HttpExporter.class)); + } + + private Exporter getExporter(String name) { + final Exporters exporters = internalCluster().getInstance(Exporters.class); + assertThat(exporters, notNullValue()); + return exporters.getExporter(name); + } + + private ActionFuture setVerificationMode(String name, VerificationMode mode) { + final ClusterUpdateSettingsRequest updateSettings = new ClusterUpdateSettingsRequest(); + final Settings settings = Settings.builder() + .put("xpack.monitoring.exporters." + name + ".ssl.verification_mode", mode.name()) + .build(); + updateSettings.transientSettings(settings); + return client().admin().cluster().updateSettings(updateSettings); + } + + private void clearTransientSettings(String... names) { + final ClusterUpdateSettingsRequest updateSettings = new ClusterUpdateSettingsRequest(); + final Settings.Builder builder = Settings.builder(); + for (String name : names) { + builder.put("xpack.monitoring.exporters." + name + ".*", (String) null); + } + updateSettings.transientSettings(builder.build()); + client().admin().cluster().updateSettings(updateSettings).actionGet(); + } +} diff --git a/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.crt b/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.crt new file mode 100644 index 0000000000000..08c160bcea5ff --- /dev/null +++ b/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID0zCCArugAwIBAgIJALi5bDfjMszLMA0GCSqGSIb3DQEBCwUAMEgxDDAKBgNV +BAoTA29yZzEWMBQGA1UECxMNZWxhc3RpY3NlYXJjaDEgMB4GA1UEAxMXRWxhc3Rp +Y3NlYXJjaCBUZXN0IE5vZGUwHhcNMTUwOTIzMTg1MjU3WhcNMTkwOTIyMTg1MjU3 +WjBIMQwwCgYDVQQKEwNvcmcxFjAUBgNVBAsTDWVsYXN0aWNzZWFyY2gxIDAeBgNV +BAMTF0VsYXN0aWNzZWFyY2ggVGVzdCBOb2RlMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3rGZ1QbsW0+MuyrSLmMfDFKtLBkIFW8V0gRuurFg1PUKKNR1 +Mq2tMVwjjYETAU/UY0iKZOzjgvYPKhDTYBTte/WHR1ZK4CYVv7TQX/gtFQG/ge/c +7u0sLch9p7fbd+/HZiLS/rBEZDIohvgUvzvnA8+OIYnw4kuxKo/5iboAIS41klMg +/lATm8V71LMY68inht71/ZkQoAHKgcR9z4yNYvQ1WqKG8DG8KROXltll3sTrKbl5 +zJhn660es/1ZnR6nvwt6xnSTl/mNHMjkfv1bs4rJ/py3qPxicdoSIn/KyojUcgHV +F38fuAy2CQTdjVG5fWj9iz+mQvLm3+qsIYQdFwIDAQABo4G/MIG8MAkGA1UdEwQC +MAAwHQYDVR0OBBYEFEMMWLWQi/g83PzlHYqAVnty5L7HMIGPBgNVHREEgYcwgYSC +CWxvY2FsaG9zdIIVbG9jYWxob3N0LmxvY2FsZG9tYWluggpsb2NhbGhvc3Q0ghds +b2NhbGhvc3Q0LmxvY2FsZG9tYWluNIIKbG9jYWxob3N0NoIXbG9jYWxob3N0Ni5s +b2NhbGRvbWFpbjaHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL +BQADggEBAMjGGXT8Nt1tbl2GkiKtmiuGE2Ej66YuZ37WSJViaRNDVHLlg87TCcHe +k2rdO+6sFqQbbzEfwQ05T7xGmVu7tm54HwKMRugoQ3wct0bQC5wEWYN+oMDvSyO6 +M28mZwWb4VtR2IRyWP+ve5DHwTM9mxWa6rBlGzsQqH6YkJpZojzqk/mQTug+Y8aE +mVoqRIPMHq9ob+S9qd5lp09+MtYpwPfTPx/NN+xMEooXWW/ARfpGhWPkg/FuCu4z +1tFmCqHgNcWirzMm3dQpF78muE9ng6OB2MXQwL4VgnVkxmlZNHbkR2v/t8MyZJxC +y4g6cTMM3S/UMt5/+aIB2JAuMKyuD+A= +-----END CERTIFICATE----- diff --git a/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.jks b/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.jks new file mode 100644 index 0000000000000..ebe6146124e8f Binary files /dev/null and b/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.jks differ diff --git a/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.pem b/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.pem new file mode 100644 index 0000000000000..5a67e1033440d --- /dev/null +++ b/x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,9D867F7E0C94D013 + +dVoVCjPeg1wgS7rVtOvGfQcrZyLkx393aWRnFq45tbjKBVuITtJ9vI7o4QXOV/15 +Gnb6WhXGIdWrzsxEAd46K6hIuNSISd4Emsx6c2Q5hTqWXXfexbOZBNfTtXtdJPnJ +1jAaikhtztLo3JSLTKNY5sNxd+XbaQyYVUWvueK6zOaIIMETvB+VPVFd9i1ROibk +Sgdtyj01KjkoalifqK/tA0CIYNKL0S6/eoK3UhAlpIprlpV+cnXa940C6bjLeJPt +PMAGGp5RrplxSgrSerw3I9DOWkHGtpqzIka3XneNUXJP8k4HUJ+aZkGH2ZILKS8d +4KMIb+KZSpHEGn+6uGccWLtZZmAjWJrDw56JbQtSHdRYLBRSOjLbTvQoPu/2Hpli +7HOxbotlvjptMunncq5aqK57SHA1dh0cwF7J3LUmGFJ67eoz+VV3b5qMn4MopSeI +mS16Ydd3nGpjSrln/elM0CQxqWfcOAXRZpDpFUQoXcBrLVzvz2DBl/0CrTRLhgzi +CO+5/IVcBWRlYpRNGgjjP7q0j6URID3jk5J06fYQXmBiwQT5j+GZqqzpMCJ9mIy2 +1O9SN1hebJnIcEU+E0njn/MGjlYdPywhaCy8pqElp6Q8TUEJpwLRFO/owCoBet/n +ZmCXUjfCGhc1pWHufFcDEQ6xMgEWWY/tdwCZeSU7EhErTjCbfupg+55A5fpDml0m +3wH4CFcuRjlqyx6Ywixm1ATeitDtJl5HQTw6b8OtEXwSgRmZ0eSqSRVk9QbVS7gu +IpQe09/Zimb5HzjZqZ3fdqHlcW4xax8hyJeyIvF5ZJ57eY8CBvu/wP2GDn26QnvF +xQqdfDbq1H4JmpwUHpbFwBoQK4Q6WFd1z4EA9bRQeo3H9PoqoOwMDjzajwLRF7b7 +q6tYH/n9PyHwdf1c4fFwgSmL1toXGfKlA9hjIaLsRSDD6srT5EdUk78bsnddwI51 +tu7C7P4JG+h1VdRNMNTlqtileWsIE7Nn2A1OkcUxZdF5mamENpDpJcHePLto6c8q +FKiwyFMsxhgsj6HK2HqO+UA4sX5Ni4oHwiPmb//EZLn045M5i1AN26KosJmb8++D +sgR5reWRy+UqJCTYblVg+7Dx++ggUnfxVyQEsWmw5r5f4KU5wXBkvoVMGtPNa9DE +n/uLtObD1qkNL38pRsr2OGRchYCgEoKGqEISBP4knfGXLOlWiW/246j9QzI97r1u +tvy7fKg28G7AUz9l6bpewsPHefBUeRQeieP9eJINaEpxkF/w2RpKDLpQjWxwDDOM +s+D0mrBMJve17AmJ8rMw6dIQPZYNZ88/jz1uQuUwQ2YlbmtZbCG81k9YMFGEU9XS +cyhJxj8hvYnt2PR5Z9/cJPyWOs0m/ufOeeQQ8SnU/lzmrQnpzUd2Z6p5i/B7LdRP +n1kX+l1qynuPnjvBz4nJQE0p6nzW8RyCDSniC9mtYtZmhgC8icqxgbvS7uEOBIYJ +NbK+0bEETTO34iY/JVTIqLOw3iQZYMeUpxpj6Phgx/oooxMTquMecPKNgeVtaBst +qjTNPX0ti1/HYpZqzYi8SV8YjHSJWCVMsZjKPr3W/HIcCKqYoIfgzi83Ha2KMQx6 +-----END RSA PRIVATE KEY----- diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java index 073b2f8268c56..fc5962d705c94 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java @@ -11,7 +11,6 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; -import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport; import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; @@ -24,34 +23,9 @@ class PkiRealmBootstrapCheck implements BootstrapCheck { private final SSLService sslService; - private final List sslConfigurations; - PkiRealmBootstrapCheck(Settings settings, SSLService sslService) { + PkiRealmBootstrapCheck(SSLService sslService) { this.sslService = sslService; - this.sslConfigurations = loadSslConfigurations(settings); - } - - /** - * {@link SSLConfiguration} may depend on {@link org.elasticsearch.common.settings.SecureSettings} that can only be read during startup. - * We need to preload these during component configuration. - */ - private List loadSslConfigurations(Settings settings) { - final List list = new ArrayList<>(); - if (HTTP_SSL_ENABLED.get(settings)) { - list.add(sslService.sslConfiguration(SSLService.getHttpTransportSSLSettings(settings), Settings.EMPTY)); - } - - if (XPackSettings.TRANSPORT_SSL_ENABLED.get(settings)) { - final Settings transportSslSettings = settings.getByPrefix(setting("transport.ssl.")); - list.add(sslService.sslConfiguration(transportSslSettings, Settings.EMPTY)); - - settings.getGroups("transport.profiles.").values().stream() - .map(SecurityNetty4Transport::profileSslSettings) - .map(s -> sslService.sslConfiguration(s, transportSslSettings)) - .forEach(list::add); - } - - return list; } /** @@ -65,7 +39,8 @@ public BootstrapCheckResult check(BootstrapContext context) { .filter(s -> PkiRealmSettings.TYPE.equals(s.get("type"))) .anyMatch(s -> s.getAsBoolean("enabled", true)); if (pkiRealmEnabled) { - for (SSLConfiguration configuration : this.sslConfigurations) { + for (String contextName : getSslContextNames(settings)) { + final SSLConfiguration configuration = sslService.getSSLConfiguration(contextName); if (sslService.isSSLClientAuthEnabled(configuration)) { return BootstrapCheckResult.success(); } @@ -77,6 +52,20 @@ public BootstrapCheckResult check(BootstrapContext context) { } } + private List getSslContextNames(Settings settings) { + final List list = new ArrayList<>(); + if (HTTP_SSL_ENABLED.get(settings)) { + list.add(setting("http.ssl")); + } + + if (XPackSettings.TRANSPORT_SSL_ENABLED.get(settings)) { + list.add(setting("transport.ssl")); + list.addAll(sslService.getTransportProfileContextNames()); + } + + return list; + } + @Override public boolean alwaysEnforce() { return true; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 596acaeeac6a0..26ec50c0eb3c4 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -117,16 +117,17 @@ import org.elasticsearch.xpack.core.security.authz.accesscontrol.SecurityIndexSearcherWrapper; import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions; import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; -import org.elasticsearch.xpack.security.authz.store.FileRolesStore; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.index.IndexAuditTrailField; import org.elasticsearch.xpack.core.security.user.AnonymousUser; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.core.ssl.TLSLicenseBootstrapCheck; import org.elasticsearch.xpack.core.ssl.action.GetCertificateInfoAction; import org.elasticsearch.xpack.core.ssl.action.TransportGetCertificateInfoAction; import org.elasticsearch.xpack.core.ssl.rest.RestGetCertificateInfoAction; +import org.elasticsearch.xpack.core.template.TemplateUtils; import org.elasticsearch.xpack.security.action.filter.SecurityActionFilter; import org.elasticsearch.xpack.security.action.interceptor.BulkShardRequestInterceptor; import org.elasticsearch.xpack.security.action.interceptor.IndicesAliasesRequestInterceptor; @@ -172,6 +173,7 @@ import org.elasticsearch.xpack.security.authz.SecuritySearchOperationListener; import org.elasticsearch.xpack.security.authz.accesscontrol.OptOutQueryCache; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; +import org.elasticsearch.xpack.security.authz.store.FileRolesStore; import org.elasticsearch.xpack.security.authz.store.NativeRolesStore; import org.elasticsearch.xpack.security.ingest.HashProcessor; import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor; @@ -232,9 +234,9 @@ import static java.util.Collections.singletonList; import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING; import static org.elasticsearch.xpack.core.XPackSettings.HTTP_SSL_ENABLED; -import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_TEMPLATE_NAME; -import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_INDEX_NAME; import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_INDEX_FORMAT; +import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_INDEX_NAME; +import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_TEMPLATE_NAME; public class Security extends Plugin implements ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin, DiscoveryPlugin, MapperPlugin, ExtensiblePlugin { @@ -286,7 +288,7 @@ public Security(Settings settings, final Path configPath) { final List checks = new ArrayList<>(); checks.addAll(Arrays.asList( new TokenSSLBootstrapCheck(), - new PkiRealmBootstrapCheck(settings, getSslService()), + new PkiRealmBootstrapCheck(getSslService()), new TLSLicenseBootstrapCheck(), new PasswordHashingAlgorithmBootstrapCheck())); checks.addAll(InternalRealms.getBootstrapChecks(settings, env)); @@ -877,10 +879,9 @@ public UnaryOperator getRestHandlerWrapper(ThreadContext threadCont return null; } final boolean ssl = HTTP_SSL_ENABLED.get(settings); - Settings httpSSLSettings = SSLService.getHttpTransportSSLSettings(settings); - boolean extractClientCertificate = ssl && getSslService().isSSLClientAuthEnabled(httpSSLSettings); - return handler -> new SecurityRestFilter(getLicenseState(), threadContext, authcService.get(), handler, - extractClientCertificate); + final SSLConfiguration httpSSLConfig = getSslService().getHttpTransportSSLConfiguration(); + boolean extractClientCertificate = ssl && getSslService().isSSLClientAuthEnabled(httpSSLConfig); + return handler -> new SecurityRestFilter(getLicenseState(), threadContext, authcService.get(), handler, extractClientCertificate); } @Override diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java index b149fec3d3db8..4226607f192f4 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java @@ -41,6 +41,7 @@ import org.elasticsearch.xpack.core.common.socket.SocketAccess; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.security.authc.file.FileUserPasswdStore; @@ -148,12 +149,12 @@ private String postURL(Settings settings, Environment env, String method, String HttpURLConnection conn; // If using SSL, need a custom service because it's likely a self-signed certificate if ("https".equalsIgnoreCase(uri.getScheme())) { - Settings sslSettings = settings.getByPrefix(setting("http.ssl.")); final SSLService sslService = new SSLService(settings, env); + final SSLConfiguration sslConfiguration = sslService.getSSLConfiguration(setting("http.ssl")); final HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection(); AccessController.doPrivileged((PrivilegedAction) () -> { // Requires permission java.lang.RuntimePermission "setFactory"; - httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslSettings)); + httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslConfiguration)); return null; }); conn = httpsConn; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/CommandLineHttpClient.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/CommandLineHttpClient.java index f14911402d60f..f69c3f6893fd8 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/CommandLineHttpClient.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/CommandLineHttpClient.java @@ -19,9 +19,11 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.common.socket.SocketAccess; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.security.authc.esnative.tool.HttpResponse.HttpResponseBuilder; +import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -35,8 +37,6 @@ import java.util.Collections; import java.util.List; -import javax.net.ssl.HttpsURLConnection; - import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_PORT; import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_PUBLISH_HOST; import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_PUBLISH_PORT; @@ -86,11 +86,10 @@ public HttpResponse execute(String method, URL url, String user, SecureString pa final SSLService sslService = new SSLService(settings, env); final HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection(); AccessController.doPrivileged((PrivilegedAction) () -> { - final Settings sslSettings = SSLService.getHttpTransportSSLSettings(settings); + final SSLConfiguration sslConfiguration = sslService.getHttpTransportSSLConfiguration(); // Requires permission java.lang.RuntimePermission "setFactory"; - httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslSettings)); - final boolean isHostnameVerificationEnabled = - sslService.getVerificationMode(sslSettings, Settings.EMPTY).isHostnameVerificationEnabled(); + httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslConfiguration)); + final boolean isHostnameVerificationEnabled = sslConfiguration.verificationMode().isHostnameVerificationEnabled(); if (isHostnameVerificationEnabled == false) { httpsConn.setHostnameVerifier((hostname, session) -> true); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactory.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactory.java index aabea2eb854e7..d062e45889524 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactory.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactory.java @@ -21,12 +21,11 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; import org.elasticsearch.xpack.core.ssl.SSLService; -import org.elasticsearch.xpack.core.ssl.VerificationMode; import javax.net.SocketFactory; - import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; @@ -133,20 +132,23 @@ protected static LDAPConnectionOptions connectionOptions(RealmConfig config, final Settings realmSSLSettings = realmSettings.getByPrefix("ssl."); final boolean verificationModeExists = sslConfigurationSettings.verificationMode.exists(realmSSLSettings); - final boolean hostnameVerficationExists = + final boolean hostnameVerificationExists = realmSettings.get(SessionFactorySettings.HOSTNAME_VERIFICATION_SETTING, null) != null; - if (verificationModeExists && hostnameVerficationExists) { + if (verificationModeExists && hostnameVerificationExists) { throw new IllegalArgumentException("[" + SessionFactorySettings.HOSTNAME_VERIFICATION_SETTING + "] and [" + sslConfigurationSettings.verificationMode.getKey() + "] may not be used at the same time"); } else if (verificationModeExists) { - VerificationMode verificationMode = sslService.getVerificationMode(realmSSLSettings, - Settings.EMPTY); - if (verificationMode == VerificationMode.FULL) { + final String sslKey = RealmSettings.getFullSettingKey(config, "ssl"); + final SSLConfiguration sslConfiguration = sslService.getSSLConfiguration(sslKey); + if (sslConfiguration == null) { + throw new IllegalStateException("cannot find SSL configuration for " + sslKey); + } + if (sslConfiguration.verificationMode().isHostnameVerificationEnabled()) { options.setSSLSocketVerifier(new HostNameSSLSocketVerifier(true)); } - } else if (hostnameVerficationExists) { + } else if (hostnameVerificationExists) { new DeprecationLogger(logger).deprecated("the setting [{}] has been deprecated and " + "will be removed in a future version. use [{}] instead", RealmSettings.getFullSettingKey(config, SessionFactorySettings.HOSTNAME_VERIFICATION_SETTING), @@ -180,7 +182,8 @@ private ServerSet serverSet(RealmConfig realmConfig, SSLService clientSSLService Settings settings = realmConfig.settings(); SocketFactory socketFactory = null; if (ldapServers.ssl()) { - socketFactory = clientSSLService.sslSocketFactory(settings.getByPrefix("ssl.")); + SSLConfiguration ssl = clientSSLService.getSSLConfiguration(RealmSettings.getFullSettingKey(realmConfig, "ssl")); + socketFactory = clientSSLService.sslSocketFactory(ssl); if (settings.getAsBoolean(SessionFactorySettings.HOSTNAME_VERIFICATION_SETTING, true)) { logger.debug("using encryption for LDAP connections with hostname verification"); } else { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java index dfa86ffe5eda0..cc160c8f78b33 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java @@ -44,6 +44,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; import org.elasticsearch.xpack.core.security.user.User; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.core.ssl.X509KeyPairSettings; @@ -498,10 +499,11 @@ private static Tuple> TRANSPORT_TYPE_SETTING_TEMPLATE = (key) -> new Setting<>(key, - "node", v - -> { - if (v.equals("node") || v.equals("client")) { - return v; - } - throw new IllegalArgumentException("type must be one of [client, node]"); + private static final Function> TRANSPORT_TYPE_SETTING_TEMPLATE = key -> new Setting<>(key, "node", v -> { + if (v.equals("node") || v.equals("client")) { + return v; + } + throw new IllegalArgumentException("type must be one of [client, node]"); }, Setting.Property.NodeScope); private static final String TRANSPORT_TYPE_SETTING_KEY = "xpack.security.type"; - public static final Setting TRANSPORT_TYPE_PROFILE_SETTING = Setting.affixKeySetting("transport.profiles.", + public static final Setting.AffixSetting TRANSPORT_TYPE_PROFILE_SETTING = Setting.affixKeySetting("transport.profiles.", TRANSPORT_TYPE_SETTING_KEY, TRANSPORT_TYPE_SETTING_TEMPLATE); private final AuthenticationService authcService; @@ -175,17 +173,17 @@ public TransportRequestHandler interceptHandler( } protected Map initializeProfileFilters(DestructiveOperations destructiveOperations) { - Map profileSettingsMap = settings.getGroups("transport.profiles.", true); - Map profileFilters = new HashMap<>(profileSettingsMap.size() + 1); + final SSLConfiguration transportSslConfiguration = sslService.getSSLConfiguration(setting("transport.ssl")); + final Map profileConfigurations = SecurityNetty4Transport.getTransportProfileConfigurations(settings, + sslService, transportSslConfiguration); + + Map profileFilters = new HashMap<>(profileConfigurations.size() + 1); - final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl.")); final boolean transportSSLEnabled = XPackSettings.TRANSPORT_SSL_ENABLED.get(settings); - for (Map.Entry entry : profileSettingsMap.entrySet()) { - Settings profileSettings = entry.getValue(); - final Settings profileSslSettings = SecurityNetty4Transport.profileSslSettings(profileSettings); - final boolean extractClientCert = transportSSLEnabled && - sslService.isSSLClientAuthEnabled(profileSslSettings, transportSSLSettings); - String type = TRANSPORT_TYPE_SETTING_TEMPLATE.apply(TRANSPORT_TYPE_SETTING_KEY).get(entry.getValue()); + for (Map.Entry entry : profileConfigurations.entrySet()) { + final SSLConfiguration profileConfiguration = entry.getValue(); + final boolean extractClientCert = transportSSLEnabled && sslService.isSSLClientAuthEnabled(profileConfiguration); + final String type = TRANSPORT_TYPE_PROFILE_SETTING.getConcreteSettingForNamespace(entry.getKey()).get(settings); switch (type) { case "client": profileFilters.put(entry.getKey(), new ServerTransportFilter.ClientProfile(authcService, authzService, @@ -202,12 +200,6 @@ protected Map initializeProfileFilters(Destructiv } } - if (!profileFilters.containsKey(TcpTransport.DEFAULT_PROFILE)) { - final boolean extractClientCert = transportSSLEnabled && sslService.isSSLClientAuthEnabled(transportSSLSettings); - profileFilters.put(TcpTransport.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService, - threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled, securityContext)); - } - return Collections.unmodifiableMap(profileFilters); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java index 9667ca675b4c1..d7a609f6f14ba 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransport.java @@ -32,7 +32,6 @@ public class SecurityNetty4HttpServerTransport extends Netty4HttpServerTransport { private final IPFilter ipFilter; - private final Settings sslSettings; private final SSLService sslService; private final SSLConfiguration sslConfiguration; @@ -42,10 +41,9 @@ public SecurityNetty4HttpServerTransport(Settings settings, NetworkService netwo super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher); this.ipFilter = ipFilter; final boolean ssl = HTTP_SSL_ENABLED.get(settings); - this.sslSettings = SSLService.getHttpTransportSSLSettings(settings); this.sslService = sslService; if (ssl) { - this.sslConfiguration = sslService.sslConfiguration(sslSettings, Settings.EMPTY); + this.sslConfiguration = sslService.getHttpTransportSSLConfiguration(); if (sslService.isConfigurationValidForServerUsage(sslConfiguration) == false) { throw new IllegalArgumentException("a key must be provided to run as a server. the key should be configured using the " + "[xpack.security.http.ssl.key] or [xpack.security.http.ssl.keystore.path] setting"); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioTransport.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioTransport.java index 1e00019793025..fb94b669e833b 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioTransport.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/nio/SecurityNioTransport.java @@ -42,7 +42,6 @@ import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; import java.util.function.Predicate; @@ -74,22 +73,12 @@ public SecurityNioTransport(Settings settings, ThreadPool threadPool, NetworkSer this.authenticator = authenticator; this.sslService = sslService; this.sslEnabled = XPackSettings.TRANSPORT_SSL_ENABLED.get(settings); - final Settings transportSSLSettings = settings.getByPrefix(setting("transport.ssl.")); if (sslEnabled) { - Map profileSettingsMap = settings.getGroups("transport.profiles.", true); - Map profileConfiguration = new HashMap<>(profileSettingsMap.size() + 1); - for (Map.Entry entry : profileSettingsMap.entrySet()) { - Settings profileSettings = entry.getValue(); - final Settings profileSslSettings = SecurityNetty4Transport.profileSslSettings(profileSettings); - SSLConfiguration configuration = sslService.sslConfiguration(profileSslSettings, transportSSLSettings); - profileConfiguration.put(entry.getKey(), configuration); - } - - if (profileConfiguration.containsKey(TcpTransport.DEFAULT_PROFILE) == false) { - profileConfiguration.put(TcpTransport.DEFAULT_PROFILE, sslService.sslConfiguration(transportSSLSettings, Settings.EMPTY)); - } - + final SSLConfiguration transportConfiguration = sslService.getSSLConfiguration(setting("transport.ssl.")); + Map profileConfiguration = SecurityNetty4Transport.getTransportProfileConfigurations(settings, + sslService, transportConfiguration); this.profileConfiguration = Collections.unmodifiableMap(profileConfiguration); + } else { profileConfiguration = Collections.emptyMap(); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java index 5610da6f75c6b..f9b1be65736e1 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheckTests.java @@ -83,7 +83,7 @@ public void testBootstrapCheckWithPkiRealm() throws Exception { } private BootstrapCheck.BootstrapCheckResult runCheck(Settings settings, Environment env) throws Exception { - return new PkiRealmBootstrapCheck(settings, new SSLService(settings, env)).check(new BootstrapContext(settings, null)); + return new PkiRealmBootstrapCheck(new SSLService(settings, env)).check(new BootstrapContext(settings, null)); } public void testBootstrapCheckWithDisabledRealm() throws Exception { @@ -112,7 +112,7 @@ public void testBootstrapCheckWithClosedSecuredSetting() throws Exception { .build(); Environment env = TestEnvironment.newEnvironment(settings); - final PkiRealmBootstrapCheck check = new PkiRealmBootstrapCheck(settings, new SSLService(settings, env)); + final PkiRealmBootstrapCheck check = new PkiRealmBootstrapCheck(new SSLService(settings, env)); secureSettings.close(); assertThat(check.check(new BootstrapContext(settings, null)).isFailure(), Matchers.equalTo(expectFail)); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java index 739523795e7c5..2c6756aada7ac 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; @@ -140,9 +141,25 @@ public boolean enableWarningsCheck() { return false; } + /** + * Creates a realm with the provided settings, rebuilds the SSL Service to be aware of the new realm, and then returns + * the RealmConfig + */ + private RealmConfig setupRealm(String realmName, Settings settings) { + final Settings merged = Settings.builder() + .put(settings) + .normalizePrefix("xpack.security.authc.realms." + realmName + ".") + .put(globalSettings) + .build(); + + final Environment env = TestEnvironment.newEnvironment(merged); + this.sslService = new SSLService(merged, env); + return new RealmConfig(realmName, settings, merged, env, new ThreadContext(merged)); + } + public void testAuthenticateUserPrincipleName() throws Exception { Settings settings = settings(); - RealmConfig config = new RealmConfig("testAuthenticateUserPrincipleName", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testAuthenticateUserPrincipleName", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -158,7 +175,7 @@ public void testAuthenticateUserPrincipleName() throws Exception { public void testAuthenticateSAMAccountName() throws Exception { Settings settings = settings(); - RealmConfig config = new RealmConfig("testAuthenticateSAMAccountName", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testAuthenticateSAMAccountName", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -182,7 +199,7 @@ protected String[] ldapUrls() throws LDAPException { public void testAuthenticateCachesSuccessfulAuthentications() throws Exception { Settings settings = settings(); - RealmConfig config = new RealmConfig("testAuthenticateCachesSuccesfulAuthentications", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testAuthenticateCachesSuccesfulAuthentications", settings); ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, sslService, threadPool)); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -200,7 +217,7 @@ public void testAuthenticateCachesSuccessfulAuthentications() throws Exception { public void testAuthenticateCachingCanBeDisabled() throws Exception { Settings settings = settings(Settings.builder().put(CachingUsernamePasswordRealmSettings.CACHE_TTL_SETTING.getKey(), -1).build()); - RealmConfig config = new RealmConfig("testAuthenticateCachingCanBeDisabled", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testAuthenticateCachingCanBeDisabled", settings); ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, sslService, threadPool)); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -218,7 +235,7 @@ public void testAuthenticateCachingCanBeDisabled() throws Exception { public void testAuthenticateCachingClearsCacheOnRoleMapperRefresh() throws Exception { Settings settings = settings(); - RealmConfig config = new RealmConfig("testAuthenticateCachingClearsCacheOnRoleMapperRefresh", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testAuthenticateCachingClearsCacheOnRoleMapperRefresh", settings); ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, sslService, threadPool)); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -266,8 +283,7 @@ private void doUnauthenticatedLookup(boolean pooled) throws Exception { builder.setSecureSettings(secureSettings); } Settings settings = settings(builder.build()); - RealmConfig config = new RealmConfig("testUnauthenticatedLookupWithConnectionPool", settings, globalSettings, - TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testUnauthenticatedLookupWithConnectionPool", settings); try (ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool)) { DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -284,7 +300,7 @@ public void testRealmMapsGroupsToRoles() throws Exception { Settings settings = settings(Settings.builder() .put(ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml")) .build()); - RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testRealmMapsGroupsToRoles", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -300,7 +316,7 @@ public void testRealmMapsUsersToRoles() throws Exception { Settings settings = settings(Settings.builder() .put(ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml")) .build()); - RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testRealmMapsGroupsToRoles", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -318,8 +334,7 @@ public void testRealmUsageStats() throws Exception { .put(ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml")) .put("load_balance.type", loadBalanceType) .build()); - RealmConfig config = new RealmConfig("testRealmUsageStats", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testRealmUsageStats", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); DnRoleMapper roleMapper = new DnRoleMapper(config, resourceWatcherService); LdapRealm realm = new LdapRealm(LdapRealmSettings.AD_TYPE, config, sessionFactory, roleMapper, threadPool); @@ -337,8 +352,7 @@ public void testRealmUsageStats() throws Exception { public void testDefaultSearchFilters() throws Exception { Settings settings = settings(); - RealmConfig config = new RealmConfig("testDefaultSearchFilters", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testDefaultSearchFilters", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); assertEquals("(&(objectClass=user)(|(sAMAccountName={0})(userPrincipalName={0}@ad.test.elasticsearch.com)))", sessionFactory.defaultADAuthenticator.getUserSearchFilter()); @@ -352,8 +366,7 @@ public void testCustomSearchFilters() throws Exception { .put(ActiveDirectorySessionFactorySettings.AD_UPN_USER_SEARCH_FILTER_SETTING, "(objectClass=upn)") .put(ActiveDirectorySessionFactorySettings.AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING, "(objectClass=down level)") .build()); - RealmConfig config = new RealmConfig("testDefaultSearchFilters", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testDefaultSearchFilters", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); assertEquals("(objectClass=default)", sessionFactory.defaultADAuthenticator.getUserSearchFilter()); assertEquals("(objectClass=upn)", sessionFactory.upnADAuthenticator.getUserSearchFilter()); @@ -364,8 +377,7 @@ public void testBuildUrlFromDomainNameAndDefaultPort() throws Exception { Settings settings = Settings.builder() .put(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING, "ad.test.elasticsearch.com") .build(); - RealmConfig config = new RealmConfig("testBuildUrlFromDomainNameAndDefaultPort", settings, globalSettings, - TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testBuildUrlFromDomainNameAndDefaultPort", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); assertSingleLdapServer(sessionFactory, "ad.test.elasticsearch.com", 389); } @@ -375,8 +387,7 @@ public void testBuildUrlFromDomainNameAndCustomPort() throws Exception { .put(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING, "ad.test.elasticsearch.com") .put(ActiveDirectorySessionFactorySettings.AD_LDAP_PORT_SETTING.getKey(), 10389) .build(); - RealmConfig config = new RealmConfig("testBuildUrlFromDomainNameAndCustomPort", settings, globalSettings, - TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testBuildUrlFromDomainNameAndCustomPort", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); assertSingleLdapServer(sessionFactory, "ad.test.elasticsearch.com", 10389); } @@ -386,8 +397,7 @@ public void testUrlConfiguredInSettings() throws Exception { .put(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING, "ad.test.elasticsearch.com") .put(SessionFactorySettings.URLS_SETTING, "ldap://ad01.testing.elastic.co:20389/") .build(); - RealmConfig config = new RealmConfig("testBuildUrlFromDomainNameAndCustomPort", settings, globalSettings, - TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = setupRealm("testBuildUrlFromDomainNameAndCustomPort", settings); ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool); assertSingleLdapServer(sessionFactory, "ad01.testing.elastic.co", 20389); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java index 2e79a45363b2d..4aff821217d14 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -19,6 +20,7 @@ import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; import org.elasticsearch.xpack.core.security.authc.Realm; import org.elasticsearch.xpack.core.security.authc.RealmConfig; +import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings; import org.elasticsearch.xpack.core.security.authc.ldap.LdapSessionFactorySettings; import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope; @@ -64,15 +66,15 @@ public class LdapRealmTests extends LdapTestCase { private ThreadPool threadPool; private ResourceWatcherService resourceWatcherService; - private Settings globalSettings; + private Settings defaultGlobalSettings; private SSLService sslService; @Before public void init() throws Exception { threadPool = new TestThreadPool("ldap realm tests"); resourceWatcherService = new ResourceWatcherService(Settings.EMPTY, threadPool); - globalSettings = Settings.builder().put("path.home", createTempDir()).build(); - sslService = new SSLService(globalSettings, TestEnvironment.newEnvironment(globalSettings)); + defaultGlobalSettings = Settings.builder().put("path.home", createTempDir()).build(); + sslService = new SSLService(defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings)); } @After @@ -85,7 +87,7 @@ public void testAuthenticateSubTreeGroupSearch() throws Exception { String groupSearchBase = "o=sevenSeas"; String userTemplate = VALID_USER_TEMPLATE; Settings settings = buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE); - RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); LdapRealm ldap = new LdapRealm(LdapRealmSettings.LDAP_TYPE, config, ldapFactory, buildGroupAsRoleMapper(resourceWatcherService), threadPool); @@ -109,7 +111,7 @@ public void testAuthenticateOneLevelGroupSearch() throws Exception { Settings settings = Settings.builder() .put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL)) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); LdapRealm ldap = @@ -134,7 +136,7 @@ public void testAuthenticateCaching() throws Exception { Settings settings = Settings.builder() .put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE)) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); ldapFactory = spy(ldapFactory); @@ -159,7 +161,7 @@ public void testAuthenticateCachingRefresh() throws Exception { Settings settings = Settings.builder() .put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE)) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); DnRoleMapper roleMapper = buildGroupAsRoleMapper(resourceWatcherService); @@ -192,7 +194,7 @@ public void testAuthenticateNoncaching() throws Exception { .put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE)) .put(CachingUsernamePasswordRealmSettings.CACHE_TTL_SETTING.getKey(), -1) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); ldapFactory = spy(ldapFactory); @@ -219,8 +221,18 @@ public void testLdapRealmSelectsLdapSessionFactory() throws Exception { .put("group_search.scope", LdapSearchScope.SUB_TREE) .put("ssl.verification_mode", VerificationMode.CERTIFICATE) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); - SessionFactory sessionFactory = LdapRealm.sessionFactory(config, sslService, threadPool, LdapRealmSettings.LDAP_TYPE); + + final String realmName = "test-ldap-realm"; + final Settings globalSettings = Settings.builder() + .put(settings) + .normalizePrefix(RealmSettings.PREFIX + realmName + ".") + .put(defaultGlobalSettings) + .build(); + + final Environment env = TestEnvironment.newEnvironment(globalSettings); + final RealmConfig config = new RealmConfig(realmName, settings, globalSettings, env, new ThreadContext(globalSettings)); + SessionFactory sessionFactory = LdapRealm.sessionFactory(config, new SSLService(globalSettings, env), threadPool, + LdapRealmSettings.LDAP_TYPE); assertThat(sessionFactory, is(instanceOf(LdapSessionFactory.class))); } @@ -235,8 +247,16 @@ public void testLdapRealmSelectsLdapUserSearchSessionFactory() throws Exception .put("group_search.scope", LdapSearchScope.SUB_TREE) .put("ssl.verification_mode", VerificationMode.CERTIFICATE) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); - SessionFactory sessionFactory = LdapRealm.sessionFactory(config, sslService, threadPool, LdapRealmSettings.LDAP_TYPE); + final String realmName = "test-ldap-realm-user-search"; + final Settings globalSettings = Settings.builder() + .put(settings) + .normalizePrefix(RealmSettings.PREFIX + realmName + ".") + .put(defaultGlobalSettings) + .build(); + final Environment env = TestEnvironment.newEnvironment(globalSettings); + final RealmConfig config = new RealmConfig(realmName, settings, globalSettings, env, new ThreadContext(globalSettings)); + SessionFactory sessionFactory = LdapRealm.sessionFactory(config, new SSLService(globalSettings, env), threadPool, + LdapRealmSettings.LDAP_TYPE); try { assertThat(sessionFactory, is(instanceOf(LdapUserSearchSessionFactory.class))); } finally { @@ -259,7 +279,7 @@ public void testLdapRealmThrowsExceptionForUserTemplateAndSearchSettings() throw .put("group_search.scope", LdapSearchScope.SUB_TREE) .put("ssl.verification_mode", VerificationMode.CERTIFICATE) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> LdapRealm.sessionFactory(config, null, threadPool, LdapRealmSettings.LDAP_TYPE)); assertThat(e.getMessage(), @@ -275,7 +295,7 @@ public void testLdapRealmThrowsExceptionWhenNeitherUserTemplateNorSearchSettings .put("group_search.scope", LdapSearchScope.SUB_TREE) .put("ssl.verification_mode", VerificationMode.CERTIFICATE) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> LdapRealm.sessionFactory(config, null, threadPool, LdapRealmSettings.LDAP_TYPE)); assertThat(e.getMessage(), @@ -292,7 +312,7 @@ public void testLdapRealmMapsUserDNToRole() throws Exception { .put(DnRoleMapperSettings.ROLE_MAPPING_FILE_SETTING.getKey(), getDataPath("/org/elasticsearch/xpack/security/authc/support/role_mapping.yml")) .build(); - RealmConfig config = new RealmConfig("test-ldap-realm-userdn", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm-userdn", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); LdapRealm ldap = new LdapRealm(LdapRealmSettings.LDAP_TYPE, config, ldapFactory, @@ -319,7 +339,7 @@ public void testLdapConnectionFailureIsTreatedAsAuthenticationFailure() throws E String groupSearchBase = "o=sevenSeas"; String userTemplate = VALID_USER_TEMPLATE; Settings settings = buildLdapSettings(new String[] { url.toString() }, userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE); - RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = new RealmConfig("test-ldap-realm", settings, defaultGlobalSettings, TestEnvironment.newEnvironment(defaultGlobalSettings), new ThreadContext(defaultGlobalSettings)); LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); LdapRealm ldap = new LdapRealm(LdapRealmSettings.LDAP_TYPE, config, ldapFactory, buildGroupAsRoleMapper(resourceWatcherService), threadPool); @@ -353,9 +373,17 @@ public void testUsageStats() throws Exception { settings.put("user_search.base_dn", ""); } - RealmConfig config = new RealmConfig("ldap-realm", settings.build(), globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); - - LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); + final Settings realmSettings = settings.build(); + final String realmName = "ldap-realm"; + final Settings globalSettings = Settings.builder() + .put(realmSettings) + .normalizePrefix(RealmSettings.PREFIX + realmName + ".") + .put(defaultGlobalSettings) + .build(); + final Environment env = TestEnvironment.newEnvironment(globalSettings); + final RealmConfig config = new RealmConfig(realmName, realmSettings, globalSettings, env, new ThreadContext(globalSettings)); + + LdapSessionFactory ldapFactory = new LdapSessionFactory(config, new SSLService(globalSettings, env), threadPool); LdapRealm realm = new LdapRealm(LdapRealmSettings.LDAP_TYPE, config, ldapFactory, new DnRoleMapper(config, resourceWatcherService), threadPool); @@ -363,7 +391,7 @@ public void testUsageStats() throws Exception { realm.usageStats(future); Map stats = future.get(); assertThat(stats, is(notNullValue())); - assertThat(stats, hasEntry("name", "ldap-realm")); + assertThat(stats, hasEntry("name", realmName)); assertThat(stats, hasEntry("order", realm.order())); assertThat(stats, hasEntry("size", 0)); assertThat(stats, hasEntry("ssl", false)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapTestUtils.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapTestUtils.java index dced24544aa98..9a9368c25e127 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapTestUtils.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapTestUtils.java @@ -15,6 +15,7 @@ import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.core.ssl.VerificationMode; import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils; @@ -59,16 +60,13 @@ public static LDAPConnection openConnection(String url, String bindDN, String bi options.setConnectTimeoutMillis(Math.toIntExact(SessionFactorySettings.TIMEOUT_DEFAULT.millis())); options.setResponseTimeoutMillis(SessionFactorySettings.TIMEOUT_DEFAULT.millis()); - Settings connectionSettings; + final SSLConfiguration sslConfiguration; if (useGlobalSSL) { - connectionSettings = Settings.EMPTY; + sslConfiguration = sslService.getSSLConfiguration("_global"); } else { - MockSecureSettings connSecureSettings = new MockSecureSettings(); - connSecureSettings.setString("truststore.secure_password", "changeit"); - connectionSettings = Settings.builder().put("truststore.path", truststore) - .setSecureSettings(connSecureSettings).build(); + sslConfiguration = sslService.getSSLConfiguration("xpack.security.authc.realms.foo.ssl"); } - return LdapUtils.privilegedConnect(() -> new LDAPConnection(sslService.sslSocketFactory(connectionSettings), options, + return LdapUtils.privilegedConnect(() -> new LDAPConnection(sslService.sslSocketFactory(sslConfiguration), options, ldapurl.getHost(), ldapurl.getPort(), bindDN, bindPassword)); } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryTests.java index 1a5fa6af5f8a3..d82eb520e9f18 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryTests.java @@ -19,12 +19,15 @@ import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.security.authc.RealmConfig; +import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.core.ssl.VerificationMode; import org.junit.After; import org.junit.Before; +import java.util.function.Function; + import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -65,34 +68,39 @@ public void testConnectionFactoryReturnsCorrectLDAPConnectionOptions() throws Ex .put(SessionFactorySettings.FOLLOW_REFERRALS_SETTING, "false") .build(); - final Environment environment = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build()); - RealmConfig realmConfig = new RealmConfig("conn settings", settings, environment.settings(), environment, new ThreadContext(Settings.EMPTY)); - LDAPConnectionOptions options = SessionFactory.connectionOptions(realmConfig, new SSLService(environment.settings(), environment), - logger); + final String realmName = "conn_settings"; + final Function globalSettings = realmSettings -> Settings.builder() + .put(realmSettings) + .normalizePrefix(RealmSettings.PREFIX + realmName + ".") + .put("path.home", createTempDir()) + .build(); + final Environment environment = TestEnvironment.newEnvironment(globalSettings.apply(settings)); + final Function sslService = realmSettings -> new SSLService(globalSettings.apply(realmSettings), environment); + + final ThreadContext threadContext = new ThreadContext(environment.settings()); + RealmConfig realmConfig = new RealmConfig(realmName, settings, environment.settings(), environment, threadContext); + LDAPConnectionOptions options = SessionFactory.connectionOptions(realmConfig, sslService.apply(settings), logger); assertThat(options.followReferrals(), is(equalTo(false))); assertThat(options.allowConcurrentSocketFactoryUse(), is(equalTo(true))); assertThat(options.getConnectTimeoutMillis(), is(equalTo(10))); assertThat(options.getResponseTimeoutMillis(), is(equalTo(20L))); assertThat(options.getSSLSocketVerifier(), is(instanceOf(TrustAllSSLSocketVerifier.class))); - assertWarnings("the setting [xpack.security.authc.realms.conn settings.hostname_verification] has been deprecated and will be " + - "removed in a future version. use [xpack.security.authc.realms.conn settings.ssl.verification_mode] instead"); + assertWarnings("the setting [xpack.security.authc.realms." + realmName + ".hostname_verification] has been deprecated" + + " and will be removed in a future version. use [xpack.security.authc.realms." + realmName + ".ssl.verification_mode] instead"); settings = Settings.builder().put("ssl.verification_mode", VerificationMode.CERTIFICATE).build(); - realmConfig = new RealmConfig("conn settings", settings, environment.settings(), environment, new ThreadContext(Settings.EMPTY)); - options = SessionFactory.connectionOptions(realmConfig, new SSLService(environment.settings(), environment), - logger); + realmConfig = new RealmConfig(realmName, settings, globalSettings.apply(settings), environment, threadContext); + options = SessionFactory.connectionOptions(realmConfig, sslService.apply(settings), logger); assertThat(options.getSSLSocketVerifier(), is(instanceOf(TrustAllSSLSocketVerifier.class))); settings = Settings.builder().put("ssl.verification_mode", VerificationMode.NONE).build(); - realmConfig = new RealmConfig("conn settings", settings, environment.settings(), environment, new ThreadContext(Settings.EMPTY)); - options = SessionFactory.connectionOptions(realmConfig, new SSLService(environment.settings(), environment), - logger); + realmConfig = new RealmConfig(realmName, settings, environment.settings(), environment, threadContext); + options = SessionFactory.connectionOptions(realmConfig, sslService.apply(settings), logger); assertThat(options.getSSLSocketVerifier(), is(instanceOf(TrustAllSSLSocketVerifier.class))); settings = Settings.builder().put("ssl.verification_mode", VerificationMode.FULL).build(); - realmConfig = new RealmConfig("conn settings", settings, environment.settings(), environment, new ThreadContext(Settings.EMPTY)); - options = SessionFactory.connectionOptions(realmConfig, new SSLService(environment.settings(), environment), - logger); + realmConfig = new RealmConfig(realmName, settings, environment.settings(), environment, threadContext); + options = SessionFactory.connectionOptions(realmConfig, sslService.apply(settings), logger); assertThat(options.getSSLSocketVerifier(), is(instanceOf(HostNameSSLSocketVerifier.class))); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java index 3d9227319a870..f87ab36d3d574 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport; import org.elasticsearch.xpack.core.ssl.SSLClientAuth; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import org.junit.Before; @@ -200,7 +201,9 @@ public void testTransportSSLOverridesGlobalSSL() throws Exception { assertFalse(engine.getWantClientAuth()); // get the global and verify that it is different in that it requires client auth - final SSLEngine globalEngine = sslService.createSSLEngine(Settings.EMPTY, Settings.EMPTY); + SSLConfiguration configuration = sslService.getSSLConfiguration("xpack.ssl"); + assertNotNull(configuration); + final SSLEngine globalEngine = sslService.createSSLEngine(configuration, null, -1); assertTrue(globalEngine.getNeedClientAuth()); assertFalse(globalEngine.getWantClientAuth()); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SimpleSecurityNioTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SimpleSecurityNioTransportTests.java index 835fcf302c727..9f33da7ae88af 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SimpleSecurityNioTransportTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SimpleSecurityNioTransportTests.java @@ -32,12 +32,12 @@ import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.common.socket.SocketAccess; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import javax.net.SocketFactory; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLSocket; - import java.io.IOException; import java.net.InetAddress; import java.net.SocketTimeoutException; @@ -160,7 +160,8 @@ public void testBindUnavailableAddress() { @SuppressForbidden(reason = "Need to open socket connection") public void testRenegotiation() throws Exception { SSLService sslService = createSSLService(); - SocketFactory factory = sslService.sslSocketFactory(Settings.EMPTY); + final SSLConfiguration sslConfiguration = sslService.getSSLConfiguration("xpack.ssl"); + SocketFactory factory = sslService.sslSocketFactory(sslConfiguration); try (SSLSocket socket = (SSLSocket) factory.createSocket()) { SocketAccess.doPrivileged(() -> socket.connect(serviceA.boundAddress().publishAddress().address())); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java index 782168d73d7fb..fa8fd00aeba61 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.xpack.core.TestXPackTransportClient; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.common.socket.SocketAccess; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.security.LocalStateSecurity; @@ -121,8 +122,9 @@ public void testThatConnectionToHTTPWorks() throws Exception { CredentialsProvider provider = new BasicCredentialsProvider(); provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nodeClientUsername(), new String(nodeClientPassword().getChars()))); + SSLConfiguration sslConfiguration = service.getSSLConfiguration("xpack.ssl"); try (CloseableHttpClient client = HttpClients.custom() - .setSSLSocketFactory(new SSLConnectionSocketFactory(service.sslSocketFactory(Settings.EMPTY), + .setSSLSocketFactory(new SSLConnectionSocketFactory(service.sslSocketFactory(sslConfiguration), SSLConnectionSocketFactory.getDefaultHostnameVerifier())) .setDefaultCredentialsProvider(provider).build(); CloseableHttpResponse response = SocketAccess.doPrivileged(() -> client.execute(new HttpGet(getNodeUrl())))) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLReloadIntegTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLReloadIntegTests.java index bebf74a368f34..4269d8a78eb7a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLReloadIntegTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLReloadIntegTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.test.SecuritySettingsSource; import org.elasticsearch.transport.Transport; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import javax.net.ssl.SSLHandshakeException; @@ -95,7 +96,8 @@ public void testThatSSLConfigurationReloadsOnModification() throws Exception { .build(); String node = randomFrom(internalCluster().getNodeNames()); SSLService sslService = new SSLService(settings, TestEnvironment.newEnvironment(settings)); - SSLSocketFactory sslSocketFactory = sslService.sslSocketFactory(settings); + SSLConfiguration sslConfiguration = sslService.getSSLConfiguration("xpack.ssl"); + SSLSocketFactory sslSocketFactory = sslService.sslSocketFactory(sslConfiguration); TransportAddress address = internalCluster() .getInstance(Transport.class, node).boundAddress().publishAddress(); try (SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(address.getAddress(), address.getPort())) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLTrustRestrictionsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLTrustRestrictionsTests.java index edf512f49cb9d..beebf928fcf27 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLTrustRestrictionsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/ssl/SSLTrustRestrictionsTests.java @@ -19,6 +19,7 @@ import org.elasticsearch.xpack.core.ssl.CertParsingUtils; import org.elasticsearch.xpack.core.ssl.PemUtils; import org.elasticsearch.xpack.core.ssl.RestrictedTrustManager; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -212,7 +213,8 @@ private void tryConnect(CertificateInfo certificate) throws Exception { String node = randomFrom(internalCluster().getNodeNames()); SSLService sslService = new SSLService(settings, TestEnvironment.newEnvironment(settings)); - SSLSocketFactory sslSocketFactory = sslService.sslSocketFactory(settings); + SSLConfiguration sslConfiguration = sslService.getSSLConfiguration("xpack.ssl"); + SSLSocketFactory sslSocketFactory = sslService.sslSocketFactory(sslConfiguration); TransportAddress address = internalCluster().getInstance(Transport.class, node).boundAddress().publishAddress(); try (SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket(address.getAddress(), address.getPort())) { assertThat(socket.isConnected(), is(true)); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java index 80d12f5fbce4e..97d7779346fae 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpClient.java @@ -40,6 +40,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.core.internal.io.Streams; import org.elasticsearch.xpack.core.common.socket.SocketAccess; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.watcher.common.http.auth.ApplicableHttpAuth; import org.elasticsearch.xpack.watcher.common.http.auth.HttpAuthRegistry; @@ -83,10 +84,10 @@ public HttpClient(Settings settings, HttpAuthRegistry httpAuthRegistry, SSLServi HttpClientBuilder clientBuilder = HttpClientBuilder.create(); // ssl setup - Settings sslSettings = settings.getByPrefix(SETTINGS_SSL_PREFIX); - boolean isHostnameVerificationEnabled = sslService.getVerificationMode(sslSettings, Settings.EMPTY).isHostnameVerificationEnabled(); + SSLConfiguration sslConfiguration = sslService.getSSLConfiguration(SETTINGS_SSL_PREFIX); + boolean isHostnameVerificationEnabled = sslConfiguration.verificationMode().isHostnameVerificationEnabled(); HostnameVerifier verifier = isHostnameVerificationEnabled ? new DefaultHostnameVerifier() : NoopHostnameVerifier.INSTANCE; - SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslService.sslSocketFactory(sslSettings), verifier); + SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslService.sslSocketFactory(sslConfiguration), verifier); clientBuilder.setSSLSocketFactory(factory); clientBuilder.evictExpiredConnections(); diff --git a/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/test/OpenLdapTests.java b/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/test/OpenLdapTests.java index eced8a1b39ae8..c6d541b8064fd 100644 --- a/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/test/OpenLdapTests.java +++ b/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/test/OpenLdapTests.java @@ -55,6 +55,7 @@ public class OpenLdapTests extends ESTestCase { private static final String HAWKEYE_DN = "uid=hawkeye,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; public static final String LDAPTRUST_PATH = "/idptrust.jks"; private static final SecureString PASSWORD_SECURE_STRING = new SecureString(PASSWORD.toCharArray()); + public static final String REALM_NAME = "oldap-test"; private boolean useGlobalSSL; private SSLService sslService; @@ -91,18 +92,18 @@ public void initializeSslSocketFactory() throws Exception { builder.put("xpack.ssl.truststore.path", truststore); mockSecureSettings.setString("xpack.ssl.truststore.secure_password", "changeit"); - // fake realm to load config with certificate verification mode - builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore); - mockSecureSettings.setString("xpack.security.authc.realms.bar.ssl.truststore.secure_password", "changeit"); - builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE); + // configure realm to load config with certificate verification mode + builder.put("xpack.security.authc.realms." + REALM_NAME + ".ssl.truststore.path", truststore); + mockSecureSettings.setString("xpack.security.authc.realms." + REALM_NAME + ".ssl.truststore.secure_password", "changeit"); + builder.put("xpack.security.authc.realms." + REALM_NAME + ".ssl.verification_mode", VerificationMode.CERTIFICATE); } else { // fake realms so ssl will get loaded builder.put("xpack.security.authc.realms.foo.ssl.truststore.path", truststore); mockSecureSettings.setString("xpack.security.authc.realms.foo.ssl.truststore.secure_password", "changeit"); builder.put("xpack.security.authc.realms.foo.ssl.verification_mode", VerificationMode.FULL); - builder.put("xpack.security.authc.realms.bar.ssl.truststore.path", truststore); - mockSecureSettings.setString("xpack.security.authc.realms.bar.ssl.truststore.secure_password", "changeit"); - builder.put("xpack.security.authc.realms.bar.ssl.verification_mode", VerificationMode.CERTIFICATE); + builder.put("xpack.security.authc.realms." + REALM_NAME + ".ssl.truststore.path", truststore); + mockSecureSettings.setString("xpack.security.authc.realms." + REALM_NAME + ".ssl.truststore.secure_password", "changeit"); + builder.put("xpack.security.authc.realms." + REALM_NAME + ".ssl.verification_mode", VerificationMode.CERTIFICATE); } globalSettings = builder.setSecureSettings(mockSecureSettings).build(); Environment environment = TestEnvironment.newEnvironment(globalSettings); @@ -114,8 +115,8 @@ public void testConnect() throws Exception { String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, - LdapSearchScope.ONE_LEVEL), globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(Settings.EMPTY)); + LdapSearchScope.ONE_LEVEL), globalSettings, TestEnvironment.newEnvironment(globalSettings), + new ThreadContext(Settings.EMPTY)); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); String[] users = new String[] { "blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor" }; @@ -132,8 +133,8 @@ public void testGroupSearchScopeBase() throws Exception { String groupSearchBase = "cn=Avengers,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; - RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, - LdapSearchScope.BASE), globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(Settings.EMPTY)); + RealmConfig config = new RealmConfig(REALM_NAME, buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, + LdapSearchScope.BASE), globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(Settings.EMPTY)); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); String[] users = new String[] { "blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor" }; @@ -148,12 +149,12 @@ public void testCustomFilter() throws Exception { String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; Settings settings = Settings.builder() - .put(buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL)) - .put("group_search.filter", "(&(objectclass=posixGroup)(memberUid={0}))") - .put("group_search.user_attribute", "uid") - .build(); + .put(buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL)) + .put("group_search.filter", "(&(objectclass=posixGroup)(memberUid={0}))") + .put("group_search.user_attribute", "uid") + .build(); RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(Settings.EMPTY)); + new ThreadContext(Settings.EMPTY)); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); try (LdapSession ldap = session(sessionFactory, "selvig", PASSWORD_SECURE_STRING)) { @@ -166,17 +167,17 @@ public void testTcpTimeout() throws Exception { String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; Settings settings = Settings.builder() - .put(buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE)) - .put("group_search.filter", "(objectClass=*)") - .put("ssl.verification_mode", VerificationMode.CERTIFICATE) - .put(SessionFactorySettings.TIMEOUT_TCP_READ_SETTING, "1ms") //1 millisecond - .build(); + .put(buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE)) + .put("group_search.filter", "(objectClass=*)") + .put("ssl.verification_mode", VerificationMode.CERTIFICATE) + .put(SessionFactorySettings.TIMEOUT_TCP_READ_SETTING, "1ms") //1 millisecond + .build(); RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(Settings.EMPTY)); + new ThreadContext(Settings.EMPTY)); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); LDAPException expected = expectThrows(LDAPException.class, - () -> session(sessionFactory, "thor", PASSWORD_SECURE_STRING).groups(new PlainActionFuture<>())); + () -> session(sessionFactory, "thor", PASSWORD_SECURE_STRING).groups(new PlainActionFuture<>())); assertThat(expected.getMessage(), containsString("A client-side timeout was encountered while waiting")); } @@ -185,22 +186,22 @@ public void testStandardLdapConnectionHostnameVerificationFailure() throws Excep String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; Settings settings = Settings.builder() - // The certificate used in the vagrant box is valid for "localhost", but not for "127.0.0.1" - .put(buildLdapSettings(OPEN_LDAP_IP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL)) - .put("ssl.verification_mode", VerificationMode.FULL) - .build(); + // The certificate used in the vagrant box is valid for "localhost", but not for "127.0.0.1" + .put(buildLdapSettings(OPEN_LDAP_IP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL)) + .put("ssl.verification_mode", VerificationMode.FULL) + .build(); RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(Settings.EMPTY)); + new ThreadContext(Settings.EMPTY)); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); String user = "blackwidow"; UncategorizedExecutionException e = expectThrows(UncategorizedExecutionException.class, - () -> session(sessionFactory, user, PASSWORD_SECURE_STRING)); + () -> session(sessionFactory, user, PASSWORD_SECURE_STRING)); assertThat(e.getCause(), instanceOf(ExecutionException.class)); assertThat(e.getCause().getCause(), instanceOf(LDAPException.class)); assertThat(e.getCause().getCause().getMessage(), - anyOf(containsString("Hostname verification failed"), containsString("peer not authenticated"))); + anyOf(containsString("Hostname verification failed"), containsString("peer not authenticated"))); } public void testStandardLdapConnectionHostnameVerificationSuccess() throws Exception { @@ -208,13 +209,13 @@ public void testStandardLdapConnectionHostnameVerificationSuccess() throws Excep String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com"; Settings settings = Settings.builder() - // The certificate used in the vagrant box is valid for "localhost" (but not for "127.0.0.1") - .put(buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL)) - .put("ssl.verification_mode", VerificationMode.FULL) - .build(); + // The certificate used in the vagrant box is valid for "localhost" (but not for "127.0.0.1") + .put(buildLdapSettings(OPEN_LDAP_DNS_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL)) + .put("ssl.verification_mode", VerificationMode.FULL) + .build(); RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(Settings.EMPTY)); + new ThreadContext(Settings.EMPTY)); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); final String user = "blackwidow"; @@ -260,9 +261,9 @@ private Settings buildLdapSettings(String ldapUrl, String userTemplate, String g return builder.build(); } return builder - .put("ssl.truststore.path", getDataPath(LDAPTRUST_PATH)) - .put("ssl.truststore.password", "changeit") - .build(); + .put("ssl.truststore.path", getDataPath(LDAPTRUST_PATH)) + .put("ssl.truststore.password", "changeit") + .build(); } private LdapSession session(SessionFactory factory, String username, SecureString password) { diff --git a/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapUserSearchSessionFactoryTests.java b/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapUserSearchSessionFactoryTests.java index c008b5260f82b..1c9d93873a493 100644 --- a/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapUserSearchSessionFactoryTests.java +++ b/x-pack/qa/openldap-tests/src/test/java/org/elasticsearch/xpack/security/authc/ldap/OpenLdapUserSearchSessionFactoryTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.OpenLdapTests; +import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.security.authc.RealmConfig; @@ -39,6 +40,7 @@ import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; +@TestLogging("org.elasticsearch.xpack.core.ssl.SSLService:TRACE") public class OpenLdapUserSearchSessionFactoryTests extends ESTestCase { private Settings globalSettings; @@ -77,7 +79,8 @@ public void testUserSearchWithBindUserOpenLDAP() throws Exception { .put("user_search.base_dn", userSearchBase) .put("group_search.user_attribute", "uid") .put("bind_dn", "uid=blackwidow,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com") - .put("user_search.pool.enabled", randomBoolean()); + .put("user_search.pool.enabled", randomBoolean()) + .put("ssl.verification_mode", "full"); if (useSecureBindPassword) { final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("secure_bind_password", OpenLdapTests.PASSWORD); @@ -89,11 +92,11 @@ public void testUserSearchWithBindUserOpenLDAP() throws Exception { TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); Settings.Builder builder = Settings.builder() .put(globalSettings, false); - builder.put(Settings.builder().put(config.settings(), false).normalizePrefix("xpack.security.authc.realms.ldap.").build()); + builder.put(Settings.builder().put(config.settings(), false).normalizePrefix("xpack.security.authc.realms.oldap-test.").build()); final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.merge(globalSecureSettings); if (useSecureBindPassword) { - secureSettings.setString("xpack.security.authc.realms.ldap.secure_bind_password", OpenLdapTests.PASSWORD); + secureSettings.setString("xpack.security.authc.realms.oldap-test.secure_bind_password", OpenLdapTests.PASSWORD); } builder.setSecureSettings(secureSettings); Settings settings = builder.build();