Define explicit settings for security realms/ssl (elastic/elasticsearch#4311)

Modified the definition and loading of settings in Security to provide early detection and failure of invalid (unrecognised or syntactically invalid) settings.
Also consolidates the number of places where settings were defined.

Each realm now defines its supported settings.
This is facilitated for custom realms via a new "getRealmSettings" method on XPackExtension.
The realm group setting performs validation of the child settings with reference to the "type".
For backwards compatibility reasons, realm types that have no configuration defined, will be accepted during validation, but may fail at realm creation time.

All SSL settings have been centralised into SSLConfigurationSettings, which supports a variable "prefix" to accommodate the multiple places we define SSL config.

HTTP Proxy settings are explicitly defined rather than being a generic group.

Where possible all security settings now reference a Setting object, and there are less magic strings scattered in the code.

Closes: elastic/elasticsearch#3965

Original commit: elastic/x-pack-elasticsearch@2c76a137a9
This commit is contained in:
Tim Vernum 2016-12-13 16:14:02 +11:00 committed by GitHub
parent 85679fcf19
commit 7192c46307
38 changed files with 1340 additions and 427 deletions

View File

@ -52,6 +52,7 @@ import org.elasticsearch.xpack.action.XPackInfoAction;
import org.elasticsearch.xpack.action.XPackUsageAction;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.http.HttpSettings;
import org.elasticsearch.xpack.common.http.auth.HttpAuthFactory;
import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry;
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth;
@ -316,7 +317,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
@Override
public List<Setting<?>> getSettings() {
ArrayList<Setting<?>> settings = new ArrayList<>();
settings.addAll(Security.getSettings(transportClientMode));
settings.addAll(Security.getSettings(transportClientMode, extensionsService));
settings.addAll(MonitoringSettings.getSettings());
settings.addAll(watcher.getSettings());
settings.addAll(licensing.getSettings());
@ -335,10 +336,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
settings.add(ReportingAttachmentParser.INTERVAL_SETTING);
// http settings
settings.add(Setting.simpleString("xpack.http.default_read_timeout", Setting.Property.NodeScope));
settings.add(Setting.simpleString("xpack.http.default_connection_timeout", Setting.Property.NodeScope));
settings.add(Setting.groupSetting("xpack.http.ssl.", Setting.Property.NodeScope));
settings.add(Setting.groupSetting("xpack.http.proxy.", Setting.Property.NodeScope));
settings.addAll(HttpSettings.getSettings());
return settings;
}

View File

@ -5,24 +5,19 @@
*/
package org.elasticsearch.xpack;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.ssl.SSLClientAuth;
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.ssl.VerificationMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.ssl.SSLClientAuth;
import org.elasticsearch.xpack.ssl.VerificationMode;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import static java.util.Collections.emptyList;
/**
* A container for xpack setting constants.
*/
@ -74,170 +69,23 @@ public class XPackSettings {
public static final VerificationMode VERIFICATION_MODE_DEFAULT = VerificationMode.FULL;
// global settings that apply to everything!
private static final Setting<List<String>> CIPHERS_SETTING = Setting.listSetting("xpack.ssl.cipher_suites", DEFAULT_CIPHERS,
Function.identity(), Property.NodeScope, Property.Filtered);
private static final Setting<List<String>> SUPPORTED_PROTOCOLS_SETTING = Setting.listSetting("xpack.ssl.supported_protocols",
DEFAULT_SUPPORTED_PROTOCOLS, Function.identity(), Property.NodeScope, Property.Filtered);
private static final Setting<SSLClientAuth> CLIENT_AUTH_SETTING = new Setting<>("xpack.ssl.client_authentication",
CLIENT_AUTH_DEFAULT.name(), SSLClientAuth::parse, Property.NodeScope, Property.Filtered);
private static final Setting<VerificationMode> VERIFICATION_MODE_SETTING = new Setting<>("xpack.ssl.verification_mode",
VERIFICATION_MODE_DEFAULT.name(), VerificationMode::parse, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> KEYSTORE_PATH_SETTING = new Setting<>("xpack.ssl.keystore.path",
s -> System.getProperty("javax.net.ssl.keyStore"), Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> KEYSTORE_PASSWORD_SETTING = new Setting<>("xpack.ssl.keystore.password",
s -> System.getProperty("javax.net.ssl.keyStorePassword"), Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<String> KEYSTORE_ALGORITHM_SETTING = new Setting<>("xpack.ssl.keystore.algorithm",
s -> System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()),
Function.identity(), Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> KEYSTORE_KEY_PASSWORD_SETTING =
new Setting<>("xpack.ssl.keystore.key_password", KEYSTORE_PASSWORD_SETTING, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRUSTSTORE_PATH_SETTING = new Setting<>("xpack.ssl.truststore.path",
s -> System.getProperty("javax.net.ssl.trustStore"), Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRUSTSTORE_PASSWORD_SETTING = new Setting<>("xpack.ssl.truststore.password",
s -> System.getProperty("javax.net.ssl.trustStorePassword"), Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<String> TRUSTSTORE_ALGORITHM_SETTING = new Setting<>("xpack.ssl.truststore.algorithm",
s -> System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()),
Function.identity(), Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> KEY_PATH_SETTING =
new Setting<>("xpack.ssl.key", (String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> KEY_PASSWORD_SETTING =
new Setting<>("xpack.ssl.key_passphrase", (String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> CERT_SETTING =
new Setting<>("xpack.ssl.certificate", (String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<List<String>> CA_PATHS_SETTING = Setting.listSetting("xpack.ssl.certificate_authorities",
Collections.emptyList(), s -> s, Property.NodeScope, Property.Filtered);
public static final String GLOBAL_SSL_PREFIX = "xpack.ssl.";
private static final SSLConfigurationSettings GLOBAL_SSL = SSLConfigurationSettings.withPrefix(GLOBAL_SSL_PREFIX);
// http specific settings
private static final Setting<List<String>> HTTP_CIPHERS_SETTING = Setting.listSetting("xpack.security.http.ssl.cipher_suites",
DEFAULT_CIPHERS, Function.identity(), Property.NodeScope, Property.Filtered);
private static final Setting<List<String>> HTTP_SUPPORTED_PROTOCOLS_SETTING =
Setting.listSetting("xpack.security.http.ssl.supported_protocols", emptyList(), Function.identity(),
Property.NodeScope, Property.Filtered);
private static final Setting<SSLClientAuth> HTTP_CLIENT_AUTH_SETTING = new Setting<>("xpack.security.http.ssl.client_authentication",
CLIENT_AUTH_DEFAULT.name(), SSLClientAuth::parse, Property.NodeScope, Property.Filtered);
private static final Setting<VerificationMode> HTTP_VERIFICATION_MODE_SETTING =
new Setting<>("xpack.security.http.ssl.verification_mode", VERIFICATION_MODE_DEFAULT.name(), VerificationMode::parse,
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_KEYSTORE_PATH_SETTING = new Setting<>("xpack.security.http.ssl.keystore.path",
(String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_KEYSTORE_PASSWORD_SETTING =
new Setting<>("xpack.security.http.ssl.keystore.password", (String) null, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<String> HTTP_KEYSTORE_ALGORITHM_SETTING = new Setting<>("xpack.security.http.ssl.keystore.algorithm",
"", Function.identity(), Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_KEYSTORE_KEY_PASSWORD_SETTING =
new Setting<>("xpack.security.http.ssl.keystore.key_password", HTTP_KEYSTORE_PASSWORD_SETTING, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_TRUSTSTORE_PATH_SETTING = new Setting<>("xpack.security.http.ssl.truststore.path",
(String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_TRUSTSTORE_PASSWORD_SETTING =
new Setting<>("xpack.security.http.ssl.truststore.password", (String) null, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<String> HTTP_TRUSTSTORE_ALGORITHM_SETTING = new Setting<>("xpack.security.http.ssl.truststore.algorithm",
"", Function.identity(), Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_KEY_PATH_SETTING =
new Setting<>("xpack.security.http.ssl.key", (String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_KEY_PASSWORD_SETTING = new Setting<>("xpack.security.http.ssl.key_passphrase",
(String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> HTTP_CERT_SETTING = new Setting<>("xpack.security.http.ssl.certificate",
(String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<List<String>> HTTP_CA_PATHS_SETTING =
Setting.listSetting("xpack.security.http.ssl.certificate_authorities", emptyList(), s -> s,
Property.NodeScope, Property.Filtered);
public static final String HTTP_SSL_PREFIX = Security.setting("http.ssl.");
private static final SSLConfigurationSettings HTTP_SSL = SSLConfigurationSettings.withPrefix(HTTP_SSL_PREFIX);
// transport specific settings
private static final Setting<List<String>> TRANSPORT_CIPHERS_SETTING =
Setting.listSetting("xpack.security.transport.ssl.cipher_suites", DEFAULT_CIPHERS, Function.identity(),
Property.NodeScope, Property.Filtered);
private static final Setting<List<String>> TRANSPORT_SUPPORTED_PROTOCOLS_SETTING =
Setting.listSetting("xpack.security.transport.ssl.supported_protocols", emptyList(), Function.identity(),
Property.NodeScope, Property.Filtered);
private static final Setting<SSLClientAuth> TRANSPORT_CLIENT_AUTH_SETTING =
new Setting<>("xpack.security.transport.ssl.client_authentication", CLIENT_AUTH_DEFAULT.name(), SSLClientAuth::parse,
Property.NodeScope, Property.Filtered);
private static final Setting<VerificationMode> TRANSPORT_VERIFICATION_MODE_SETTING =
new Setting<>("xpack.security.transport.ssl.verification_mode", VERIFICATION_MODE_DEFAULT.name(), VerificationMode::parse,
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_KEYSTORE_PATH_SETTING =
new Setting<>("xpack.security.transport.ssl.keystore.path", (String) null, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_KEYSTORE_PASSWORD_SETTING =
new Setting<>("xpack.security.transport.ssl.keystore.password", (String) null, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<String> TRANSPORT_KEYSTORE_ALGORITHM_SETTING =
new Setting<>("xpack.security.transport.ssl.keystore.algorithm", "", Function.identity(), Property.NodeScope,
Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_KEYSTORE_KEY_PASSWORD_SETTING =
new Setting<>("xpack.security.transport.ssl.keystore.key_password", TRANSPORT_KEYSTORE_PASSWORD_SETTING, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_TRUSTSTORE_PATH_SETTING =
new Setting<>("xpack.security.transport.ssl.truststore.path", (String) null, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_TRUSTSTORE_PASSWORD_SETTING =
new Setting<>("xpack.security.transport.ssl.truststore.password", (String) null, Optional::ofNullable,
Property.NodeScope, Property.Filtered);
private static final Setting<String> TRANSPORT_TRUSTSTORE_ALGORITHM_SETTING =
new Setting<>("xpack.security.transport.ssl.truststore.algorithm", "", Function.identity(),
Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_KEY_PATH_SETTING =
new Setting<>("xpack.security.transport.ssl.key", (String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_KEY_PASSWORD_SETTING =
new Setting<>("xpack.security.transport.ssl.key_passphrase", (String) null, Optional::ofNullable, Property.NodeScope,
Property.Filtered);
private static final Setting<Optional<String>> TRANSPORT_CERT_SETTING = new Setting<>("xpack.security.transport.ssl.certificate",
(String) null, Optional::ofNullable, Property.NodeScope, Property.Filtered);
private static final Setting<List<String>> TRANSPORT_CA_PATHS_SETTING =
Setting.listSetting("xpack.security.transport.ssl.certificate_authorities", emptyList(), s -> s,
Property.NodeScope, Property.Filtered);
public static final String TRANSPORT_SSL_PREFIX = Security.setting("transport.ssl.");
private static final SSLConfigurationSettings TRANSPORT_SSL = SSLConfigurationSettings.withPrefix(TRANSPORT_SSL_PREFIX);
/* End SSL settings */
static {
ALL_SETTINGS.add(CIPHERS_SETTING);
ALL_SETTINGS.add(SUPPORTED_PROTOCOLS_SETTING);
ALL_SETTINGS.add(KEYSTORE_PATH_SETTING);
ALL_SETTINGS.add(KEYSTORE_PASSWORD_SETTING);
ALL_SETTINGS.add(KEYSTORE_ALGORITHM_SETTING);
ALL_SETTINGS.add(KEYSTORE_KEY_PASSWORD_SETTING);
ALL_SETTINGS.add(KEY_PATH_SETTING);
ALL_SETTINGS.add(KEY_PASSWORD_SETTING);
ALL_SETTINGS.add(CERT_SETTING);
ALL_SETTINGS.add(TRUSTSTORE_PATH_SETTING);
ALL_SETTINGS.add(TRUSTSTORE_PASSWORD_SETTING);
ALL_SETTINGS.add(TRUSTSTORE_ALGORITHM_SETTING);
ALL_SETTINGS.add(CA_PATHS_SETTING);
ALL_SETTINGS.add(VERIFICATION_MODE_SETTING);
ALL_SETTINGS.add(CLIENT_AUTH_SETTING);
ALL_SETTINGS.add(HTTP_CIPHERS_SETTING);
ALL_SETTINGS.add(HTTP_SUPPORTED_PROTOCOLS_SETTING);
ALL_SETTINGS.add(HTTP_KEYSTORE_PATH_SETTING);
ALL_SETTINGS.add(HTTP_KEYSTORE_PASSWORD_SETTING);
ALL_SETTINGS.add(HTTP_KEYSTORE_ALGORITHM_SETTING);
ALL_SETTINGS.add(HTTP_KEYSTORE_KEY_PASSWORD_SETTING);
ALL_SETTINGS.add(HTTP_KEY_PATH_SETTING);
ALL_SETTINGS.add(HTTP_KEY_PASSWORD_SETTING);
ALL_SETTINGS.add(HTTP_CERT_SETTING);
ALL_SETTINGS.add(HTTP_TRUSTSTORE_PATH_SETTING);
ALL_SETTINGS.add(HTTP_TRUSTSTORE_PASSWORD_SETTING);
ALL_SETTINGS.add(HTTP_TRUSTSTORE_ALGORITHM_SETTING);
ALL_SETTINGS.add(HTTP_CA_PATHS_SETTING);
ALL_SETTINGS.add(HTTP_VERIFICATION_MODE_SETTING);
ALL_SETTINGS.add(HTTP_CLIENT_AUTH_SETTING);
ALL_SETTINGS.add(TRANSPORT_CIPHERS_SETTING);
ALL_SETTINGS.add(TRANSPORT_SUPPORTED_PROTOCOLS_SETTING);
ALL_SETTINGS.add(TRANSPORT_KEYSTORE_PATH_SETTING);
ALL_SETTINGS.add(TRANSPORT_KEYSTORE_PASSWORD_SETTING);
ALL_SETTINGS.add(TRANSPORT_KEYSTORE_ALGORITHM_SETTING);
ALL_SETTINGS.add(TRANSPORT_KEYSTORE_KEY_PASSWORD_SETTING);
ALL_SETTINGS.add(TRANSPORT_KEY_PATH_SETTING);
ALL_SETTINGS.add(TRANSPORT_KEY_PASSWORD_SETTING);
ALL_SETTINGS.add(TRANSPORT_CERT_SETTING);
ALL_SETTINGS.add(TRANSPORT_TRUSTSTORE_PATH_SETTING);
ALL_SETTINGS.add(TRANSPORT_TRUSTSTORE_PASSWORD_SETTING);
ALL_SETTINGS.add(TRANSPORT_TRUSTSTORE_ALGORITHM_SETTING);
ALL_SETTINGS.add(TRANSPORT_CA_PATHS_SETTING);
ALL_SETTINGS.add(TRANSPORT_VERIFICATION_MODE_SETTING);
ALL_SETTINGS.add(TRANSPORT_CLIENT_AUTH_SETTING);
ALL_SETTINGS.addAll(GLOBAL_SSL.getAllSettings());
ALL_SETTINGS.addAll(HTTP_SSL.getAllSettings());
ALL_SETTINGS.addAll(TRANSPORT_SSL.getAllSettings());
}
/**

View File

@ -39,12 +39,6 @@ import java.util.Map;
*/
public class HttpClient extends AbstractComponent {
static final String SETTINGS_SSL_PREFIX = "xpack.http.ssl.";
static final String SETTINGS_PROXY_PREFIX = "xpack.http.proxy.";
static final String SETTINGS_PROXY_HOST = SETTINGS_PROXY_PREFIX + "host";
static final String SETTINGS_PROXY_PORT = SETTINGS_PROXY_PREFIX + "port";
private final HttpAuthRegistry httpAuthRegistry;
private final TimeValue defaultConnectionTimeout;
private final TimeValue defaultReadTimeout;
@ -55,21 +49,27 @@ public class HttpClient extends AbstractComponent {
public HttpClient(Settings settings, HttpAuthRegistry httpAuthRegistry, SSLService sslService) {
super(settings);
this.httpAuthRegistry = httpAuthRegistry;
this.defaultConnectionTimeout = settings.getAsTime("xpack.http.default_connection_timeout", TimeValue.timeValueSeconds(10));
this.defaultReadTimeout = settings.getAsTime("xpack.http.default_read_timeout", TimeValue.timeValueSeconds(10));
Integer proxyPort = settings.getAsInt(SETTINGS_PROXY_PORT, null);
String proxyHost = settings.get(SETTINGS_PROXY_HOST, null);
this.defaultConnectionTimeout = HttpSettings.CONNECTION_TIMEOUT.get(settings);
this.defaultReadTimeout = HttpSettings.READ_TIMEOUT.get(settings);
final Integer proxyPort;
if (HttpSettings.PROXY_HOST.exists(settings)) {
proxyPort = HttpSettings.PROXY_PORT.get(settings);
} else {
proxyPort = null;
}
final String proxyHost = HttpSettings.PROXY_HOST.get(settings);
if (proxyPort != null && Strings.hasText(proxyHost)) {
this.proxy = new HttpProxy(proxyHost, proxyPort);
logger.info("Using default proxy for http input and slack/hipchat/pagerduty/webhook actions [{}:{}]", proxyHost, proxyPort);
} else if (proxyPort == null && Strings.hasText(proxyHost) == false) {
this.proxy = HttpProxy.NO_PROXY;
} else {
throw new IllegalArgumentException("HTTP Proxy requires both settings: [" + SETTINGS_PROXY_HOST + "] and [" +
SETTINGS_PROXY_PORT + "]");
throw new IllegalArgumentException("HTTP Proxy requires both settings: [" + HttpSettings.PROXY_HOST_KEY + "] and [" +
HttpSettings.PROXY_PORT_KEY + "]");
}
Settings sslSettings = settings.getByPrefix(SETTINGS_SSL_PREFIX);
this.sslSocketFactory = sslService.sslSocketFactory(settings.getByPrefix(SETTINGS_SSL_PREFIX));
Settings sslSettings = settings.getByPrefix(HttpSettings.SSL_KEY_PREFIX);
this.sslSocketFactory = sslService.sslSocketFactory(settings.getByPrefix(HttpSettings.SSL_KEY_PREFIX));
this.isHostnameVerificationEnabled = sslService.getVerificationMode(sslSettings, Settings.EMPTY).isHostnameVerificationEnabled();
}

View File

@ -0,0 +1,49 @@
/*
* 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.common.http;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
import java.util.ArrayList;
import java.util.List;
/**
* Handles the configuration and parsing of settings for the <code>xpack.http.</code> prefix
*/
public class HttpSettings {
private static final TimeValue DEFAULT_READ_TIMEOUT = TimeValue.timeValueSeconds(10);
private static final TimeValue DEFAULT_CONNECTION_TIMEOUT = DEFAULT_READ_TIMEOUT;
static final Setting<TimeValue> READ_TIMEOUT = Setting.timeSetting("xpack.http.default_read_timeout",
DEFAULT_READ_TIMEOUT, Setting.Property.NodeScope);
static final Setting<TimeValue> CONNECTION_TIMEOUT = Setting.timeSetting("xpack.http.default_connection_timeout",
DEFAULT_CONNECTION_TIMEOUT, Setting.Property.NodeScope);
static final String PROXY_HOST_KEY = "xpack.http.proxy.host";
static final String PROXY_PORT_KEY = "xpack.http.proxy.port";
static final String SSL_KEY_PREFIX = "xpack.http.ssl.";
static final Setting<String> PROXY_HOST = Setting.simpleString(PROXY_HOST_KEY, Setting.Property.NodeScope);
static final Setting<Integer> PROXY_PORT = Setting.intSetting(PROXY_PORT_KEY, 0, 0, 0xFFFF, Setting.Property.NodeScope);
private static final SSLConfigurationSettings SSL = SSLConfigurationSettings.withPrefix(SSL_KEY_PREFIX);
public static List<? extends Setting<?>> getSettings() {
final ArrayList<Setting<?>> settings = new ArrayList<>();
settings.addAll(SSL.getAllSettings());
settings.add(READ_TIMEOUT);
settings.add(CONNECTION_TIMEOUT);
settings.add(PROXY_HOST);
settings.add(PROXY_PORT);
return settings;
}
private HttpSettings() {
}
}

View File

@ -9,10 +9,13 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.RealmConfig;
/**
@ -49,6 +52,16 @@ public abstract class XPackExtension {
return Collections.emptyMap();
}
/**
* Returns the set of {@link Setting settings} that may be configured for the each type of realm.
*
* Each <em>setting key</em> must be unqualified and is in the same format as will be provided via {@link RealmConfig#settings()}.
* If a given realm-type is not present in the returned map, then it will be treated as if it supported <em>all</em> possible settings.
*
* The life-cycle of an extension dictates that this method will be called before {@link #getRealms(ResourceWatcherService)}
*/
public Map<String, Set<Setting<?>>> getRealmSettings() { return Collections.emptyMap(); }
/**
* Returns a handler for authentication failures, or null to use the default handler.
*

View File

@ -13,6 +13,7 @@ import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.inject.util.Providers;
@ -47,6 +48,7 @@ import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.extensions.XPackExtension;
import org.elasticsearch.xpack.extensions.XPackExtensionsService;
import org.elasticsearch.xpack.security.action.SecurityActionModule;
import org.elasticsearch.xpack.security.action.filter.SecurityActionFilter;
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheAction;
@ -79,15 +81,14 @@ import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.InternalRealms;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.RealmSettings;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
@ -246,13 +247,7 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
final AnonymousUser anonymousUser = new AnonymousUser(settings);
final ReservedRealm reservedRealm = new ReservedRealm(env, settings, nativeUsersStore, anonymousUser);
Map<String, Realm.Factory> realmFactories = new HashMap<>();
realmFactories.put(FileRealm.TYPE, config -> new FileRealm(config, resourceWatcherService));
realmFactories.put(NativeRealm.TYPE, config -> new NativeRealm(config, nativeUsersStore));
realmFactories.put(LdapRealm.AD_TYPE,
config -> new LdapRealm(LdapRealm.AD_TYPE, config, resourceWatcherService, sslService, threadPool));
realmFactories.put(LdapRealm.LDAP_TYPE,
config -> new LdapRealm(LdapRealm.LDAP_TYPE, config, resourceWatcherService, sslService, threadPool));
realmFactories.put(PkiRealm.TYPE, config -> new PkiRealm(config, resourceWatcherService, sslService));
realmFactories.putAll(InternalRealms.getFactories(threadPool, resourceWatcherService, sslService, nativeUsersStore));
for (XPackExtension extension : extensions) {
Map<String, Realm.Factory> newRealms = extension.getRealms(resourceWatcherService);
for (Map.Entry<String, Realm.Factory> entry : newRealms.entrySet()) {
@ -380,7 +375,10 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
return settingsBuilder.build();
}
public static List<Setting<?>> getSettings(boolean transportClientMode) {
/**
* Get the {@link Setting setting configuration} for all security components, including those defined in extensions.
*/
public static List<Setting<?>> getSettings(boolean transportClientMode, @Nullable XPackExtensionsService extensionsService) {
List<Setting<?>> settingsList = new ArrayList<>();
// always register for both client and node modes
settingsList.add(USER_SETTING);
@ -401,7 +399,7 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
// authentication settings
AnonymousUser.addSettings(settingsList);
Realms.addSettings(settingsList);
RealmSettings.addSettings(settingsList, extensionsService == null ? null : extensionsService.getExtensions());
NativeRolesStore.addSettings(settingsList);
AuthenticationService.addSettings(settingsList);
AuthorizationService.addSettings(settingsList);

View File

@ -0,0 +1,91 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authc;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.ssl.SSLService;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Provides a single entry point into dealing with all standard XPack security {@link Realm realms}.
* This class does not handle extensions.
* @see Realms for the component that manages configured realms (including custom extension realms)
*/
public class InternalRealms {
/**
* The list of all <em>internal</em> realm types, excluding {@link ReservedRealm#TYPE}.
*/
private static final Set<String> TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
NativeRealm.TYPE, FileRealm.TYPE, LdapRealm.AD_TYPE, LdapRealm.LDAP_TYPE, PkiRealm.TYPE
)));
/**
* Determines whether <code>type</code> is an internal realm-type, optionally considering
* the {@link ReservedRealm}.
*/
public static boolean isInternalRealm(String type, boolean includeReservedRealm) {
if (TYPES.contains(type)) {
return true;
}
if (includeReservedRealm && ReservedRealm.TYPE.equals(type)) {
return true;
}
return false;
}
/**
* Creates {@link Realm.Factory factories} for each <em>internal</em> realm type.
* This excludes the {@link ReservedRealm}, as it cannot be created dynamically.
* @return A map from <em>realm-type</em> to <code>Factory</code>
*/
public static Map<String, Realm.Factory> getFactories(ThreadPool threadPool, ResourceWatcherService resourceWatcherService,
SSLService sslService, NativeUsersStore nativeUsersStore){
Map<String, Realm.Factory> map = new HashMap<>();
map.put(FileRealm.TYPE, config -> new FileRealm(config, resourceWatcherService));
map.put(NativeRealm.TYPE, config -> new NativeRealm(config, nativeUsersStore));
map.put(LdapRealm.AD_TYPE,
config -> new LdapRealm(LdapRealm.AD_TYPE, config, resourceWatcherService, sslService, threadPool));
map.put(LdapRealm.LDAP_TYPE,
config -> new LdapRealm(LdapRealm.LDAP_TYPE, config, resourceWatcherService, sslService, threadPool));
map.put(PkiRealm.TYPE, config -> new PkiRealm(config, resourceWatcherService, sslService));
return Collections.unmodifiableMap(map);
}
/**
* Provides the {@link Setting setting configuration} for each <em>internal</em> realm type.
* This excludes the {@link ReservedRealm}, as it cannot be configured dynamically.
* @return A map from <em>realm-type</em> to a collection of <code>Setting</code> objects.
*/
public static Map<String,Set<Setting<?>>> getSettings() {
Map<String, Set<Setting<?>>> map = new HashMap<>();
map.put(FileRealm.TYPE, FileRealm.getSettings());
map.put(NativeRealm.TYPE, NativeRealm.getSettings());
map.put(LdapRealm.AD_TYPE, LdapRealm.getSettings(LdapRealm.AD_TYPE));
map.put(LdapRealm.LDAP_TYPE, LdapRealm.getSettings(LdapRealm.LDAP_TYPE));
map.put(PkiRealm.TYPE, PkiRealm.getSettings());
return Collections.unmodifiableMap(map);
}
private InternalRealms() {
}
}

View File

@ -29,8 +29,8 @@ public class RealmConfig {
this.settings = settings;
this.globalSettings = globalSettings;
this.env = env;
enabled = settings.getAsBoolean("enabled", true);
order = settings.getAsInt("order", Integer.MAX_VALUE);
enabled = RealmSettings.ENABLED_SETTING.get(settings);
order = RealmSettings.ORDER_SETTING.get(settings);
}
public String name() {

View File

@ -0,0 +1,154 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authc;
import org.elasticsearch.common.settings.AbstractScopedSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.extensions.XPackExtension;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import static org.elasticsearch.common.Strings.isNullOrEmpty;
import static org.elasticsearch.xpack.security.Security.setting;
/**
* Configures the {@link Setting#groupSetting(String, Consumer, Setting.Property...) group setting} for security
* {@link Realm realms}, with validation according to the realm type.
* <p>
* The allowable settings for a given realm are dependent on the {@link Realm#type() realm type}, so it is not possible
* to simply provide a list of {@link Setting} objects and rely on the global setting validation (e.g. A custom realm-type might
* define a setting with the same logical key as an internal realm-type, but a different data type).
* </p> <p>
* Instead, realm configuration relies on the <code>validator</code> parameter to
* {@link Setting#groupSetting(String, Consumer, Setting.Property...)} in order to validate each realm in a way that respects the
* declared <code>type</code>.
* Internally, this validation delegates to {@link AbstractScopedSettings#validate(Settings)} so that validation is reasonably aligned
* with the way we validate settings globally.
* </p>
* <p>
* The allowable settings for each realm-type are determined by calls to {@link InternalRealms#getSettings()} and
* {@link XPackExtension#getRealmSettings()}
*/
public class RealmSettings {
public static final String PREFIX = setting("authc.realms.");
static final Setting<String> TYPE_SETTING = Setting.simpleString("type", Setting.Property.NodeScope);
static final Setting<Boolean> ENABLED_SETTING = Setting.boolSetting("enabled", true, Setting.Property.NodeScope);
static final Setting<Integer> ORDER_SETTING = Setting.intSetting("order", Integer.MAX_VALUE, Setting.Property.NodeScope);
/**
* Add the {@link Setting} configuration for <em>all</em> realms to the provided list.
*/
public static void addSettings(List<Setting<?>> settingsList, List<XPackExtension> extensions) {
settingsList.add(getGroupSetting(extensions));
}
/**
* Extract the child {@link Settings} for the {@link #PREFIX realms prefix}.
* The top level names in the returned <code>Settings</code> will be the names of the configured realms.
*/
public static Settings get(Settings settings) {
return settings.getByPrefix(RealmSettings.PREFIX);
}
/**
* Convert the child {@link Setting} for the provided realm into a fully scoped key for use in an error message.
* @see #PREFIX
*/
public static String getFullSettingKey(RealmConfig realm, Setting<?> setting) {
return getFullSettingKey(realm.name(), setting);
}
/**
* @see #getFullSettingKey(RealmConfig, Setting)
*/
public static String getFullSettingKey(RealmConfig realm, String subKey) {
return getFullSettingKey(realm.name(), subKey);
}
private static String getFullSettingKey(String name, Setting<?> setting) {
return getFullSettingKey(name, setting.getKey());
}
private static String getFullSettingKey(String name, String subKey) {
return PREFIX + name + "." + subKey;
}
private static Setting<Settings> getGroupSetting(List<XPackExtension> extensions) {
return Setting.groupSetting(PREFIX, getSettingsValidator(extensions), Setting.Property.NodeScope);
}
private static Consumer<Settings> getSettingsValidator(List<XPackExtension> extensions) {
final Map<String, Set<Setting<?>>> childSettings = new HashMap<>(InternalRealms.getSettings());
if (extensions != null) {
extensions.forEach(ext -> {
final Map<String, Set<Setting<?>>> extSettings = ext.getRealmSettings();
extSettings.keySet().stream().filter(childSettings::containsKey).forEach(type -> {
throw new IllegalArgumentException("duplicate realm type " + type);
});
childSettings.putAll(extSettings);
});
}
childSettings.forEach(RealmSettings::verify);
return validator(childSettings);
}
private static void verify(String type, Set<Setting<?>> settings) {
Set<String> keys = new HashSet<>();
settings.forEach(setting -> {
final String key = setting.getKey();
if (keys.contains(key)) {
throw new IllegalArgumentException("duplicate setting for key " + key + " in realm type " + type);
}
keys.add(key);
if (setting.getProperties().contains(Setting.Property.NodeScope) == false) {
throw new IllegalArgumentException("setting " + key + " in realm type " + type + " does not have NodeScope");
}
});
}
private static Consumer<Settings> validator(Map<String, Set<Setting<?>>> validSettings) {
return (settings) -> settings.names().forEach(n -> validateRealm(n, settings.getAsSettings(n), validSettings));
}
private static void validateRealm(String name, Settings settings, Map<String, Set<Setting<?>>> validSettings) {
final String type = TYPE_SETTING.get(settings);
if (isNullOrEmpty(type)) {
throw new IllegalArgumentException("missing realm type [" + getFullSettingKey(name, TYPE_SETTING) + "] for realm");
}
validateRealm(name, type, settings, validSettings.get(type));
}
private static void validateRealm(String name, String type, Settings settings, Set<Setting<?>> validSettings) {
if (validSettings == null) {
// For backwards compatibility, we assume that is we don't know the valid settings for a realm.type then everything
// is valid. Ideally we would reject these, but XPackExtension doesn't enforce that realm-factories and realm-settings are
// perfectly aligned
return;
}
Set<Setting<?>> settingSet = new HashSet<>(validSettings);
settingSet.add(TYPE_SETTING);
settingSet.add(ENABLED_SETTING);
settingSet.add(ORDER_SETTING);
final AbstractScopedSettings validator = new AbstractScopedSettings(settings, settingSet, Setting.Property.NodeScope) { };
try {
validator.validate(settings);
} catch (RuntimeException e) {
throw new IllegalArgumentException("incorrect configuration for realm [" + getFullSettingKey(name, "")
+ "] of type " + type, e);
}
}
private RealmSettings() {
}
}

View File

@ -18,8 +18,6 @@ import java.util.Set;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.XPackLicenseState;
@ -27,21 +25,13 @@ import org.elasticsearch.license.XPackLicenseState.AllowedRealmType;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import static org.elasticsearch.xpack.security.Security.setting;
/**
* Serves as a realms registry (also responsible for ordering the realms appropriately)
*/
public class Realms extends AbstractComponent implements Iterable<Realm> {
static final List<String> INTERNAL_REALM_TYPES =
Arrays.asList(ReservedRealm.TYPE, NativeRealm.TYPE, FileRealm.TYPE, LdapRealm.AD_TYPE, LdapRealm.LDAP_TYPE, PkiRealm.TYPE);
public static final Setting<Settings> REALMS_GROUPS_SETTINGS = Setting.groupSetting(setting("authc.realms."), Property.NodeScope);
private final Environment env;
private final Map<String, Realm.Factory> factories;
private final XPackLicenseState licenseState;
@ -68,7 +58,7 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
List<Realm> nativeRealms = new ArrayList<>();
for (Realm realm : realms) {
// don't add the reserved realm here otherwise we end up with only this realm...
if (INTERNAL_REALM_TYPES.contains(realm.type()) && ReservedRealm.TYPE.equals(realm.type()) == false) {
if (InternalRealms.isInternalRealm(realm.type(), false)) {
internalRealms.add(realm);
}
@ -142,7 +132,7 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
}
protected List<Realm> initRealms() throws Exception {
Settings realmsSettings = REALMS_GROUPS_SETTINGS.get(settings);
Settings realmsSettings = RealmSettings.get(settings);
Set<String> internalTypes = new HashSet<>();
List<Realm> realms = new ArrayList<>();
for (String name : realmsSettings.names()) {
@ -239,10 +229,6 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
}
}
public static void addSettings(List<Setting<?>> settingsModule) {
settingsModule.add(REALMS_GROUPS_SETTINGS);
}
private static void combineMaps(Map<String, Object> mapA, Map<String, Object> mapB) {
for (Entry<String, Object> entry : mapB.entrySet()) {
mapA.compute(entry.getKey(), (key, value) -> {
@ -274,9 +260,10 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
case NATIVE:
return FileRealm.TYPE.equals(type) || NativeRealm.TYPE.equals(type);
case DEFAULT:
return INTERNAL_REALM_TYPES.contains(type);
return InternalRealms.isInternalRealm(type, true);
default:
throw new IllegalStateException("unknown enabled realm type [" + enabledRealmType + "]");
}
}
}

View File

@ -6,11 +6,15 @@
package org.elasticsearch.xpack.security.authc.esnative;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.user.User;
import java.util.HashSet;
import java.util.Set;
/**
* User/password realm that is backed by an Elasticsearch index
*/
@ -34,4 +38,11 @@ public class NativeRealm extends CachingUsernamePasswordRealm {
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener) {
userStore.verifyPassword(token.principal(), token.credentials(), listener);
}
/**
* @return The {@link Setting setting configuration} for this realm type
*/
public static Set<Setting<?>> getSettings() {
return CachingUsernamePasswordRealm.getCachingSettings();
}
}

View File

@ -5,9 +5,12 @@
*/
package org.elasticsearch.xpack.security.authc.file;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
@ -61,4 +64,11 @@ public class FileRealm extends CachingUsernamePasswordRealm {
stats.put("size", userPasswdStore.usersCount());
return stats;
}
/**
* @return The {@link Setting setting configuration} for this realm type
*/
public static Set<Setting<?>> getSettings() {
return CachingUsernamePasswordRealm.getCachingSettings();
}
}

View File

@ -16,6 +16,7 @@ import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.RealmConfig;
@ -26,8 +27,10 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.ssl.SSLService;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
@ -107,6 +110,18 @@ class ActiveDirectorySessionFactory extends SessionFactory {
return "DC=" + domain.replace(".", ",DC=");
}
public static Set<Setting<?>> getSettings() {
Set<Setting<?>> settings = new HashSet<>();
settings.addAll(SessionFactory.getSettings());
settings.add(Setting.simpleString(AD_DOMAIN_NAME_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(AD_GROUP_SEARCH_BASEDN_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(AD_GROUP_SEARCH_SCOPE_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(AD_USER_SEARCH_BASEDN_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(AD_USER_SEARCH_FILTER_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(AD_USER_SEARCH_SCOPE_SETTING, Setting.Property.NodeScope));
return settings;
}
ADAuthenticator getADAuthenticator(String username) {
if (username.indexOf('\\') > 0) {
return downLevelADAuthenticator;

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.security.authc.ldap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
@ -13,12 +14,13 @@ import com.unboundid.ldap.sdk.LDAPException;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapLoadBalancing;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.RealmSettings;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
@ -59,13 +61,27 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
} else {
assert LDAP_TYPE.equals(type) : "type [" + type + "] is unknown. expected one of [" + AD_TYPE + ", " + LDAP_TYPE + "]";
Settings searchSettings = userSearchSettings(config);
if (searchSettings.names().isEmpty()) {
sessionFactory = new LdapSessionFactory(config, sslService);
} else if (config.settings().getAsArray(LdapSessionFactory.USER_DN_TEMPLATES_SETTING).length > 0) {
throw new IllegalArgumentException("settings were found for both user search and user template modes of operation. " +
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
final boolean hasSearchSettings = LdapUserSearchSessionFactory.hasUserSearchSettings(config);
final boolean hasTemplates = LdapSessionFactory.USER_DN_TEMPLATES_SETTING.exists(config.settings());
if (hasSearchSettings == false) {
if(hasTemplates == false) {
throw new IllegalArgumentException("settings were not found for either user search [" +
RealmSettings.getFullSettingKey(config, LdapUserSearchSessionFactory.SEARCH_PREFIX) +
"] or user template [" +
RealmSettings.getFullSettingKey(config, LdapSessionFactory.USER_DN_TEMPLATES_SETTING) +
"] modes of operation. " +
"Please provide the settings for the mode you wish to use. For more details refer to the ldap " +
"authentication section of the X-Pack guide.");
}
sessionFactory = new LdapSessionFactory(config, sslService);
} else if (hasTemplates) {
throw new IllegalArgumentException("settings were found for both user search [" +
RealmSettings.getFullSettingKey(config, LdapUserSearchSessionFactory.SEARCH_PREFIX) +
"] and user template [" +
RealmSettings.getFullSettingKey(config, LdapSessionFactory.USER_DN_TEMPLATES_SETTING) +
"] modes of operation. " +
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
"authentication section of the X-Pack guide.");
} else {
sessionFactory = new LdapUserSearchSessionFactory(config, sslService);
}
@ -73,8 +89,22 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
return sessionFactory;
}
static Settings userSearchSettings(RealmConfig config) {
return config.settings().getAsSettings("user_search");
/**
* @return The {@link Setting setting configuration} for this realm type
* @param type Either {@link #AD_TYPE} or {@link #LDAP_TYPE}
*/
public static Set<Setting<?>> getSettings(String type) {
Set<Setting<?>> settings = new HashSet<>();
settings.addAll(CachingUsernamePasswordRealm.getCachingSettings());
DnRoleMapper.getSettings(settings);
if (AD_TYPE.equals(type)) {
settings.addAll(ActiveDirectorySessionFactory.getSettings());
} else {
assert LDAP_TYPE.equals(type) : "type [" + type + "] is unknown. expected one of [" + AD_TYPE + ", " + LDAP_TYPE + "]";
settings.addAll(LdapSessionFactory.getSettings());
settings.addAll(LdapUserSearchSessionFactory.getSettings());
}
return settings;
}
/**
@ -106,7 +136,7 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
Map<String, Object> usage = super.usageStats();
usage.put("load_balance_type", LdapLoadBalancing.resolve(config.settings()).toString());
usage.put("ssl", sessionFactory.isSslUsed());
usage.put("user_search", userSearchSettings(config).isEmpty() == false);
usage.put("user_search", LdapUserSearchSessionFactory.hasUserSearchSettings(config));
return usage;
}

View File

@ -12,8 +12,11 @@ import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.RealmSettings;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
@ -23,7 +26,12 @@ import org.elasticsearch.xpack.ssl.SSLService;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.Function;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.escapedRDNValue;
@ -35,7 +43,8 @@ import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.esca
*/
public class LdapSessionFactory extends SessionFactory {
public static final String USER_DN_TEMPLATES_SETTING = "user_dn_templates";
public static final Setting<List<String>> USER_DN_TEMPLATES_SETTING = Setting.listSetting("user_dn_templates",
Collections.emptyList(), Function.identity(), Setting.Property.NodeScope);
private final String[] userDnTemplates;
private final GroupsResolver groupResolver;
@ -43,9 +52,10 @@ public class LdapSessionFactory extends SessionFactory {
public LdapSessionFactory(RealmConfig config, SSLService sslService) {
super(config, sslService);
Settings settings = config.settings();
userDnTemplates = settings.getAsArray(USER_DN_TEMPLATES_SETTING);
if (userDnTemplates == null) {
throw new IllegalArgumentException("missing required LDAP setting [" + USER_DN_TEMPLATES_SETTING + "]");
userDnTemplates = USER_DN_TEMPLATES_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
if (userDnTemplates.length == 0) {
throw new IllegalArgumentException("missing required LDAP setting ["
+ RealmSettings.getFullSettingKey(config, USER_DN_TEMPLATES_SETTING) + "]");
}
groupResolver = groupResolver(settings);
}
@ -116,10 +126,16 @@ public class LdapSessionFactory extends SessionFactory {
}
static GroupsResolver groupResolver(Settings settings) {
Settings searchSettings = settings.getAsSettings("group_search");
if (!searchSettings.names().isEmpty()) {
return new SearchGroupsResolver(searchSettings);
if (SearchGroupsResolver.BASE_DN.exists(settings)) {
return new SearchGroupsResolver(settings);
}
return new UserAttributeGroupsResolver(settings);
}
public static Set<Setting<?>> getSettings() {
Set<Setting<?>> settings = new HashSet<>();
settings.addAll(SessionFactory.getSettings());
settings.add(USER_DN_TEMPLATES_SETTING);
return settings;
}
}

View File

@ -17,9 +17,11 @@ import com.unboundid.ldap.sdk.SimpleBindRequest;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.RealmSettings;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
@ -30,6 +32,11 @@ import org.elasticsearch.xpack.ssl.SSLService;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import static com.unboundid.ldap.sdk.Filter.createEqualityFilter;
import static com.unboundid.ldap.sdk.Filter.encodeValue;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
@ -42,6 +49,30 @@ class LdapUserSearchSessionFactory extends SessionFactory {
static final String DEFAULT_USERNAME_ATTRIBUTE = "uid";
static final TimeValue DEFAULT_HEALTH_CHECK_INTERVAL = TimeValue.timeValueSeconds(60L);
static final String SEARCH_PREFIX = "user_search.";
private static final Setting<String> SEARCH_BASE_DN = Setting.simpleString("user_search.base_dn", Setting.Property.NodeScope);
private static final Setting<String> SEARCH_ATTRIBUTE = new Setting<>("user_search.attribute", DEFAULT_USERNAME_ATTRIBUTE,
Function.identity(), Setting.Property.NodeScope);
private static final Setting<LdapSearchScope> SEARCH_SCOPE = new Setting<>("user_search.scope", (String) null,
s -> LdapSearchScope.resolve(s, LdapSearchScope.SUB_TREE), Setting.Property.NodeScope);
private static final Setting<Boolean> POOL_ENABLED = Setting.boolSetting("user_search.pool.enabled",
true, Setting.Property.NodeScope);
private static final Setting<Integer> POOL_INITIAL_SIZE = Setting.intSetting("user_search.pool.initial_size",
DEFAULT_CONNECTION_POOL_INITIAL_SIZE, 0, Setting.Property.NodeScope);
private static final Setting<Integer> POOL_SIZE = Setting.intSetting("user_search.pool.size",
DEFAULT_CONNECTION_POOL_SIZE, 1, Setting.Property.NodeScope);
private static final Setting<TimeValue> HEALTH_CHECK_INTERVAL = Setting.timeSetting("user_search.pool.health_check.interval",
DEFAULT_HEALTH_CHECK_INTERVAL, Setting.Property.NodeScope);
private static final Setting<Boolean> HEALTH_CHECK_ENABLED = Setting.boolSetting("user_search.pool.health_check.enabled",
true, Setting.Property.NodeScope);
private static final Setting<Optional<String>> HEALTH_CHECK_DN = new Setting<>("user_search.pool.health_check.dn", (String) null,
Optional::ofNullable, Setting.Property.NodeScope);
private static final Setting<String> BIND_DN = Setting.simpleString("bind_dn", Setting.Property.NodeScope);
private static final Setting<String> BIND_PASSWORD = Setting.simpleString("bind_password", Setting.Property.NodeScope);
private final String userSearchBaseDn;
private final LdapSearchScope scope;
private final String userAttribute;
@ -53,14 +84,15 @@ class LdapUserSearchSessionFactory extends SessionFactory {
LdapUserSearchSessionFactory(RealmConfig config, SSLService sslService) throws LDAPException {
super(config, sslService);
Settings settings = config.settings();
userSearchBaseDn = settings.get("user_search.base_dn");
if (userSearchBaseDn == null) {
throw new IllegalArgumentException("user_search base_dn must be specified");
if (SEARCH_BASE_DN.exists(settings)) {
userSearchBaseDn = SEARCH_BASE_DN.get(settings);
} else {
throw new IllegalArgumentException("[" + RealmSettings.getFullSettingKey(config, SEARCH_BASE_DN) + "] must be specified");
}
scope = LdapSearchScope.resolve(settings.get("user_search.scope"), LdapSearchScope.SUB_TREE);
userAttribute = settings.get("user_search.attribute", DEFAULT_USERNAME_ATTRIBUTE);
groupResolver = groupResolver(config.settings());
useConnectionPool = settings.getAsBoolean("user_search.pool.enabled", true);
scope = SEARCH_SCOPE.get(settings);
userAttribute = SEARCH_ATTRIBUTE.get(settings);
groupResolver = groupResolver(settings);
useConnectionPool = POOL_ENABLED.get(settings);
if (useConnectionPool) {
connectionPool = createConnectionPool(config, serverSet, timeout, logger);
} else {
@ -72,17 +104,16 @@ class LdapUserSearchSessionFactory extends SessionFactory {
throws LDAPException {
Settings settings = config.settings();
SimpleBindRequest bindRequest = bindRequest(settings);
final int initialSize = settings.getAsInt("user_search.pool.initial_size", DEFAULT_CONNECTION_POOL_INITIAL_SIZE);
final int size = settings.getAsInt("user_search.pool.size", DEFAULT_CONNECTION_POOL_SIZE);
final int initialSize = POOL_INITIAL_SIZE.get(settings);
final int size = POOL_SIZE.get(settings);
LDAPConnectionPool pool = null;
boolean success = false;
try {
pool = new LDAPConnectionPool(serverSet, bindRequest, initialSize, size);
pool.setRetryFailedOperationsDueToInvalidConnections(true);
if (settings.getAsBoolean("user_search.pool.health_check.enabled", true)) {
String entryDn = settings.get("user_search.pool.health_check.dn", (bindRequest == null) ? null : bindRequest.getBindDN());
final long healthCheckInterval =
settings.getAsTime("user_search.pool.health_check.interval", DEFAULT_HEALTH_CHECK_INTERVAL).millis();
if (HEALTH_CHECK_ENABLED.get(settings)) {
String entryDn = HEALTH_CHECK_DN.get(settings).orElseGet(() -> bindRequest == null ? null : bindRequest.getBindDN());
final long healthCheckInterval = HEALTH_CHECK_INTERVAL.get(settings).millis();
if (entryDn != null) {
// Checks the status of the LDAP connection at a specified interval in the background. We do not check on
// on create as the LDAP server may require authentication to get an entry and a bind request has not been executed
@ -93,7 +124,8 @@ class LdapUserSearchSessionFactory extends SessionFactory {
pool.setHealthCheck(healthCheck);
pool.setHealthCheckIntervalMillis(healthCheckInterval);
} else {
logger.warn("[bind_dn] and [user_search.pool.health_check.dn] have not been specified so no " +
logger.warn("[" + RealmSettings.getFullSettingKey(config, BIND_DN) + "] and [" +
RealmSettings.getFullSettingKey(config, HEALTH_CHECK_DN) + "] have not been specified so no " +
"ldap query will be run as a health check");
}
}
@ -109,13 +141,16 @@ class LdapUserSearchSessionFactory extends SessionFactory {
static SimpleBindRequest bindRequest(Settings settings) {
SimpleBindRequest request = null;
String bindDn = settings.get("bind_dn");
if (bindDn != null) {
request = new SimpleBindRequest(bindDn, settings.get("bind_password"));
if (BIND_DN.exists(settings)) {
request = new SimpleBindRequest(BIND_DN.get(settings), BIND_PASSWORD.get(settings));
}
return request;
}
public static boolean hasUserSearchSettings(RealmConfig config) {
return config.settings().getByPrefix("user_search.").isEmpty() == false;
}
@Override
public void session(String user, SecuredString password, ActionListener<LdapSession> listener) {
if (useConnectionPool) {
@ -268,10 +303,30 @@ class LdapUserSearchSessionFactory extends SessionFactory {
}
static GroupsResolver groupResolver(Settings settings) {
Settings searchSettings = settings.getAsSettings("group_search");
if (!searchSettings.names().isEmpty()) {
return new SearchGroupsResolver(searchSettings);
if (SearchGroupsResolver.BASE_DN.exists(settings)) {
return new SearchGroupsResolver(settings);
}
return new UserAttributeGroupsResolver(settings);
}
public static Set<Setting<?>> getSettings() {
Set<Setting<?>> settings = new HashSet<>();
settings.addAll(SessionFactory.getSettings());
settings.add(SEARCH_BASE_DN);
settings.add(SEARCH_SCOPE);
settings.add(SEARCH_ATTRIBUTE);
settings.add(POOL_ENABLED);
settings.add(POOL_INITIAL_SIZE);
settings.add(POOL_SIZE);
settings.add(HEALTH_CHECK_ENABLED);
settings.add(HEALTH_CHECK_DN);
settings.add(HEALTH_CHECK_INTERVAL);
settings.add(BIND_DN);
settings.add(BIND_PASSWORD);
settings.addAll(SearchGroupsResolver.getSettings());
settings.addAll(UserAttributeGroupsResolver.getSettings());
return settings;
}
}

View File

@ -13,6 +13,8 @@ import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchScope;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
@ -20,9 +22,13 @@ import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsRes
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Set;
import java.util.function.Function;
import static org.elasticsearch.common.Strings.isNullOrEmpty;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.OBJECT_CLASS_PRESENCE_FILTER;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.createFilter;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.search;
@ -38,19 +44,28 @@ class SearchGroupsResolver implements GroupsResolver {
"(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames)(objectclass=group)(objectclass=posixGroup))" +
"(|(uniqueMember={0})(member={0})(memberUid={0})))";
static final Setting<String> BASE_DN = Setting.simpleString("group_search.base_dn", Setting.Property.NodeScope);
static final Setting<LdapSearchScope> SCOPE = new Setting<>("group_search.scope", (String) null,
s -> LdapSearchScope.resolve(s, LdapSearchScope.SUB_TREE), Setting.Property.NodeScope);
static final Setting<String> USER_ATTRIBUTE = Setting.simpleString("group_search.user_attribute", Setting.Property.NodeScope);
static final Setting<String> FILTER = new Setting<>("group_search.filter", GROUP_SEARCH_DEFAULT_FILTER,
Function.identity(), Setting.Property.NodeScope);
private final String baseDn;
private final String filter;
private final String userAttribute;
private final LdapSearchScope scope;
SearchGroupsResolver(Settings settings) {
baseDn = settings.get("base_dn");
if (baseDn == null) {
if (BASE_DN.exists(settings)) {
baseDn = BASE_DN.get(settings);
} else {
throw new IllegalArgumentException("base_dn must be specified");
}
filter = settings.get("filter", GROUP_SEARCH_DEFAULT_FILTER);
userAttribute = settings.get("user_attribute");
scope = LdapSearchScope.resolve(settings.get("scope"), LdapSearchScope.SUB_TREE);
filter = FILTER.get(settings);
userAttribute = USER_ATTRIBUTE.get(settings);
scope = SCOPE.get(settings);
}
@Override
@ -75,7 +90,7 @@ class SearchGroupsResolver implements GroupsResolver {
}
public String[] attributes() {
if (userAttribute != null) {
if (Strings.hasLength(userAttribute)) {
return new String[] { userAttribute };
}
return null;
@ -83,7 +98,7 @@ class SearchGroupsResolver implements GroupsResolver {
private void getUserId(String dn, Collection<Attribute> attributes, LDAPInterface connection, TimeValue timeout,
ActionListener<String> listener) {
if (userAttribute == null) {
if (isNullOrEmpty(userAttribute)) {
listener.onResponse(dn);
} else if (attributes != null) {
final String value = attributes.stream().filter((attribute) -> attribute.getName().equals(userAttribute))
@ -107,4 +122,13 @@ class SearchGroupsResolver implements GroupsResolver {
}, listener::onFailure),
userAttribute);
}
public static Set<Setting<?>> getSettings() {
Set<Setting<?>> settings = new HashSet<>();
settings.add(BASE_DN);
settings.add(FILTER);
settings.add(USER_ATTRIBUTE);
settings.add(SCOPE);
return settings;
}
}

View File

@ -10,6 +10,7 @@ import com.unboundid.ldap.sdk.LDAPInterface;
import com.unboundid.ldap.sdk.SearchScope;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
@ -20,6 +21,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.Set;
import java.util.function.Function;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.OBJECT_CLASS_PRESENCE_FILTER;
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.searchForEntry;
@ -29,10 +32,12 @@ import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.sear
*/
class UserAttributeGroupsResolver implements GroupsResolver {
private static final Setting<String> ATTRIBUTE = new Setting<>("user_group_attribute", "memberOf",
Function.identity(), Setting.Property.NodeScope);
private final String attribute;
UserAttributeGroupsResolver(Settings settings) {
this(settings.get("user_group_attribute", "memberOf"));
this(ATTRIBUTE.get(settings));
}
private UserAttributeGroupsResolver(String attribute) {
@ -62,4 +67,8 @@ class UserAttributeGroupsResolver implements GroupsResolver {
public String[] attributes() {
return new String[] { attribute };
}
public static Set<Setting<?>> getSettings() {
return Collections.singleton(ATTRIBUTE);
}
}

View File

@ -12,11 +12,14 @@ import com.unboundid.ldap.sdk.RoundRobinServerSet;
import com.unboundid.ldap.sdk.ServerSet;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import javax.net.SocketFactory;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
/**
* Enumeration representing the various supported {@link ServerSet} types that can be used with out built in realms.
@ -51,7 +54,7 @@ public enum LdapLoadBalancing {
if (InetAddresses.isInetAddress(addresses[0])) {
throw new IllegalArgumentException(toString() + " can only be used with a DNS name");
}
TimeValue dnsTtl = settings.getAsTime("cache_ttl", TimeValue.timeValueHours(1L));
TimeValue dnsTtl = settings.getAsTime(CACHE_TTL_SETTING, CACHE_TTL_DEFAULT);
return new RoundRobinDNSServerSet(addresses[0], ports[0],
RoundRobinDNSServerSet.AddressSelectionMode.ROUND_ROBIN, dnsTtl.millis(), null, socketFactory, options);
}
@ -67,7 +70,7 @@ public enum LdapLoadBalancing {
if (InetAddresses.isInetAddress(addresses[0])) {
throw new IllegalArgumentException(toString() + " can only be used with a DNS name");
}
TimeValue dnsTtl = settings.getAsTime("cache_ttl", TimeValue.timeValueHours(1L));
TimeValue dnsTtl = settings.getAsTime(CACHE_TTL_SETTING, CACHE_TTL_DEFAULT);
return new RoundRobinDNSServerSet(addresses[0], ports[0],
RoundRobinDNSServerSet.AddressSelectionMode.FAILOVER, dnsTtl.millis(), null, socketFactory, options);
}
@ -76,6 +79,8 @@ public enum LdapLoadBalancing {
public static final String LOAD_BALANCE_SETTINGS = "load_balance";
public static final String LOAD_BALANCE_TYPE_SETTING = "type";
public static final String LOAD_BALANCE_TYPE_DEFAULT = LdapLoadBalancing.FAILOVER.toString();
public static final String CACHE_TTL_SETTING = "cache_ttl";
public static final TimeValue CACHE_TTL_DEFAULT = TimeValue.timeValueHours(1L);
abstract ServerSet buildServerSet(String[] addresses, int[] ports, Settings settings, @Nullable SocketFactory socketFactory,
@Nullable LDAPConnectionOptions options);
@ -101,4 +106,11 @@ public enum LdapLoadBalancing {
Settings loadBalanceSettings = settings.getAsSettings(LOAD_BALANCE_SETTINGS);
return loadBalancing.buildServerSet(addresses, ports, loadBalanceSettings, socketFactory, options);
}
public static Set<Setting<?>> getSettings() {
Set<Setting<?>> settings = new HashSet<>();
settings.add(Setting.simpleString(LOAD_BALANCE_SETTINGS + "." + LOAD_BALANCE_TYPE_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(LOAD_BALANCE_SETTINGS + "." + CACHE_TTL_SETTING, Setting.Property.NodeScope));
return settings;
}
}

View File

@ -13,14 +13,20 @@ import com.unboundid.util.ssl.HostNameSSLSocketVerifier;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.ssl.SSLService;
import javax.net.SocketFactory;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
@ -149,6 +155,19 @@ public abstract class SessionFactory {
return sslUsed;
}
protected static Set<Setting<?>> getSettings() {
Set<Setting<?>> settings = new HashSet<>();
settings.addAll(LdapLoadBalancing.getSettings());
settings.add(Setting.listSetting(URLS_SETTING, Collections.emptyList(), Function.identity(), Setting.Property.NodeScope));
settings.add(Setting.timeSetting(TIMEOUT_TCP_CONNECTION_SETTING, TIMEOUT_DEFAULT, Setting.Property.NodeScope));
settings.add(Setting.timeSetting(TIMEOUT_TCP_READ_SETTING, TIMEOUT_DEFAULT, Setting.Property.NodeScope));
settings.add(Setting.timeSetting(TIMEOUT_LDAP_SETTING, TIMEOUT_DEFAULT, Setting.Property.NodeScope));
settings.add(Setting.boolSetting(HOSTNAME_VERIFICATION_SETTING, true, Setting.Property.NodeScope));
settings.add(Setting.boolSetting(FOLLOW_REFERRALS_SETTING, true, Setting.Property.NodeScope));
settings.addAll(SSLConfigurationSettings.withPrefix("ssl.").getAllSettings());
return settings;
}
public static class LDAPServers {
private final String[] addresses;

View File

@ -11,42 +11,48 @@ import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.Environment;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport;
import org.elasticsearch.xpack.ssl.CertUtils;
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.RealmSettings;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.elasticsearch.xpack.security.Security.setting;
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
import static org.elasticsearch.xpack.XPackSettings.TRANSPORT_SSL_ENABLED;
import static org.elasticsearch.xpack.security.Security.setting;
public class PkiRealm extends Realm {
public static final String PKI_CERT_HEADER_NAME = "__SECURITY_CLIENT_CERTIFICATE";
public static final String TYPE = "pki";
public static final String DEFAULT_USERNAME_PATTERN = "CN=(.*?)(?:,|$)";
static final String DEFAULT_USERNAME_PATTERN = "CN=(.*?)(?:,|$)";
private static final Setting<Pattern> USERNAME_PATTERN_SETTING = new Setting<>("username_pattern", DEFAULT_USERNAME_PATTERN,
s -> Pattern.compile(s, Pattern.CASE_INSENSITIVE), Setting.Property.NodeScope);
private static final SSLConfigurationSettings SSL_SETTINGS = SSLConfigurationSettings.withoutPrefix();
// For client based cert validation, the auth type must be specified but UNKNOWN is an acceptable value
public static final String AUTH_TYPE = "UNKNOWN";
@ -64,8 +70,7 @@ public class PkiRealm extends Realm {
PkiRealm(RealmConfig config, DnRoleMapper roleMapper, SSLService sslService) {
super(TYPE, config);
this.trustManager = trustManagers(config);
this.principalPattern = Pattern.compile(config.settings().get("username_pattern", DEFAULT_USERNAME_PATTERN),
Pattern.CASE_INSENSITIVE);
this.principalPattern = USERNAME_PATTERN_SETTING.get(config.settings());
this.roleMapper = roleMapper;
checkSSLEnabled(config, sslService);
}
@ -149,31 +154,26 @@ public class PkiRealm extends Realm {
static X509TrustManager trustManagers(RealmConfig realmConfig) {
final Settings settings = realmConfig.settings();
final Environment env = realmConfig.env();
String[] certificateAuthorities = settings.getAsArray("certificate_authorities", null);
String truststorePath = settings.get("truststore.path");
String[] certificateAuthorities = settings.getAsArray(SSL_SETTINGS.caPaths.getKey(), null);
String truststorePath = SSL_SETTINGS.truststorePath.get(settings).orElse(null);
if (truststorePath == null && certificateAuthorities == null) {
return null;
} else if (truststorePath != null && certificateAuthorities != null) {
final String settingPrefix = Realms.REALMS_GROUPS_SETTINGS.getKey() + realmConfig.name() + ".";
throw new IllegalArgumentException("[" + settingPrefix + "truststore.path] and [" + settingPrefix + "certificate_authorities]" +
" cannot be used at the same time");
final String pathKey = RealmSettings.getFullSettingKey(realmConfig, SSL_SETTINGS.truststorePath);
final String caKey = RealmSettings.getFullSettingKey(realmConfig, SSL_SETTINGS.caPaths);
throw new IllegalArgumentException("[" + pathKey + "] and [" + caKey + "] cannot be used at the same time");
} else if (truststorePath != null) {
return trustManagersFromTruststore(realmConfig);
return trustManagersFromTruststore(truststorePath, realmConfig);
}
return trustManagersFromCAs(settings, env);
}
private static X509TrustManager trustManagersFromTruststore(RealmConfig realmConfig) {
private static X509TrustManager trustManagersFromTruststore(String truststorePath, RealmConfig realmConfig) {
final Settings settings = realmConfig.settings();
String truststorePath = settings.get("truststore.path");
String password = settings.get("truststore.password");
if (password == null) {
final String settingPrefix = Realms.REALMS_GROUPS_SETTINGS.getKey() + realmConfig.name() + ".";
throw new IllegalArgumentException("[" + settingPrefix + "truststore.password] is not configured");
}
String trustStoreAlgorithm = settings.get("truststore.algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm",
TrustManagerFactory.getDefaultAlgorithm()));
String password = SSL_SETTINGS.truststorePassword.get(settings).orElseThrow(() -> new IllegalArgumentException(
"[" + RealmSettings.getFullSettingKey(realmConfig, SSL_SETTINGS.truststorePassword) + "] is not configured"
));
String trustStoreAlgorithm = SSL_SETTINGS.truststoreAlgorithm.get(settings);
try {
return CertUtils.trustManager(truststorePath, password, trustStoreAlgorithm, realmConfig.env());
} catch (Exception e) {
@ -182,7 +182,7 @@ public class PkiRealm extends Realm {
}
private static X509TrustManager trustManagersFromCAs(Settings settings, Environment env) {
String[] certificateAuthorities = settings.getAsArray("certificate_authorities", null);
String[] certificateAuthorities = settings.getAsArray(SSL_SETTINGS.caPaths.getKey(), null);
assert certificateAuthorities != null;
try {
Certificate[] certificates = CertUtils.readCertificates(Arrays.asList(certificateAuthorities), env);
@ -232,4 +232,21 @@ public class PkiRealm extends Realm {
throw new IllegalStateException("PKI realm [" + config.name() + "] is enabled but cannot be used as neither HTTP or Transport " +
"has SSL with client authentication enabled");
}
/**
* @return The {@link Setting setting configuration} for this realm type
*/
public static Set<Setting<?>> getSettings() {
Set<Setting<?>> settings = new HashSet<>();
settings.add(USERNAME_PATTERN_SETTING);
settings.add(SSL_SETTINGS.truststorePath);
settings.add(SSL_SETTINGS.truststorePassword);
settings.add(SSL_SETTINGS.truststoreAlgorithm);
settings.add(SSL_SETTINGS.caPaths);
DnRoleMapper.getSettings(settings);
return settings;
}
}

View File

@ -8,34 +8,41 @@ package org.elasticsearch.xpack.security.authc.support;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.user.User;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm implements CachingRealm {
public static final String CACHE_HASH_ALGO_SETTING = "cache.hash_algo";
public static final String CACHE_TTL_SETTING = "cache.ttl";
public static final String CACHE_MAX_USERS_SETTING = "cache.max_users";
public static final Setting<String> CACHE_HASH_ALGO_SETTING = Setting.simpleString("cache.hash_algo", Setting.Property.NodeScope);
private static final TimeValue DEFAULT_TTL = TimeValue.timeValueMinutes(20);
private static final int DEFAULT_MAX_USERS = 100000; //100k users
public static final Setting<TimeValue> CACHE_TTL_SETTING = Setting.timeSetting("cache.ttl", DEFAULT_TTL, Setting.Property.NodeScope);
private static final int DEFAULT_MAX_USERS = 100_000; //100k users
public static final Setting<Integer> CACHE_MAX_USERS_SETTING = Setting.intSetting("cache.max_users", DEFAULT_MAX_USERS,
Setting.Property.NodeScope);
private final Cache<String, UserWithHash> cache;
final Hasher hasher;
protected CachingUsernamePasswordRealm(String type, RealmConfig config) {
super(type, config);
hasher = Hasher.resolve(config.settings().get(CACHE_HASH_ALGO_SETTING, null), Hasher.SSHA256);
TimeValue ttl = config.settings().getAsTime(CACHE_TTL_SETTING, DEFAULT_TTL);
hasher = Hasher.resolve(CACHE_HASH_ALGO_SETTING.get(config.settings()), Hasher.SSHA256);
TimeValue ttl = CACHE_TTL_SETTING.get(config.settings());
if (ttl.getNanos() > 0) {
cache = CacheBuilder.<String, UserWithHash>builder()
.setExpireAfterAccess(ttl)
.setMaximumWeight(config.settings().getAsInt(CACHE_MAX_USERS_SETTING, DEFAULT_MAX_USERS))
.setMaximumWeight(CACHE_MAX_USERS_SETTING.get(config.settings()))
.build();
} else {
cache = null;
@ -172,6 +179,13 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
protected abstract void doLookupUser(String username, ActionListener<User> listener);
/**
* Returns the {@link Setting setting configuration} that is common for all caching realms
*/
protected static Set<Setting<?>> getCachingSettings() {
return new HashSet<>(Arrays.asList(CACHE_HASH_ALGO_SETTING, CACHE_TTL_SETTING, CACHE_MAX_USERS_SETTING));
}
private static class UserWithHash {
User user;
char[] hash;

View File

@ -11,6 +11,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.watcher.FileChangesListener;
@ -30,6 +31,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableMap;
@ -41,9 +43,12 @@ import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.rela
*/
public class DnRoleMapper {
public static final String DEFAULT_FILE_NAME = "role_mapping.yml";
public static final String ROLE_MAPPING_FILE_SETTING = "files.role_mapping";
public static final String USE_UNMAPPED_GROUPS_AS_ROLES_SETTING = "unmapped_groups_as_roles";
private static final String DEFAULT_FILE_NAME = "role_mapping.yml";
public static final Setting<String> ROLE_MAPPING_FILE_SETTING = new Setting<>("files.role_mapping", DEFAULT_FILE_NAME,
Function.identity(), Setting.Property.NodeScope);
public static final Setting<Boolean> USE_UNMAPPED_GROUPS_AS_ROLES_SETTING = Setting.boolSetting("unmapped_groups_as_roles", false,
Setting.Property.NodeScope);
protected final Logger logger;
protected final RealmConfig config;
@ -61,7 +66,7 @@ public class DnRoleMapper {
this.logger = config.logger(getClass());
this.listeners = new CopyOnWriteArrayList<>(Collections.singleton(listener));
useUnmappedGroupsAsRoles = config.settings().getAsBoolean(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, false);
useUnmappedGroupsAsRoles = USE_UNMAPPED_GROUPS_AS_ROLES_SETTING.get(config.settings());
file = resolveFile(config.settings(), config.env());
dnRoles = parseFileLenient(file, logger, realmType, config.name());
FileWatcher watcher = new FileWatcher(file.getParent());
@ -78,7 +83,7 @@ public class DnRoleMapper {
}
public static Path resolveFile(Settings settings, Environment env) {
String location = settings.get(ROLE_MAPPING_FILE_SETTING, DEFAULT_FILE_NAME);
String location = ROLE_MAPPING_FILE_SETTING.get(settings);
return XPackPlugin.resolveConfigFile(env, location);
}
@ -185,6 +190,11 @@ public class DnRoleMapper {
listeners.forEach(Runnable::run);
}
public static void getSettings(Set<Setting<?>> settings) {
settings.add(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING);
settings.add(ROLE_MAPPING_FILE_SETTING);
}
private class FileListener implements FileChangesListener {
@Override
public void onFileCreated(Path file) {

View File

@ -5,22 +5,19 @@
*/
package org.elasticsearch.xpack.ssl;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.XPackSettings;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Represents the configuration for an SSLContext
*/
@ -29,48 +26,7 @@ class SSLConfiguration {
// These settings are never registered, but they exist so that we can parse the values defined under grouped settings. Also, some are
// implemented as optional settings, which provides a declarative manner for fallback as we typically fallback to values from a
// different configuration
private static final Setting<List<String>> CIPHERS_SETTING = Setting.listSetting("cipher_suites", Collections.emptyList(), s -> s);
private static final Setting<List<String>> SUPPORTED_PROTOCOLS_SETTING =
Setting.listSetting("supported_protocols", Collections.emptyList(), s -> s);
private static final Setting<Optional<String>> KEYSTORE_PATH_SETTING =
new Setting<>("keystore.path", (String) null, Optional::ofNullable);
private static final Setting<Optional<String>> KEYSTORE_PASSWORD_SETTING =
new Setting<>("keystore.password", (String) null, Optional::ofNullable);
private static final Setting<String> KEYSTORE_ALGORITHM_SETTING = new Setting<>("keystore.algorithm",
s -> System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()), Function.identity());
private static final Setting<Optional<String>> KEYSTORE_KEY_PASSWORD_SETTING =
new Setting<>("keystore.key_password", KEYSTORE_PASSWORD_SETTING, Optional::ofNullable);
private static final Setting<Optional<String>> TRUSTSTORE_PATH_SETTING =
new Setting<>("truststore.path", (String) null, Optional::ofNullable);
private static final Setting<Optional<String>> TRUSTSTORE_PASSWORD_SETTING =
new Setting<>("truststore.password", (String) null, Optional::ofNullable);
private static final Setting<String> TRUSTSTORE_ALGORITHM_SETTING = new Setting<>("truststore.algorithm",
s -> System.getProperty("ssl.TrustManagerFactory.algorithm",
TrustManagerFactory.getDefaultAlgorithm()), Function.identity());
private static final Setting<Optional<String>> KEY_PATH_SETTING =
new Setting<>("key", (String) null, Optional::ofNullable);
private static final Setting<Optional<String>> KEY_PASSWORD_SETTING =
new Setting<>("key_passphrase", (String) null, Optional::ofNullable);
private static final Setting<Optional<String>> CERT_SETTING =
new Setting<>("certificate", (String) null, Optional::ofNullable);
private static final Setting<List<String>> CA_PATHS_SETTING =
Setting.listSetting("certificate_authorities", Collections.emptyList(), s -> s);
private static final Setting<Optional<SSLClientAuth>> CLIENT_AUTH_SETTING =
new Setting<>("client_authentication", (String) null, s -> {
if (s == null) {
return Optional.ofNullable(null);
} else {
return Optional.of(SSLClientAuth.parse(s));
}
});
private static final Setting<Optional<VerificationMode>> VERIFICATION_MODE_SETTING = new Setting<>("verification_mode", (String) null,
s -> {
if (s == null) {
return Optional.ofNullable(null);
} else {
return Optional.of(VerificationMode.parse(s));
}
});
private static final SSLConfigurationSettings SETTINGS_PARSER = SSLConfigurationSettings.withoutPrefix();
private final KeyConfig keyConfig;
private final TrustConfig trustConfig;
@ -87,10 +43,10 @@ class SSLConfiguration {
SSLConfiguration(Settings settings) {
this.keyConfig = createKeyConfig(settings, null);
this.trustConfig = createTrustConfig(settings, keyConfig, null);
this.ciphers = getListOrDefault(CIPHERS_SETTING, settings, XPackSettings.DEFAULT_CIPHERS);
this.supportedProtocols = getListOrDefault(SUPPORTED_PROTOCOLS_SETTING, settings, XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS);
this.sslClientAuth = CLIENT_AUTH_SETTING.get(settings).orElse(XPackSettings.CLIENT_AUTH_DEFAULT);
this.verificationMode = VERIFICATION_MODE_SETTING.get(settings).orElse(XPackSettings.VERIFICATION_MODE_DEFAULT);
this.ciphers = getListOrDefault(SETTINGS_PARSER.ciphers, settings, XPackSettings.DEFAULT_CIPHERS);
this.supportedProtocols = getListOrDefault(SETTINGS_PARSER.supportedProtocols, settings, XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS);
this.sslClientAuth = SETTINGS_PARSER.clientAuth.get(settings).orElse(XPackSettings.CLIENT_AUTH_DEFAULT);
this.verificationMode = SETTINGS_PARSER.verificationMode.get(settings).orElse(XPackSettings.VERIFICATION_MODE_DEFAULT);
}
/**
@ -103,10 +59,11 @@ class SSLConfiguration {
Objects.requireNonNull(globalSSLConfiguration);
this.keyConfig = createKeyConfig(settings, globalSSLConfiguration);
this.trustConfig = createTrustConfig(settings, keyConfig, globalSSLConfiguration);
this.ciphers = getListOrDefault(CIPHERS_SETTING, settings, globalSSLConfiguration.cipherSuites());
this.supportedProtocols = getListOrDefault(SUPPORTED_PROTOCOLS_SETTING, settings, globalSSLConfiguration.supportedProtocols());
this.sslClientAuth = CLIENT_AUTH_SETTING.get(settings).orElse(globalSSLConfiguration.sslClientAuth());
this.verificationMode = VERIFICATION_MODE_SETTING.get(settings).orElse(globalSSLConfiguration.verificationMode());
this.ciphers = getListOrDefault(SETTINGS_PARSER.ciphers, settings, globalSSLConfiguration.cipherSuites());
this.supportedProtocols = getListOrDefault(SETTINGS_PARSER.supportedProtocols, settings,
globalSSLConfiguration.supportedProtocols());
this.sslClientAuth = SETTINGS_PARSER.clientAuth.get(settings).orElse(globalSSLConfiguration.sslClientAuth());
this.verificationMode = SETTINGS_PARSER.verificationMode.get(settings).orElse(globalSSLConfiguration.verificationMode());
}
/**
@ -216,8 +173,8 @@ class SSLConfiguration {
}
private static KeyConfig createKeyConfig(Settings settings, SSLConfiguration global) {
String keyStorePath = KEYSTORE_PATH_SETTING.get(settings).orElse(null);
String keyPath = KEY_PATH_SETTING.get(settings).orElse(null);
String keyStorePath = SETTINGS_PARSER.keystorePath.get(settings).orElse(null);
String keyPath = SETTINGS_PARSER.keyPath.get(settings).orElse(null);
if (keyPath != null && keyStorePath != null) {
throw new IllegalArgumentException("you cannot specify a keystore and key file");
} else if (keyStorePath == null && keyPath == null) {
@ -233,29 +190,29 @@ class SSLConfiguration {
}
if (keyPath != null) {
String keyPassword = KEY_PASSWORD_SETTING.get(settings).orElse(null);
String certPath = CERT_SETTING.get(settings).orElse(null);
String keyPassword = SETTINGS_PARSER.keyPassword.get(settings).orElse(null);
String certPath = SETTINGS_PARSER.cert.get(settings).orElse(null);
if (certPath == null) {
throw new IllegalArgumentException("you must specify the certificates to use with the key");
}
return new PEMKeyConfig(keyPath, keyPassword, certPath);
} else {
String keyStorePassword = KEYSTORE_PASSWORD_SETTING.get(settings).orElse(null);
String keyStoreAlgorithm = KEYSTORE_ALGORITHM_SETTING.get(settings);
String keyStoreKeyPassword = KEYSTORE_KEY_PASSWORD_SETTING.get(settings).orElse(keyStorePassword);
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
String keyStorePassword = SETTINGS_PARSER.keystorePassword.get(settings).orElse(null);
String keyStoreAlgorithm = SETTINGS_PARSER.keystoreAlgorithm.get(settings);
String keyStoreKeyPassword = SETTINGS_PARSER.keystoreKeyPassword.get(settings).orElse(keyStorePassword);
String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings);
return new StoreKeyConfig(keyStorePath, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, trustStoreAlgorithm);
}
}
private static TrustConfig createTrustConfig(Settings settings, KeyConfig keyConfig, SSLConfiguration global) {
String trustStorePath = TRUSTSTORE_PATH_SETTING.get(settings).orElse(null);
List<String> caPaths = getListOrNull(CA_PATHS_SETTING, settings);
String trustStorePath = SETTINGS_PARSER.truststorePath.get(settings).orElse(null);
List<String> caPaths = getListOrNull(SETTINGS_PARSER.caPaths, settings);
if (trustStorePath != null && caPaths != null) {
throw new IllegalArgumentException("you cannot specify a truststore and ca files");
}
VerificationMode verificationMode = VERIFICATION_MODE_SETTING.get(settings).orElseGet(() -> {
VerificationMode verificationMode = SETTINGS_PARSER.verificationMode.get(settings).orElseGet(() -> {
if (global != null) {
return global.verificationMode();
}
@ -266,8 +223,8 @@ class SSLConfiguration {
} else if (caPaths != null) {
return new PEMTrustConfig(caPaths);
} else if (trustStorePath != null) {
String trustStorePassword = TRUSTSTORE_PASSWORD_SETTING.get(settings).orElse(null);
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
String trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings).orElse(null);
String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings);
return new StoreTrustConfig(trustStorePath, trustStorePassword, trustStoreAlgorithm);
} else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) {
return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"),

View File

@ -0,0 +1,135 @@
/*
* 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.ssl;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
/**
* Bridges {@link SSLConfiguration} into the {@link Settings} framework, using {@link Setting} objects.
*/
public class SSLConfigurationSettings {
private final String prefix;
public final Setting<List<String>> ciphers;
public final Setting<List<String>> supportedProtocols;
public final Setting<Optional<String>> keystorePath;
public final Setting<Optional<String>> keystorePassword;
public final Setting<String> keystoreAlgorithm;
public final Setting<Optional<String>> keystoreKeyPassword;
public final Setting<Optional<String>> truststorePath;
public final Setting<Optional<String>> truststorePassword;
public final Setting<String> truststoreAlgorithm;
public final Setting<Optional<String>> keyPath;
public final Setting<Optional<String>> keyPassword;
public final Setting<Optional<String>> cert;
public final Setting<List<String>> caPaths;
public final Setting<Optional<SSLClientAuth>> clientAuth;
public final Setting<Optional<VerificationMode>> verificationMode;
/**
* @see #withoutPrefix
* @see #withPrefix
* @param prefix The prefix under which each setting should be defined. Must be either the empty string (<code>""</code>) or a string
* ending in <code>"."</code>
*/
private SSLConfigurationSettings(String prefix) {
assert prefix != null : "Prefix cannot be null (but can be blank)";
this.prefix = prefix;
ciphers = list("cipher_suites", Collections.emptyList());
supportedProtocols = list("supported_protocols", Collections.emptyList());
keystorePath = optionalString("keystore.path");
keystorePassword = optionalString("keystore.password");
keystoreKeyPassword = optionalString("keystore.key_password", keystorePassword);
truststorePath = optionalString("truststore.path");
truststorePassword = optionalString("truststore.password");
keystoreAlgorithm = systemProperty("keystore.algorithm",
"ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm());
truststoreAlgorithm = systemProperty("truststore.algorithm", "ssl.TrustManagerFactory.algorithm",
TrustManagerFactory.getDefaultAlgorithm());
keyPath = optionalString("key");
keyPassword = optionalString("key_passphrase");
cert = optionalString("certificate");
caPaths = list("certificate_authorities", Collections.emptyList());
clientAuth = optional("client_authentication", SSLClientAuth::parse);
verificationMode = optional("verification_mode", VerificationMode::parse);
}
public List<Setting<?>> getAllSettings() {
return Arrays.asList(ciphers, supportedProtocols,
keystorePath, keystorePassword, keystoreAlgorithm, keystoreKeyPassword,
truststorePath, truststorePassword, truststoreAlgorithm,
keyPath, keyPassword,
cert, caPaths, clientAuth, verificationMode);
}
private Setting<Optional<String>> optionalString(String keyPart) {
return optionalString(keyPart, (s) -> null);
}
private Setting<Optional<String>> optionalString(String keyPart, Function<Settings, String> defaultValue) {
return new Setting<>(prefix + keyPart, defaultValue, Optional::ofNullable,
Setting.Property.NodeScope, Setting.Property.Filtered);
}
private Setting<Optional<String>> optionalString(String keyPart, Setting<Optional<String>> fallback) {
return new Setting<>(prefix + keyPart, fallback, Optional::ofNullable,
Setting.Property.NodeScope, Setting.Property.Filtered);
}
private <T> Setting<Optional<T>> optional(String keyPart, Function<String, T> parserIfNotNull) {
Function<String,Optional<T>> parser = s -> {
if (s == null) {
return Optional.empty();
} else {
return Optional.of(parserIfNotNull.apply(s));
}
};
return new Setting<>(prefix + keyPart, (String) null, parser, Setting.Property.NodeScope, Setting.Property.Filtered);
}
private Setting<String> systemProperty(String keyPart, String systemProperty, String defaultValue) {
return string(keyPart, s -> System.getProperty(systemProperty, defaultValue));
}
private Setting<String> string(String keyPart, Function<Settings, String> defaultFunction) {
return new Setting<>(prefix + keyPart, defaultFunction, Function.identity(),
Setting.Property.NodeScope, Setting.Property.Filtered);
}
private Setting<List<String>> list(String keyPart, List<String> defaultValue) {
return Setting.listSetting(prefix + keyPart, defaultValue, Function.identity(),
Setting.Property.NodeScope, Setting.Property.Filtered);
}
/**
* Construct settings that are un-prefixed. That is, they can be used to read from a {@link Settings} object where the configuration
* keys are the root names of the <code>Settings</code>.
*/
public static SSLConfigurationSettings withoutPrefix() {
return new SSLConfigurationSettings("");
}
/**
* Construct settings that have a prefixed. That is, they can be used to read from a {@link Settings} object where the configuration
* keys are prefixed-children of the <code>Settings</code>.
* @param prefix A string that must end in <code>"ssl."</code>
*/
public static SSLConfigurationSettings withPrefix(String prefix) {
assert prefix.endsWith("ssl.") : "The ssl config prefix (" + prefix + ") should end in 'ssl.'";
return new SSLConfigurationSettings(prefix);
}
}

View File

@ -14,6 +14,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.transport.TransportSettings;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.security.Security;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@ -62,7 +63,7 @@ public class SSLService extends AbstractComponent {
public SSLService(Settings settings, Environment environment) {
super(settings);
this.env = environment;
this.globalSSLConfiguration = new SSLConfiguration(settings.getByPrefix("xpack.ssl."));
this.globalSSLConfiguration = new SSLConfiguration(settings.getByPrefix(XPackSettings.GLOBAL_SSL_PREFIX));
this.sslContexts = loadSSLConfigurations();
}
@ -387,7 +388,7 @@ public class SSLService extends AbstractComponent {
Map<SSLConfiguration, SSLContextHolder> sslConfigurations = new HashMap<>();
sslConfigurations.put(globalSSLConfiguration, createSslContext(globalSSLConfiguration));
final Settings transportSSLSettings = settings.getByPrefix("xpack.security.transport.ssl.");
final Settings transportSSLSettings = settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX);
List<Settings> sslSettingsList = new ArrayList<>();
sslSettingsList.add(transportSSLSettings);
sslSettingsList.add(getHttpTransportSSLSettings(settings));
@ -741,7 +742,7 @@ public class SSLService extends AbstractComponent {
private static List<Settings> getRealmsSSLSettings(Settings settings) {
List<Settings> sslSettings = new ArrayList<>();
Settings realmsSettings = settings.getByPrefix("xpack.security.authc.realms.");
Settings realmsSettings = settings.getByPrefix(Security.setting("authc.realms."));
for (String name : realmsSettings.names()) {
Settings realmSSLSettings = realmsSettings.getAsSettings(name).getByPrefix("ssl.");
if (realmSSLSettings.isEmpty() == false) {
@ -764,7 +765,7 @@ public class SSLService extends AbstractComponent {
}
public static Settings getHttpTransportSSLSettings(Settings settings) {
Settings httpSSLSettings = settings.getByPrefix("xpack.security.http.ssl.");
Settings httpSSLSettings = settings.getByPrefix(XPackSettings.HTTP_SSL_PREFIX);
if (httpSSLSettings.isEmpty()) {
return httpSSLSettings;
}

View File

@ -39,7 +39,7 @@ public class SettingsFilterTests extends ESTestCase {
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.type", "ldap");
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.enabled", "false");
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.url", "ldap://host.domain");
configureFilteredSetting("xpack.security.authc.realms.ldap1.hostname_verification", randomAsciiOfLength(5));
configureFilteredSetting("xpack.security.authc.realms.ldap1.hostname_verification", randomBooleanSetting());
configureFilteredSetting("xpack.security.authc.realms.ldap1.bind_dn", randomAsciiOfLength(5));
configureFilteredSetting("xpack.security.authc.realms.ldap1.bind_password", randomAsciiOfLength(5));
@ -47,7 +47,7 @@ public class SettingsFilterTests extends ESTestCase {
configureUnfilteredSetting("xpack.security.authc.realms.ad1.type", "active_directory");
configureUnfilteredSetting("xpack.security.authc.realms.ad1.enabled", "false");
configureUnfilteredSetting("xpack.security.authc.realms.ad1.url", "ldap://host.domain");
configureFilteredSetting("xpack.security.authc.realms.ad1.hostname_verification", randomAsciiOfLength(5));
configureFilteredSetting("xpack.security.authc.realms.ad1.hostname_verification", randomBooleanSetting());
// pki filtering
configureUnfilteredSetting("xpack.security.authc.realms.pki1.type", "pki");
@ -115,6 +115,10 @@ public class SettingsFilterTests extends ESTestCase {
}
}
private String randomBooleanSetting() {
return randomFrom("true", "1", "on", "yes", "false", "0", "off", "no");
}
private void configureUnfilteredSetting(String settingName, String value) {
configureSetting(settingName, value, is(value));
}

View File

@ -284,8 +284,8 @@ public class HttpClientTests extends ESTestCase {
proxyServer.enqueue(new MockResponse().setResponseCode(200).setBody("fullProxiedContent"));
proxyServer.start();
Settings settings = Settings.builder()
.put(HttpClient.SETTINGS_PROXY_HOST, "localhost")
.put(HttpClient.SETTINGS_PROXY_PORT, proxyServer.getPort())
.put(HttpSettings.PROXY_HOST.getKey(), "localhost")
.put(HttpSettings.PROXY_PORT.getKey(), proxyServer.getPort())
.build();
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
@ -309,8 +309,8 @@ public class HttpClientTests extends ESTestCase {
proxyServer.enqueue(new MockResponse().setResponseCode(200).setBody("fullProxiedContent"));
proxyServer.start();
Settings settings = Settings.builder()
.put(HttpClient.SETTINGS_PROXY_HOST, "localhost")
.put(HttpClient.SETTINGS_PROXY_PORT, proxyServer.getPort() + 1)
.put(HttpSettings.PROXY_HOST.getKey(), "localhost")
.put(HttpSettings.PROXY_PORT.getKey(), proxyServer.getPort() + 1)
.build();
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));

View File

@ -65,7 +65,7 @@ public class SecurityTests extends ESTestCase {
ThreadPool threadPool = mock(ThreadPool.class);
ClusterService clusterService = mock(ClusterService.class);
settings = Security.additionalSettings(settings, false);
Set<Setting<?>> allowedSettings = new HashSet<>(Security.getSettings(false));
Set<Setting<?>> allowedSettings = new HashSet<>(Security.getSettings(false, null));
allowedSettings.addAll(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
ClusterSettings clusterSettings = new ClusterSettings(settings, allowedSettings);
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);

View File

@ -0,0 +1,288 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.extensions.XPackExtension;
import org.elasticsearch.xpack.security.authc.support.Hasher;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.notNullValue;
public class RealmSettingsTests extends ESTestCase {
private static final List<String> HASH_ALGOS = Arrays.stream(Hasher.values()).map(Hasher::name).collect(Collectors.toList());
public void testRealmWithoutTypeDoesNotValidate() throws Exception {
final Settings.Builder builder = baseSettings("x", false);
builder.remove("type");
assertErrorWithMessage("empty1", "missing realm type", realm("empty1", builder).build());
}
public void testRealmWithBlankTypeDoesNotValidate() throws Exception {
final Settings.Builder builder = baseSettings("", false);
assertErrorWithMessage("empty2", "missing realm type", realm("empty2", builder).build());
}
/**
* This test exists because (in 5.x), we want to be backwards compatible and accept custom realms that
* have not been updated to explicitly declare their settings.
*
* @see XPackExtension#getRealmSettings()
*/
public void testRealmWithUnknownTypeAcceptsAllSettings() throws Exception {
final Settings.Builder settings = baseSettings("tam", true)
.put("ip", "8.6.75.309")
.put(randomAsciiOfLengthBetween(4, 8), randomTimeValue());
assertSuccess(realm("tam", settings));
}
public void testFileRealmWithAllSettingsValidatesSuccessfully() throws Exception {
assertSuccess(fileRealm("file1"));
}
public void testFileRealmWithUnknownConfigurationDoesNotValidate() throws Exception {
final Settings.Builder builder = realm("file2", fileSettings().put("not-valid", randomInt()));
assertErrorWithCause("file2", "unknown setting [not-valid]", builder.build());
}
public void testNativeRealmWithAllSettingsValidatesSuccessfully() throws Exception {
assertSuccess(nativeRealm("native1"));
}
public void testNativeRealmWithUnknownConfigurationDoesNotValidate() throws Exception {
final Settings.Builder builder = realm("native2", nativeSettings().put("not-valid", randomAsciiOfLength(10)));
assertErrorWithCause("native2", "unknown setting [not-valid]", builder.build());
}
public void testLdapRealmWithUserTemplatesAndGroupAttributesValidatesSuccessfully() throws Exception {
assertSuccess(ldapRealm("ldap1", false, false));
}
public void testLdapRealmWithUserSearchAndGroupSearchValidatesSuccessfully() throws Exception {
assertSuccess(ldapRealm("ldap2", true, true));
}
public void testActiveDirectoryRealmWithAllSettingsValidatesSuccessfully() throws Exception {
assertSuccess(activeDirectoryRealm("ad1"));
}
public void testPkiRealmWithCertificateAuthoritiesValidatesSuccessfully() throws Exception {
assertSuccess(pkiRealm("pki1", false));
}
public void testPkiRealmWithTrustStoreValidatesSuccessfully() throws Exception {
assertSuccess(pkiRealm("pki2", true));
}
public void testPkiRealmWithFullSslSettingsDoesNotValidate() throws Exception {
final Settings.Builder realm = realm("pki3", configureSsl("", pkiSettings(true), true, true));
assertError("pki3", realm.build());
}
public void testSettingsWithMultipleRealmsValidatesSuccessfully() throws Exception {
final Settings settings = Settings.builder()
.put(fileRealm("file1").build())
.put(nativeRealm("native2").build())
.put(ldapRealm("ldap3", true, false).build())
.put(activeDirectoryRealm("ad4").build())
.put(pkiRealm("pki5", false).build())
.build();
assertSuccess(settings);
}
private Settings.Builder nativeRealm(String name) {
return realm(name, nativeSettings());
}
private Settings.Builder nativeSettings() {
return baseSettings("native", true);
}
private Settings.Builder fileRealm(String name) {
return realm(name, fileSettings());
}
private Settings.Builder fileSettings() {
return baseSettings("file", true);
}
private Settings.Builder ldapRealm(String name, boolean userSearch, boolean groupSearch) {
return realm(name, ldapSettings(userSearch, groupSearch));
}
private Settings.Builder ldapSettings(boolean userSearch, boolean groupSearch) {
final Settings.Builder builder = commonLdapSettings("ldap")
.put("bind_dn", "elasticsearch")
.put("bind_password", "t0p_s3cr3t")
.put("follow_referrals", randomBoolean());
if (userSearch) {
builder.put("user_search.base_dn", "o=people, dc=example, dc=com");
builder.put("user_search.scope", "sub_tree");
builder.put("user_search.attribute", randomAsciiOfLengthBetween(2, 5));
builder.put("user_search.pool.enabled", randomBoolean());
builder.put("user_search.pool.size", randomIntBetween(10, 100));
builder.put("user_search.pool.initial_size", randomIntBetween(1, 10));
builder.put("user_search.pool.health_check.enabled", randomBoolean());
builder.put("user_search.pool.health_check.dn", randomAsciiOfLength(32));
builder.put("user_search.pool.health_check.interval", randomPositiveTimeValue());
} else {
builder.putArray("user_dn_templates",
"cn={0}, ou=staff, o=people, dc=example, dc=com",
"cn={0}, ou=visitors, o=people, dc=example, dc=com");
}
if (groupSearch) {
builder.put("group_search.base_dn", "o=groups, dc=example, dc=com");
builder.put("group_search.scope", "one_level");
builder.put("group_search.filter", "userGroup");
builder.put("group_search.user_attribute", "uid");
} else {
builder.put("user_group_attribute", randomAsciiOfLength(8));
}
return builder;
}
private Settings.Builder activeDirectoryRealm(String name) {
return realm(name, activeDirectorySettings());
}
private Settings.Builder activeDirectorySettings() {
final Settings.Builder builder = commonLdapSettings("active_directory")
.put("domain_name", "MEGACORP");
builder.put("user_search.base_dn", "o=people, dc.example, dc.com");
builder.put("user_search.scope", "sub_tree");
builder.put("user_search.filter", randomAsciiOfLength(5) + "={0}");
builder.put("group_search.base_dn", "o=groups, dc=example, dc=com");
builder.put("group_search.scope", "one_level");
return builder;
}
private Settings.Builder commonLdapSettings(String type) {
final Settings.Builder builder = baseSettings(type, true)
.putArray("url", "ldap://dir1.internal:9876", "ldap://dir2.internal:9876", "ldap://dir3.internal:9876")
.put("load_balance.type", "round_robin")
.put("load_balance.cache_ttl", randomTimeValue())
.put("unmapped_groups_as_roles", randomBoolean())
.put("files.role_mapping", "x-pack/" + randomAsciiOfLength(8) + ".yml")
.put("timeout.tcp_connect", randomPositiveTimeValue())
.put("timeout.tcp_read", randomPositiveTimeValue())
.put("timeout.ldap_search", randomPositiveTimeValue());
configureSsl("ssl.", builder, randomBoolean(), randomBoolean());
return builder;
}
private Settings.Builder pkiRealm(String name, boolean useTrustStore) {
return realm(name, pkiSettings(useTrustStore));
}
private Settings.Builder pkiSettings(boolean useTrustStore) {
final Settings.Builder builder = baseSettings("pki", false)
.put("username_pattern", "CN=\\D(\\d+)(?:,\\|$)")
.put("files.role_mapping", "x-pack/" + randomAsciiOfLength(8) + ".yml");
if (useTrustStore) {
builder.put("truststore.path", randomAsciiOfLengthBetween(8, 32));
builder.put("truststore.password", randomAsciiOfLengthBetween(4, 12));
builder.put("truststore.algorithm", randomAsciiOfLengthBetween(6, 10));
} else {
builder.putArray("certificate_authorities", generateRandomStringArray(5, 32, false, false));
}
return builder;
}
private Settings.Builder configureSsl(String prefix, Settings.Builder builder, boolean useKeyStore, boolean useTrustStore) {
if (useKeyStore) {
builder.put(prefix + "keystore.path", "x-pack/ssl/" + randomAsciiOfLength(5) + ".jks");
builder.put(prefix + "keystore.password", randomAsciiOfLength(8));
builder.put(prefix + "keystore.key_password", randomAsciiOfLength(8));
} else {
builder.put(prefix + "key", "x-pack/ssl/" + randomAsciiOfLength(5) + ".key");
builder.put(prefix + "key_passphrase", randomAsciiOfLength(32));
builder.put(prefix + "certificate", "x-pack/ssl/" + randomAsciiOfLength(5) + ".cert");
}
if (useTrustStore) {
builder.put(prefix + "truststore.path", "x-pack/ssl/" + randomAsciiOfLength(5) + ".jts");
builder.put(prefix + "truststore.password", randomAsciiOfLength(8));
} else {
builder.put(prefix + "certificate_authorities", "x-pack/ssl/" + randomAsciiOfLength(8) + ".ca");
}
builder.put(prefix + "verification_mode", "full");
builder.putArray(prefix + "supported_protocols", randomSubsetOf(XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS));
builder.putArray(prefix + "cipher_suites", randomSubsetOf(XPackSettings.DEFAULT_CIPHERS));
return builder;
}
private Settings.Builder baseSettings(String type, boolean withCacheSettings) {
final Settings.Builder builder = Settings.builder()
.put("type", type)
.put("order", randomInt())
.put("enabled", true);
if (withCacheSettings) {
builder.put("cache.ttl", randomPositiveTimeValue())
.put("cache.max_users", randomIntBetween(1_000, 1_000_000))
.put("cache.hash_algo", randomFrom(HASH_ALGOS));
}
return builder;
}
private Settings.Builder realm(String name, Settings.Builder settings) {
return settings.normalizePrefix(realmPrefix(name));
}
private String realmPrefix(String name) {
return RealmSettings.PREFIX + name + ".";
}
private void assertSuccess(Settings.Builder builder) {
assertSuccess(builder.build());
}
private void assertSuccess(Settings settings) {
assertThat(group().get(settings), notNullValue());
}
private void assertErrorWithCause(String realmName, String message, Settings settings) {
final IllegalArgumentException exception = assertError(realmName, settings);
assertThat(exception.getCause(), notNullValue());
assertThat(exception.getCause().getMessage(), containsString(message));
}
private void assertErrorWithMessage(String realmName, String message, Settings settings) {
final IllegalArgumentException exception = assertError(realmName, settings);
assertThat(exception.getMessage(), containsString(message));
}
private IllegalArgumentException assertError(String realmName, Settings settings) {
final IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> group().get(settings)
);
assertThat(exception.getMessage(), containsString(realmPrefix(realmName)));
return exception;
}
private Setting<?> group() {
final List<Setting<?>> list = new ArrayList<>();
final List<XPackExtension> noExtensions = Collections.emptyList();
RealmSettings.addSettings(list, noExtensions);
assertThat(list, hasSize(1));
return list.get(0);
}
}

View File

@ -64,6 +64,7 @@ import static org.mockito.Mockito.verify;
public class ActiveDirectoryRealmTests extends ESTestCase {
private static final String PASSWORD = "password";
private static final String ROLE_MAPPING_FILE_SETTING = DnRoleMapper.ROLE_MAPPING_FILE_SETTING.getKey();
static int numberOfLdapServers;
InMemoryDirectoryServer[] directoryServers;
@ -168,7 +169,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
}
public void testAuthenticateCachingCanBeDisabled() throws Exception {
Settings settings = settings(Settings.builder().put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING, -1).build());
Settings settings = settings(Settings.builder().put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING.getKey(), -1).build());
RealmConfig config = new RealmConfig("testAuthenticateCachingCanBeDisabled", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null));
DnRoleMapper roleMapper = new DnRoleMapper(LdapRealm.AD_TYPE, config, resourceWatcherService, () -> {});
@ -216,7 +217,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
public void testRealmMapsGroupsToRoles() throws Exception {
Settings settings = settings(Settings.builder()
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
.put(ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
.build());
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
@ -232,7 +233,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
public void testRealmMapsUsersToRoles() throws Exception {
Settings settings = settings(Settings.builder()
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
.put(ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
.build());
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
@ -249,7 +250,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
public void testRealmUsageStats() throws Exception {
String loadBalanceType = randomFrom("failover", "round_robin");
Settings settings = settings(Settings.builder()
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
.put(ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
.put("load_balance.type", loadBalanceType)
.build());
RealmConfig config = new RealmConfig("testRealmUsageStats", settings, globalSettings);
@ -274,7 +275,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
return Settings.builder()
.putArray(URLS_SETTING, ldapUrls())
.put(ActiveDirectorySessionFactory.AD_DOMAIN_NAME_SETTING, "ad.test.elasticsearch.com")
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING.getKey(), true)
.put(HOSTNAME_VERIFICATION_SETTING, false)
.put(extraSettings)
.build();

View File

@ -12,6 +12,7 @@ import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapTestCase;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
@ -23,9 +24,9 @@ import org.elasticsearch.watcher.ResourceWatcherService;
import org.junit.After;
import org.junit.Before;
import java.util.Arrays;
import java.util.Map;
import static org.elasticsearch.xpack.security.authc.ldap.LdapSessionFactory.USER_DN_TEMPLATES_SETTING;
import static org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory.HOSTNAME_VERIFICATION_SETTING;
import static org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory.URLS_SETTING;
import static org.hamcrest.Matchers.arrayContaining;
@ -46,6 +47,8 @@ public class LdapRealmTests extends LdapTestCase {
public static final String VALID_USERNAME = "Thomas Masterman Hardy";
public static final String PASSWORD = "pass";
private static final String USER_DN_TEMPLATES_SETTING_KEY = LdapSessionFactory.USER_DN_TEMPLATES_SETTING.getKey();
private ThreadPool threadPool;
private ResourceWatcherService resourceWatcherService;
private Settings globalSettings;
@ -95,7 +98,7 @@ public class LdapRealmTests extends LdapTestCase {
ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)), future);
User user = future.actionGet();
assertThat(user, notNullValue());
assertThat(user.roles(), arrayContaining("HMS Victory"));
assertThat("For roles " + Arrays.toString(user.roles()), user.roles(), arrayContaining("HMS Victory"));
}
public void testAuthenticateCaching() throws Exception {
@ -158,7 +161,7 @@ public class LdapRealmTests extends LdapTestCase {
String userTemplate = VALID_USER_TEMPLATE;
Settings settings = Settings.builder()
.put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
.put(LdapRealm.CACHE_TTL_SETTING, -1)
.put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING.getKey(), -1)
.build();
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
@ -182,7 +185,7 @@ public class LdapRealmTests extends LdapTestCase {
String userTemplate = VALID_USER_TEMPLATE;
Settings settings = Settings.builder()
.putArray(URLS_SETTING, ldapUrls())
.putArray(USER_DN_TEMPLATES_SETTING, userTemplate)
.putArray(USER_DN_TEMPLATES_SETTING_KEY, userTemplate)
.put("group_search.base_dn", groupSearchBase)
.put("group_search.scope", LdapSearchScope.SUB_TREE)
.put(HOSTNAME_VERIFICATION_SETTING, false)
@ -215,7 +218,7 @@ public class LdapRealmTests extends LdapTestCase {
public void testLdapRealmThrowsExceptionForUserTemplateAndSearchSettings() throws Exception {
Settings settings = Settings.builder()
.putArray(URLS_SETTING, ldapUrls())
.putArray(USER_DN_TEMPLATES_SETTING, "cn=foo")
.putArray(USER_DN_TEMPLATES_SETTING_KEY, "cn=foo")
.put("user_search.base_dn", "cn=bar")
.put("group_search.base_dn", "")
.put("group_search.scope", LdapSearchScope.SUB_TREE)
@ -224,7 +227,26 @@ public class LdapRealmTests extends LdapTestCase {
RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings);
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> LdapRealm.sessionFactory(config, null, LdapRealm.LDAP_TYPE));
assertThat(e.getMessage(), containsString("settings were found for both user search and user template"));
assertThat(e.getMessage(),
containsString("settings were found for both" +
" user search [xpack.security.authc.realms.test-ldap-realm-user-search.user_search.] and" +
" user template [xpack.security.authc.realms.test-ldap-realm-user-search.user_dn_templates]"));
}
public void testLdapRealmThrowsExceptionWhenNeitherUserTemplateNorSearchSettingsProvided() throws Exception {
Settings settings = Settings.builder()
.putArray(URLS_SETTING, ldapUrls())
.put("group_search.base_dn", "")
.put("group_search.scope", LdapSearchScope.SUB_TREE)
.put(HOSTNAME_VERIFICATION_SETTING, false)
.build();
RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings);
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> LdapRealm.sessionFactory(config, null, LdapRealm.LDAP_TYPE));
assertThat(e.getMessage(),
containsString("settings were not found for either" +
" user search [xpack.security.authc.realms.test-ldap-realm-user-search.user_search.] or" +
" user template [xpack.security.authc.realms.test-ldap-realm-user-search.user_dn_templates]"));
}
public void testLdapRealmMapsUserDNToRole() throws Exception {
@ -232,7 +254,7 @@ public class LdapRealmTests extends LdapTestCase {
String userTemplate = VALID_USER_TEMPLATE;
Settings settings = Settings.builder()
.put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING,
.put(DnRoleMapper.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);
@ -256,6 +278,7 @@ public class LdapRealmTests extends LdapTestCase {
.put("bind_password", PASSWORD)
.put("group_search.base_dn", groupSearchBase)
.put("group_search.scope", LdapSearchScope.SUB_TREE)
.put(LdapSessionFactory.USER_DN_TEMPLATES_SETTING.getKey(), "--")
.put(HOSTNAME_VERIFICATION_SETTING, false);
int order = randomIntBetween(0, 10);

View File

@ -27,7 +27,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testResolveSubTree() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
@ -42,8 +42,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testResolveOneLevel() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("scope", LdapSearchScope.ONE_LEVEL)
.put("group_search.base_dn", "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.scope", LdapSearchScope.ONE_LEVEL)
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
@ -58,8 +58,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testResolveBase() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "cn=Avengers,ou=People,dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("scope", LdapSearchScope.BASE)
.put("group_search.base_dn", "cn=Avengers,ou=People,dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.scope", LdapSearchScope.BASE)
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
@ -70,9 +70,9 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testResolveCustomFilter() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("filter", "(&(objectclass=posixGroup)(memberUID={0}))")
.put("user_attribute", "uid")
.put("group_search.base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.filter", "(&(objectclass=posixGroup)(memberUID={0}))")
.put("group_search.user_attribute", "uid")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
@ -84,8 +84,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testFilterIncludesPosixGroups() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("user_attribute", "uid")
.put("group_search.base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.user_attribute", "uid")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
@ -97,7 +97,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testCreateWithoutSpecifyingBaseDN() throws Exception {
Settings settings = Settings.builder()
.put("scope", LdapSearchScope.SUB_TREE)
.put("group_search.scope", LdapSearchScope.SUB_TREE)
.build();
try {
@ -110,8 +110,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testReadUserAttributeUid() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("user_attribute", "uid").build();
.put("group_search.base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.user_attribute", "uid").build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
PlainActionFuture<String> future = new PlainActionFuture<>();
resolver.readUserAttribute(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(5), future);
@ -120,8 +120,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testReadUserAttributeCn() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("user_attribute", "cn")
.put("group_search.base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.user_attribute", "cn")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
@ -132,8 +132,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testReadNonExistentUserAttribute() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("user_attribute", "doesntExists")
.put("group_search.base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.user_attribute", "doesntExists")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
@ -144,8 +144,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public void testReadBinaryUserAttribute() throws Exception {
Settings settings = Settings.builder()
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("user_attribute", "userPassword")
.put("group_search.base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
.put("group_search.user_attribute", "userPassword")
.build();
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);

View File

@ -14,6 +14,7 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapSessionFactory;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.watcher.ResourceWatcherService;
@ -28,10 +29,11 @@ import java.util.Objects;
import static org.elasticsearch.xpack.security.authc.ldap.LdapSessionFactory.HOSTNAME_VERIFICATION_SETTING;
import static org.elasticsearch.xpack.security.authc.ldap.LdapSessionFactory.URLS_SETTING;
import static org.elasticsearch.xpack.security.authc.ldap.LdapSessionFactory.USER_DN_TEMPLATES_SETTING;
public abstract class LdapTestCase extends ESTestCase {
private static final String USER_DN_TEMPLATES_SETTING_KEY = LdapSessionFactory.USER_DN_TEMPLATES_SETTING.getKey();
static int numberOfLdapServers;
protected InMemoryDirectoryServer[] ldapServers;
@ -86,7 +88,7 @@ public abstract class LdapTestCase extends ESTestCase {
LdapLoadBalancing serverSetType) {
Settings.Builder builder = Settings.builder()
.putArray(URLS_SETTING, ldapUrl)
.putArray(USER_DN_TEMPLATES_SETTING, userTemplate)
.putArray(USER_DN_TEMPLATES_SETTING_KEY, userTemplate)
.put("group_search.base_dn", groupSearchBase)
.put("group_search.scope", scope)
.put(HOSTNAME_VERIFICATION_SETTING, false);
@ -100,14 +102,14 @@ public abstract class LdapTestCase extends ESTestCase {
public static Settings buildLdapSettings(String[] ldapUrl, String userTemplate, boolean hostnameVerification) {
return Settings.builder()
.putArray(URLS_SETTING, ldapUrl)
.putArray(USER_DN_TEMPLATES_SETTING, userTemplate)
.putArray(USER_DN_TEMPLATES_SETTING_KEY, userTemplate)
.put(HOSTNAME_VERIFICATION_SETTING, hostnameVerification)
.build();
}
protected DnRoleMapper buildGroupAsRoleMapper(ResourceWatcherService resourceWatcherService) {
Settings settings = Settings.builder()
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING.getKey(), true)
.build();
Settings global = Settings.builder().put("path.home", createTempDir()).build();
RealmConfig config = new RealmConfig("ldap1", settings, global);

View File

@ -42,9 +42,9 @@ public class CachingUsernamePasswordRealmTests extends ESTestCase {
int maxUsers = randomIntBetween(10, 100);
TimeValue ttl = TimeValue.timeValueMinutes(randomIntBetween(10, 20));
Settings settings = Settings.builder()
.put(CachingUsernamePasswordRealm.CACHE_HASH_ALGO_SETTING, hashAlgo)
.put(CachingUsernamePasswordRealm.CACHE_MAX_USERS_SETTING, maxUsers)
.put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING, ttl)
.put(CachingUsernamePasswordRealm.CACHE_HASH_ALGO_SETTING.getKey(), hashAlgo)
.put(CachingUsernamePasswordRealm.CACHE_MAX_USERS_SETTING.getKey(), maxUsers)
.put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING.getKey(), ttl)
.build();
RealmConfig config = new RealmConfig("test_realm", settings, globalSettings);

View File

@ -47,6 +47,9 @@ import static org.hamcrest.Matchers.notNullValue;
public class DnRoleMapperTests extends ESTestCase {
private static final String ROLE_MAPPING_FILE_SETTING = DnRoleMapper.ROLE_MAPPING_FILE_SETTING.getKey();
private static final String USE_UNMAPPED_GROUPS_AS_ROLES_SETTING_KEY = DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING.getKey();
private static final String[] STARK_GROUP_DNS = new String[] {
//groups can be named by different attributes, depending on the directory,
//we don't care what it is named by
@ -230,7 +233,7 @@ public class DnRoleMapperTests extends ESTestCase {
public void testYaml() throws Exception {
Path file = getDataPath("role_mapping.yml");
Settings ldapSettings = Settings.builder()
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
.put(ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
.build();
RealmConfig config = new RealmConfig("ldap1", ldapSettings, settings);
@ -244,7 +247,7 @@ public class DnRoleMapperTests extends ESTestCase {
public void testRelativeDN() {
Settings ldapSettings = Settings.builder()
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
.put(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING_KEY, true)
.build();
RealmConfig config = new RealmConfig("ldap1", ldapSettings, settings);
@ -257,8 +260,8 @@ public class DnRoleMapperTests extends ESTestCase {
public void testUserDNMapping() throws Exception {
Path file = getDataPath("role_mapping.yml");
Settings ldapSettings = Settings.builder()
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, false)
.put(ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
.put(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING_KEY, false)
.build();
RealmConfig config = new RealmConfig("ldap-userdn-role", ldapSettings, settings);

View File

@ -0,0 +1,103 @@
/*
* 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.ssl;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.util.Arrays;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import static org.hamcrest.Matchers.is;
public class SSLConfigurationSettingsTests extends ESTestCase {
public void testParseCipherSettingsWithoutPrefix() {
final SSLConfigurationSettings ssl = SSLConfigurationSettings.withoutPrefix();
assertThat(ssl.ciphers.match("cipher_suites"), is(true));
assertThat(ssl.ciphers.match("ssl.cipher_suites"), is(false));
assertThat(ssl.ciphers.match("xpack.ssl.cipher_suites"), is(false));
final Settings settings = Settings.builder()
.put("cipher_suites.0", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256")
.put("cipher_suites.1", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256")
.put("cipher_suites.2", "TLS_RSA_WITH_AES_128_CBC_SHA256")
.build();
assertThat(ssl.ciphers.get(settings), is(Arrays.asList(
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256"
)));
}
public void testParseClientAuthWithPrefix() {
final SSLConfigurationSettings ssl = SSLConfigurationSettings.withPrefix("xpack.security.http.ssl.");
assertThat(ssl.clientAuth.match("xpack.security.http.ssl.client_authentication"), is(true));
assertThat(ssl.clientAuth.match("client_authentication"), is(false));
final Settings settings = Settings.builder()
.put("xpack.security.http.ssl.client_authentication", SSLClientAuth.OPTIONAL.name())
.build();
assertThat(ssl.clientAuth.get(settings).get(), is(SSLClientAuth.OPTIONAL));
}
public void testParseKeystoreAlgorithmWithPrefix() {
final SSLConfigurationSettings ssl = SSLConfigurationSettings.withPrefix("xpack.security.authc.realms.ldap1.ssl.");
assertThat(ssl.keystoreAlgorithm.match("xpack.security.authc.realms.ldap1.ssl.keystore.algorithm"), is(true));
final String algo = randomAsciiOfLength(16);
final Settings settings = Settings.builder()
.put("xpack.security.authc.realms.ldap1.ssl.keystore.algorithm", algo)
.build();
assertThat(ssl.keystoreAlgorithm.get(settings), is(algo));
}
public void testParseProtocolsListWithPrefix() {
final SSLConfigurationSettings ssl = SSLConfigurationSettings.withPrefix("ssl.");
assertThat(ssl.supportedProtocols.match("ssl.supported_protocols"), is(true));
final Settings settings = Settings.builder()
.putArray("ssl.supported_protocols", "SSLv3", "SSLv2Hello", "SSLv2")
.build();
assertThat(ssl.supportedProtocols.get(settings), is(Arrays.asList("SSLv3", "SSLv2Hello", "SSLv2")));
}
public void testKeyStoreKeyPasswordDefaultsToKeystorePassword() {
final SSLConfigurationSettings ssl = SSLConfigurationSettings.withPrefix("xpack.ssl.");
assertThat(ssl.keystorePassword.match("xpack.ssl.keystore.password"), is(true));
assertThat(ssl.keystoreKeyPassword.match("xpack.ssl.keystore.key_password"), is(true));
assertThat(ssl.keystorePassword.match("xpack.ssl.keystore.key_password"), is(false));
assertThat(ssl.keystoreKeyPassword.match("xpack.ssl.keystore.password"), is(false));
final String password = randomAsciiOfLength(16);
final Settings settings = Settings.builder()
.put("xpack.ssl.keystore.password", password)
.build();
assertThat(ssl.keystoreKeyPassword.get(settings).get(), is(password));
}
public void testEmptySettingsParsesToDefaults() {
final SSLConfigurationSettings ssl = SSLConfigurationSettings.withoutPrefix();
final Settings settings = Settings.EMPTY;
assertThat(ssl.caPaths.get(settings).size(), is(0));
assertThat(ssl.cert.get(settings).isPresent(), is(false));
assertThat(ssl.ciphers.get(settings).size(), is(0));
assertThat(ssl.clientAuth.get(settings).isPresent(), is(false));
assertThat(ssl.keyPassword.get(settings).isPresent(), is(false));
assertThat(ssl.keyPath.get(settings).isPresent(), is(false));
assertThat(ssl.keystoreAlgorithm.get(settings), is(KeyManagerFactory.getDefaultAlgorithm()));
assertThat(ssl.keystoreKeyPassword.get(settings).isPresent(), is(false));
assertThat(ssl.keystorePassword.get(settings).isPresent(), is(false));
assertThat(ssl.keystorePath.get(settings).isPresent(), is(false));
assertThat(ssl.supportedProtocols.get(settings).size(), is(0));
assertThat(ssl.truststoreAlgorithm.get(settings), is(TrustManagerFactory.getDefaultAlgorithm()));
assertThat(ssl.truststorePassword.get(settings).isPresent(), is(false));
assertThat(ssl.truststorePath.get(settings).isPresent(), is(false));
assertThat(ssl.verificationMode.get(settings).isPresent(), is(false));
}
}

View File

@ -37,6 +37,7 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
@ -236,6 +237,21 @@ public class SSLServiceTests extends ESTestCase {
settings.getByPrefix("xpack.security.transport.ssl.")));
}
public void testThatHttpClientAuthDefaultsToNone() {
final Settings globalSettings = Settings.builder()
.put("xpack.security.http.ssl.enabled", true)
.put("xpack.ssl.client_authentication", SSLClientAuth.OPTIONAL.name())
.build();
final SSLService sslService = new SSLService(globalSettings, env);
final SSLConfiguration globalConfig = sslService.sslConfiguration(Settings.EMPTY);
assertThat(globalConfig.sslClientAuth(), is(SSLClientAuth.OPTIONAL));
final Settings httpSettings = SSLService.getHttpTransportSSLSettings(globalSettings);
final SSLConfiguration httpConfig = sslService.sslConfiguration(httpSettings);
assertThat(httpConfig.sslClientAuth(), is(SSLClientAuth.NONE));
}
public void testThatTruststorePasswordIsRequired() throws Exception {
Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", testnodeStore)