From e5795f52f26b1679327bf5dd01816103ba61b3e7 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Fri, 20 Jul 2018 18:32:28 +1000 Subject: [PATCH] Backport SSL context names (#30953) to 6.x (#32223) Historically we have loaded SSL objects (such as SSLContext, SSLIOSessionStrategy) by passing in the SSL settings, constructing a new SSL configuration from those settings and then looking for a cached object that matches those settings. The primary issue with this approach is that it requires a fully configured Settings object to be available any time the SSL context needs to be loaded. If the Settings include SecureSettings (such as passwords for keys or keystores) then this is not true, and the cached SSL object cannot be loaded at runtime. This commit introduces an alternative approach of naming every cached ssl configuration, so that it is possible to load the SSL context for a named configuration (such as "xpack.http.ssl"). This means that the calling code does not need to have ongoing access to the secure settings that were used to load the configuration. This change also allows monitoring exporters to use SSL passwords from secure settings, however an exporter that uses a secure SSL setting (e.g. truststore.secure_password) may not have its SSL settings updated dynamically (this is prevented by a settings validator). Exporters without secure settings can continue to be defined and updated dynamically. Backport of: - c662565 - edbea73 - 6f2b7dc --- .../netty4/SecurityNetty4Transport.java | 38 ++- .../core/ssl/SSLConfigurationSettings.java | 8 + .../xpack/core/ssl/SSLService.java | 241 +++++++++--------- .../netty4/SecurityNetty4TransportTests.java | 43 ++++ .../ssl/SSLConfigurationReloaderTests.java | 10 +- .../xpack/core/ssl/SSLServiceTests.java | 171 +++++++++---- .../xpack/monitoring/exporter/Exporters.java | 1 + .../exporter/http/HttpExporter.java | 43 +++- .../exporter/http/HttpExporterSslIT.java | 188 ++++++++++++++ .../monitoring/exporter/http/testnode.crt | 23 ++ .../monitoring/exporter/http/testnode.jks | Bin 0 -> 9360 bytes .../monitoring/exporter/http/testnode.pem | 30 +++ .../security/PkiRealmBootstrapCheck.java | 45 ++-- .../xpack/security/Security.java | 14 +- .../esnative/ESNativeRealmMigrateTool.java | 5 +- .../esnative/tool/CommandLineHttpClient.java | 11 +- .../authc/ldap/support/SessionFactory.java | 21 +- .../xpack/security/authc/saml/SamlRealm.java | 8 +- .../SecurityServerTransportInterceptor.java | 40 ++- .../SecurityNetty4HttpServerTransport.java | 4 +- .../security/PkiRealmBootstrapCheckTests.java | 4 +- .../authc/ldap/ActiveDirectoryRealmTests.java | 52 ++-- .../security/authc/ldap/LdapRealmTests.java | 68 +++-- .../security/authc/ldap/LdapTestUtils.java | 12 +- .../ldap/support/SessionFactoryTests.java | 38 +-- .../SecurityNetty4ServerTransportTests.java | 5 +- .../transport/ssl/SslIntegrationTests.java | 4 +- .../xpack/ssl/SSLReloadIntegTests.java | 4 +- .../xpack/ssl/SSLTrustRestrictionsTests.java | 4 +- .../xpack/watcher/common/http/HttpClient.java | 7 +- .../org/elasticsearch/test/OpenLdapTests.java | 87 ++++--- ...OpenLdapUserSearchSessionFactoryTests.java | 9 +- .../ADLdapUserSearchSessionFactoryTests.java | 2 +- .../ActiveDirectorySessionFactoryTests.java | 50 ++-- 34 files changed, 868 insertions(+), 422 deletions(-) create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/transport/netty4/SecurityNetty4TransportTests.java create mode 100644 x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporterSslIT.java create mode 100644 x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.crt create mode 100644 x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.jks create mode 100644 x-pack/plugin/monitoring/src/test/resources/org/elasticsearch/xpack/monitoring/exporter/http/testnode.pem 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 a7ef1f0c02f4f..1773b9664bbd4 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 @@ -27,12 +27,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; @@ -57,22 +57,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(); @@ -80,6 +67,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(); @@ -208,8 +210,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 e2ca3516159a0..0dffca8a73aa4 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 0000000000000000000000000000000000000000..ebe6146124e8fd607e46a1a3129bdf9b4de0370d GIT binary patch literal 9360 zcmdUV2T)YowrzLQl93!F2SGC3WF$w)IfLYGK$2vT*iDigl`N7oh@eEt83BokWI;th zKok%V_zil_#dF{N=hmxN@83^VSMRxd)mp3enq$l{$L93&>1O}{0788*FTTk=VD28y zE;cYOYe##Svj^8*Hy60QBMbllY8=Nx4**vTxdk91003Gv1c?>_L4sT!qk(`R5Eh_) zU50G!4F3%T4;_m=TrvCr4+zA$0)Q|==^?b}XnJ5Y;(s+egq9o+ikg;}4=OCcDIVy2IuGcP39Gn>U% zK!zH|Y#zr76z$THNrVtl2Hz*duE|_x{SHKW=Wy9~#x0?_!WxzreKbBd_{B!%FFc+} z#hS1iA!Ph9s*KL}5ZCWMX)1FD)R@OQManE3e-@+qd0nOC5~I@Wvv)K719(Ez^^D!& zt6;h^oFHXs|H>4zGnJ%}f+`Pp>rEdE;=+o<-25$T9lIy1B0c;S!Z_r&_k6nwupM+P z`QIj_3i+10?X14(vmJKmiZElETC3=_VBSWbe19M>na%UTgjKu!)Hb0X5AJ>=1@Lw7vcxjN}1eMG&CU^70?>@)@Jj3w6do zK=}wbX`7exBW2Hy$yVaDo$ICp=}fYb@;nQCWlEe6F`UeJ?OsbuRMGQ zgd{VQ1`}qlEo37p4u-{8N|I&os?=jM_S_F93rh*dwGvc4>^v;F@<@1%AfI?JIxoOG#d{a^ zfIt8&OKT5%FPM$J8|t~~u}>Z;{sXMDD6qapfwk=?SP2aA8Y;D$$)co!mluiHCH4RE zP$7X(VhABRHdYc#Wb!Vj4e%Nnnzf4)lmZJVJR-@b8DwGjw9y}xw)Rb-X`tOZ@L?r7m{x$tD>pf-DK8J8PK&?Rve%UmMWfn zkY1J8$6e`6OS^j3!ZBdDpQ)-!J@JJ984?Dw%90 z9h9Kf+y~ zZ;X4CM_eV}GZ5(`=K*hqpZ8TuPP9ObD5GdI!|KXQ!DE#8?8q-!J#9& z<{0CP?#6ZZ#oFo@=oP4^3J28dh2~!eXSx~~6ipLu^f&9MPd~6p^1dZo&i#yysyksD zk}_18V>Qk~vQwzp^Jc<7;yAcH?9kbl>dg;vDb^QW7vRS}huY7Q{j*f_g#JtPV*wf` zQ+Jb8TG{^cI3dbVTnP3#FtLfzT-or(bOGc4(Y5{#j-5r{Ppfe;7RN>qhPm0{UaW%Vx1xCa5qm=3uM*tyDmx^f zYB_a@Gg~w4N2l?k$LGG^P!NIepM`u@h$CVHNvqI zC)X{Bme~#(ow!*Q{K^N&ikb6+rMH->KX;ZbN6hV7!(1Bu`qVw?;aP#3&Yrekzjpej zK?ev|N zXpSYGJX7Ur@l^_!fa=P>E&WMk1O|eS+U?%te$I}MSaKjCeWH%kki*#p`IR^ zVzx|rZ=W!gg0!F0H2VQ^^Fbof!}9le_))5Y=S5|6FO7Cv zbx_><@>*LeHI$ay>|Nv}{__#cG`gqE5HI=yy!zq2=%Sr>Y-uEp7wE@yxeIf4w6R19 z0pRVhFITw##6I{Y1PK;IRe#z)WcoxH4#%|zmFAbRe+4DeU7$D@vJi|;4ARs2BfumE z$x8ph0~o}3YA&A69w?nhuWt|Yh7v%o{$vY`Up(-~NEBoI#tcxx3+_iT2BrpTtEzI+ znim8i1QFsDfC}TOWK!IHlvq*$BEG}@fH_Cm@4K8Uq-IvD*m|7@F6Jq zuwGO-ZesCM!egNa*#)E@4QIrW8I23(sbgFL z-JqdF#j`8Vy}VmdE^%g2k0bzk-aO3BahcB5rNS zxl7R4(8SoexacwQK1c1P8)rsVPYAtP{l+gAnS>z5E){N%TZk`R&*4@b12lj~mK`oDh0NG!mg`Hy?PMdCk{$fc$gg@y}Zfu}F? zSu0-A5J<7i9~u3DF0|o^$U)%UsmE_ku>)^Gq%Eedjl|h!miFi4Sf0Lza4Yh$3`(a- zlSoW|GYZ|6+ksyVU2ZqAPbBTjepxQ8f5SX?^^wnbkC^~H#@eED&|5^5(CZW?&81lp zuK(Kl>+YUJ=bsA;AOPnN zL%!JXclJ}LX4jeGrIbUTc!v*~SOh1OiRa(L7uS+&TRqzL$=2m37@-?ugp z;<967zk5`#GF$mwu!!%bjbE#w?DiH;8#?Wzm57N|0FrE zn;pF<1Q#6>WkZaJqlDvMT!`PO>8EH>M_C>hj3f*ZfC@oG_ynO46eIBqKp_y`U*T^q z#J`%u2ttgGaj`B4_-g}bKnye)0C)>_&44m!Or)#1Ut8&;!v0wFl7XiLS&Hkpf6yQ+ zS$u^RL1(ei^U17X7FKiWIm_PUG%*qPkB-&%nj|U<+I{m8br3|&CFH3AtedxB3{(XP zEn%Zcf>g8WD}%`mB@~WM zp9uG(2Oy&6>W}bkjBJFW3kT}F6e9aa)BSXsD4RZ=aNVx{s&pychT8+Y$$l_!H^FaL z3LLd5q?EMk(g;f9FP8|X)yaCx6I!~n)nmu%`$=|;KQj#O^GGWI)jJJs z7>Ac9vNah6&K&AwA1hzS{iHejdVYVmZF*^BM|8K!2)nP`sY>CxeFi~sC&l{Y_0{O- zR3w-dQMDW`5;v=7NA6>dI8VL&`0&n)2=-n!aIaAxvy^GPc>jC8J0k!|&vN;X?e2o? zwky$-nw!ods}&g=K8Qr_j^(ku%x(XbPEm^7#X#04E>m|5a#SCvn|mjcA-(GfSo!6& zwo4GA-&byR`#aIaH8CYDzQA5wsTt$tD+m|9?pO*JTVtH)G_p-^rk-c&&hGLBJ0dr& z2HYnandk_~8S)LW z{)anZ&dF##-IbO!HlOrrA1oi$i|ZKRW2P#NW12=pa%4>IZtA*obM+ zUr`Tno4i}q5^Kd+o7JSL=5rZPlc46PloNW-aFxv_(_h*;*fV`daXfEiSnv3Vr)ub+ zGvw0>%{HX26_R#m)1Q}ov&mmtWdu>LrUjHJ2jK4u9jC@di^dq=M4hG)N(@7*Ibn94fDkgeZ1{bnK4+j=-Bc-b2kbl zB2~`9MlJg3nM9CMwMj#DMNZkY^(!}d=9l~B)2~-t=jY-H@fnybqV&{T6)llgafjWi zre8Oej@qni0pGukP;yElQPyUbR0vj64{6xhQ&{gaRu9*}FbDPDrKEY?oF2X`^?(_4 zmb)GR`9|N;k_xAaOKOcB-X5VI4lS)VpXq&hy5>;c=;P=Toe{dSmZd>q!5ROshhh@8 zezz8bnn9YgLPmvb&zHYGpx{ZvsYY(jgBa_`6ML+^wSF{M8|!4r?N-PPovp{FEv z=Pcib1<|(w1v)7kIBo9?+~*w?elzPllgNPEn;f9$IlZoM!$#4pBvCIflR?r-u)c6+ z(<;2~Ya?~aow*x1$ftJceu3cv-rdSCcqt5`!-iUS;QcI~^>3#oN1~~|xtRM0-|ar2 z-}HJmdu!h-8}h(C+}L*|A=$sKNQ*=nCXxLHm$0Of+U)vQ=e8;`-id27tf^>6nRV(o zD_35LMQCiXvc;2P5C$3ZExN#sI331)j7wM4?A}WR5y0vQHB4^!9Cb#LW2-5h8Wg_W zE?(7>&&`t(tsSfKU2gO8^6pwlf#)k(@H#E`*xxz}(ZGA`$b|V!H-7Eo_a;mxUQoaG z@iUWo&dy;${A)aMi66W&;E|)TL`@ITKe|!zaenPBdhYvMi9C1z&?Zn0VDle+KtrRW z&1xm~aLZ+Za#F!ldcDg52k7y8`+5HkKBT>PLT)EHQ+wS0?)^4H3rz7L`xRQ?0#5WuV4tJA^+R{Ul!N!Swi%8vAG}bX`+Jl z{oGbt`~Cx^#3hmbhxOmzd(GpA;JlOb?eHYV$_$QhVoRotB5vEjX~hRt_QbNfZtp`z zCa^jdlJCrmZdH&KP`Nvizsp}P9;bei0LINMRZ>umh z@wu?rUddcG(Rvf=W_VWZm(bV2C!R@~vHk=`B}z~dn3j-Xlqa^He3+oVCSlzhnr_T4 z7xI#}+RkP5Md<>pSR;^knyvj{LXvJ+Xj7FairmPhUH(WR%z8ECi!=5X-}D$9J9kN- zC$E~1Wqy*4yp5$n!!{%z@uufhdn+l@)27$nh~I1VmcvD^9)+Fcjc{mRqB-6n{4VXay+HKSqK6{!h@8K($I}H)GZL zd+vn7OSEuCTdiM0|B8QpC@%zZ-qoWKv;9Igy)4Yl!yayLZRr7{*L87mq?h*eaIu9s z!`x7JZ0LU(_QDWeC`vr@i$J00%{&wp^8X4Ugiw6cLKm~5yIVS0S@NUg`v3iXT_puI zWzN=ski(8IGp7|TpM;&i+%EXYl{BUj)_2s7CuR6q-a0@K^u0%!{YEWpYsrF1XERyZ zf$tMSTink}o&$ffH!`}Es;V{6M4j$g-H?#|zQ}jW+UO?yG=&aPZ`u&)cNLS#c$E%; zP1mx?uy^0lDm~1Sxl4AS3}fIGtsrpPzu9UsUFfLaHBjFbJbOC8(^<{SnS0n!StB#g zL}+<5vF5jPm0~0Vjt%Q?>}z(+kvmw*edbY` z@SqjXB|6rfvcCz`u_)Y;_+8ajHX`pJD&kNHczrrb!=?w{fiV124cV4iCdMz&zJwqK zE{Xo+XoqLy?Bk=64cI=0e(aTjm0Xe#m-AMZ32X;}2a~{K(-z>-Z{9+k8X@v&$i>E+ zQXRR%1%i z+gZhDOYfLI95J{tK}^7qxH4qdT=iwIXI;%~<(7x#tDq?=+?_Bwkrz$Q`TdVlR))UZ zB@j7mCy|Sc;&$|B7PbW~9eY=&!EdvD*IjQ)sB0(+cInMrs1{CN7*c3cOPQ|Rp{(Mt zOjz!_eLXl-Mnq|AsoQfh;NJug*Q+l)T z8)*rHeYv|M#Ut?%U?vsg>xas%hWwvYC#0-Sur)F2=FtkgqgCA#-e%z?+T?CPv=igi zT1bbmLe@F14M@f`Vd)CEd&TVRZ9lERix`Pv&Rj93@;7`u^|14k-pNBn{fTijp&Qtp z1B1q7E%*$Cjn_4DC1sJoqaH8jlQik)F@7bhGU9K_XgO4mt1xyV1wx@8p6R>!Z=zo} zJP|z{6Si_^2=pvzAdCS%KC?wD74V_1_=k2eaRNll~ zg_)0a(KkYGbiWo&D8{^z3der9T|L|venrzv_U)qcQ^$MFo;M|wFp-P!qEO(#sfdZ1oL-r?nXuCiHy2Qq0 ziM;!sUs2F`&Az?Mh;}V|OcB@zA_b&cXvUK=;k^R`-C{MPC+tX)V!8h%5RdT?@P(Uf7yaQA1qCnw{nH+*r>*BMBG9Ld(hllZ5Ey>&8FWz*Q!dPYoj zCzFk0KZGQUrz|3zXT|9#%3tEMsddw5y@{5+yMt>KbeorB%SpCOL#uM0uGFADE&g#g#^|1;Itr9?;L0YErGBwTfC_I{Rc$*iOkJ|A4=@L50Z x`UE0joFCFX(i+qD)1h^DcPJOtN>)kRbQgMUO*p!K@AKymmi%I+{Ip*({}0#og{%Mo literal 0 HcmV?d00001 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 4868c7c78a5d9..0121b1ba69ccf 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 @@ -122,6 +122,7 @@ 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; @@ -233,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 { @@ -295,7 +296,7 @@ public Security(Settings settings, final Path configPath) { checks.addAll(Arrays.asList( new TokenPassphraseBootstrapCheck(settings), new TokenSSLBootstrapCheck(), - new PkiRealmBootstrapCheck(settings, getSslService()), + new PkiRealmBootstrapCheck(getSslService()), new TLSLicenseBootstrapCheck(), new PasswordHashingAlgorithmBootstrapCheck())); checks.addAll(InternalRealms.getBootstrapChecks(settings, env)); @@ -940,10 +941,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 5b4543ccaf275..4684853278346 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/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..8bdfd02d2fcc5 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("xpack.ssl"); } 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/ssl/SslIntegrationTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java index 1be64d3f4de89..48abeb3e43e39 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 @@ -29,6 +29,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; @@ -117,8 +118,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 b19c750412847..c0b21e6576f78 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; @@ -94,7 +95,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 0620495211ec6..18d7780629144 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 c6e10130db7c2..88aa71c09462c 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,19 +92,25 @@ 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); + + // If not using global ssl, need to set the truststore for the "full verification" realm + builder.put("xpack.security.authc.realms.vmode_full.ssl.truststore.path", truststore); + mockSecureSettings.setString("xpack.security.authc.realms.vmode_full.ssl.truststore.secure_password", "changeit"); } + builder.put("xpack.security.authc.realms.vmode_full.ssl.verification_mode", VerificationMode.FULL); + globalSettings = builder.setSecureSettings(mockSecureSettings).build(); Environment environment = TestEnvironment.newEnvironment(globalSettings); sslService = new SSLService(globalSettings, environment); @@ -114,8 +121,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 +139,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 +155,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 +173,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 +192,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)) + .build(); - RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(Settings.EMPTY)); + // Pick up the "full" verification mode config + RealmConfig config = new RealmConfig("vmode_full", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), + 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 +215,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)) + .build(); - RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(Settings.EMPTY)); + // Pick up the "full" verification mode config + RealmConfig config = new RealmConfig("vmode_full", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), + new ThreadContext(Settings.EMPTY)); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); final String user = "blackwidow"; @@ -260,9 +267,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(); diff --git a/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ADLdapUserSearchSessionFactoryTests.java b/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ADLdapUserSearchSessionFactoryTests.java index 98594917129f2..c3777a9bee9aa 100644 --- a/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ADLdapUserSearchSessionFactoryTests.java +++ b/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ADLdapUserSearchSessionFactoryTests.java @@ -86,7 +86,7 @@ public void testUserSearchWithActiveDirectory() throws Exception { Settings.Builder builder = Settings.builder() .put(globalSettings); settings.keySet().forEach(k -> { - builder.copy("xpack.security.authc.realms.ldap." + k, k, settings); + builder.copy("xpack.security.authc.realms.ad-as-ldap-test." + k, k, settings); }); Settings fullSettings = builder.build(); diff --git a/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java b/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java index a11647844e79b..a7a1d20e30c68 100644 --- a/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java +++ b/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.concurrent.UncategorizedExecutionException; +import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -59,9 +60,7 @@ public boolean enableWarningsCheck() { @SuppressWarnings("unchecked") public void testAdAuth() throws Exception { - RealmConfig config = new RealmConfig("ad-test", - buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false), - globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(Settings.EMPTY)); + RealmConfig config = configureRealm("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false)); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { String userName = "ironman"; @@ -82,11 +81,21 @@ public void testAdAuth() throws Exception { } } + private RealmConfig configureRealm(String name, Settings settings) { + final Environment env = TestEnvironment.newEnvironment(globalSettings); + final Settings mergedSettings = Settings.builder() + .put(settings) + .normalizePrefix("xpack.security.authc.realms." + name + ".") + .put(globalSettings) + .build(); + this.sslService = new SSLService(mergedSettings, env); + return new RealmConfig(name, settings, globalSettings, env, new ThreadContext(globalSettings)); + } + @SuppressWarnings("unchecked") public void testNetbiosAuth() throws Exception { final String adUrl = randomFrom(AD_LDAP_URL, AD_LDAP_GC_URL); - RealmConfig config = new RealmConfig("ad-test", buildAdSettings(adUrl, AD_DOMAIN, false), globalSettings, - TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", buildAdSettings(adUrl, AD_DOMAIN, false)); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { String userName = "ades\\ironman"; @@ -108,8 +117,7 @@ public void testNetbiosAuth() throws Exception { } public void testAdAuthAvengers() throws Exception { - RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false), globalSettings, - TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false)); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { String[] users = new String[]{"cap", "hawkeye", "hulk", "ironman", "thor", "blackwidow"}; @@ -126,8 +134,7 @@ public void testAdAuthAvengers() throws Exception { public void testAuthenticate() throws Exception { Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com", LdapSearchScope.ONE_LEVEL, false); - RealmConfig config = new RealmConfig("ad-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", settings); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { String userName = "hulk"; @@ -151,8 +158,7 @@ public void testAuthenticate() throws Exception { public void testAuthenticateBaseUserSearch() throws Exception { Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Bruce Banner, CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com", LdapSearchScope.BASE, false); - RealmConfig config = new RealmConfig("ad-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", settings); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { String userName = "hulk"; @@ -180,8 +186,7 @@ public void testAuthenticateBaseGroupSearch() throws Exception { "CN=Avengers,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com") .put(ActiveDirectorySessionFactorySettings.AD_GROUP_SEARCH_SCOPE_SETTING, LdapSearchScope.BASE) .build(); - RealmConfig config = new RealmConfig("ad-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", settings); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { String userName = "hulk"; @@ -198,8 +203,7 @@ public void testAuthenticateBaseGroupSearch() throws Exception { public void testAuthenticateWithUserPrincipalName() throws Exception { Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com", LdapSearchScope.ONE_LEVEL, false); - RealmConfig config = new RealmConfig("ad-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", settings); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { //Login with the UserPrincipalName @@ -220,8 +224,7 @@ public void testAuthenticateWithUserPrincipalName() throws Exception { public void testAuthenticateWithSAMAccountName() throws Exception { Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com", LdapSearchScope.ONE_LEVEL, false); - RealmConfig config = new RealmConfig("ad-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", settings); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { //login with sAMAccountName @@ -247,8 +250,7 @@ public void testCustomUserFilter() throws Exception { .put(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_FILTER_SETTING, "(&(objectclass=user)(userPrincipalName={0}@ad.test.elasticsearch.com))") .build(); - RealmConfig config = new RealmConfig("ad-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-test", settings); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { //Login with the UserPrincipalName @@ -285,8 +287,7 @@ public void testStandardLdapConnection() throws Exception { .put("ssl.truststore.password", "changeit") .build(); } - RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-as-ldap-test", settings); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); String user = "Bruce Banner"; @@ -351,8 +352,7 @@ public void testStandardLdapWithAttributeGroups() throws Exception { .put("ssl.truststore.password", "changeit") .build(); } - RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings), - new ThreadContext(globalSettings)); + RealmConfig config = configureRealm("ad-as-ldap-test", settings); LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool); String user = "Bruce Banner"; @@ -369,9 +369,7 @@ public void testStandardLdapWithAttributeGroups() throws Exception { } public void testADLookup() throws Exception { - RealmConfig config = new RealmConfig("ad-test", - buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false, true), - globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(Settings.EMPTY)); + RealmConfig config = configureRealm("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false, true)); try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) { List users = randomSubsetOf(Arrays.asList("cap", "hawkeye", "hulk", "ironman", "thor", "blackwidow",