mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-24 17:09:48 +00:00
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:
parent
85679fcf19
commit
7192c46307
@ -52,6 +52,7 @@ import org.elasticsearch.xpack.action.XPackInfoAction;
|
|||||||
import org.elasticsearch.xpack.action.XPackUsageAction;
|
import org.elasticsearch.xpack.action.XPackUsageAction;
|
||||||
import org.elasticsearch.xpack.common.http.HttpClient;
|
import org.elasticsearch.xpack.common.http.HttpClient;
|
||||||
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
|
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.HttpAuthFactory;
|
||||||
import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry;
|
import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry;
|
||||||
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth;
|
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth;
|
||||||
@ -316,7 +317,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
|||||||
@Override
|
@Override
|
||||||
public List<Setting<?>> getSettings() {
|
public List<Setting<?>> getSettings() {
|
||||||
ArrayList<Setting<?>> settings = new ArrayList<>();
|
ArrayList<Setting<?>> settings = new ArrayList<>();
|
||||||
settings.addAll(Security.getSettings(transportClientMode));
|
settings.addAll(Security.getSettings(transportClientMode, extensionsService));
|
||||||
settings.addAll(MonitoringSettings.getSettings());
|
settings.addAll(MonitoringSettings.getSettings());
|
||||||
settings.addAll(watcher.getSettings());
|
settings.addAll(watcher.getSettings());
|
||||||
settings.addAll(licensing.getSettings());
|
settings.addAll(licensing.getSettings());
|
||||||
@ -335,10 +336,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
|
|||||||
settings.add(ReportingAttachmentParser.INTERVAL_SETTING);
|
settings.add(ReportingAttachmentParser.INTERVAL_SETTING);
|
||||||
|
|
||||||
// http settings
|
// http settings
|
||||||
settings.add(Setting.simpleString("xpack.http.default_read_timeout", Setting.Property.NodeScope));
|
settings.addAll(HttpSettings.getSettings());
|
||||||
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));
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,24 +5,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
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.
|
* A container for xpack setting constants.
|
||||||
*/
|
*/
|
||||||
@ -74,170 +69,23 @@ public class XPackSettings {
|
|||||||
public static final VerificationMode VERIFICATION_MODE_DEFAULT = VerificationMode.FULL;
|
public static final VerificationMode VERIFICATION_MODE_DEFAULT = VerificationMode.FULL;
|
||||||
|
|
||||||
// global settings that apply to everything!
|
// global settings that apply to everything!
|
||||||
private static final Setting<List<String>> CIPHERS_SETTING = Setting.listSetting("xpack.ssl.cipher_suites", DEFAULT_CIPHERS,
|
public static final String GLOBAL_SSL_PREFIX = "xpack.ssl.";
|
||||||
Function.identity(), Property.NodeScope, Property.Filtered);
|
private static final SSLConfigurationSettings GLOBAL_SSL = SSLConfigurationSettings.withPrefix(GLOBAL_SSL_PREFIX);
|
||||||
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);
|
|
||||||
|
|
||||||
// http specific settings
|
// http specific settings
|
||||||
private static final Setting<List<String>> HTTP_CIPHERS_SETTING = Setting.listSetting("xpack.security.http.ssl.cipher_suites",
|
public static final String HTTP_SSL_PREFIX = Security.setting("http.ssl.");
|
||||||
DEFAULT_CIPHERS, Function.identity(), Property.NodeScope, Property.Filtered);
|
private static final SSLConfigurationSettings HTTP_SSL = SSLConfigurationSettings.withPrefix(HTTP_SSL_PREFIX);
|
||||||
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);
|
|
||||||
|
|
||||||
// transport specific settings
|
// transport specific settings
|
||||||
private static final Setting<List<String>> TRANSPORT_CIPHERS_SETTING =
|
public static final String TRANSPORT_SSL_PREFIX = Security.setting("transport.ssl.");
|
||||||
Setting.listSetting("xpack.security.transport.ssl.cipher_suites", DEFAULT_CIPHERS, Function.identity(),
|
private static final SSLConfigurationSettings TRANSPORT_SSL = SSLConfigurationSettings.withPrefix(TRANSPORT_SSL_PREFIX);
|
||||||
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);
|
|
||||||
/* End SSL settings */
|
/* End SSL settings */
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ALL_SETTINGS.add(CIPHERS_SETTING);
|
ALL_SETTINGS.addAll(GLOBAL_SSL.getAllSettings());
|
||||||
ALL_SETTINGS.add(SUPPORTED_PROTOCOLS_SETTING);
|
ALL_SETTINGS.addAll(HTTP_SSL.getAllSettings());
|
||||||
ALL_SETTINGS.add(KEYSTORE_PATH_SETTING);
|
ALL_SETTINGS.addAll(TRANSPORT_SSL.getAllSettings());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,12 +39,6 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class HttpClient extends AbstractComponent {
|
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 HttpAuthRegistry httpAuthRegistry;
|
||||||
private final TimeValue defaultConnectionTimeout;
|
private final TimeValue defaultConnectionTimeout;
|
||||||
private final TimeValue defaultReadTimeout;
|
private final TimeValue defaultReadTimeout;
|
||||||
@ -55,21 +49,27 @@ public class HttpClient extends AbstractComponent {
|
|||||||
public HttpClient(Settings settings, HttpAuthRegistry httpAuthRegistry, SSLService sslService) {
|
public HttpClient(Settings settings, HttpAuthRegistry httpAuthRegistry, SSLService sslService) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.httpAuthRegistry = httpAuthRegistry;
|
this.httpAuthRegistry = httpAuthRegistry;
|
||||||
this.defaultConnectionTimeout = settings.getAsTime("xpack.http.default_connection_timeout", TimeValue.timeValueSeconds(10));
|
this.defaultConnectionTimeout = HttpSettings.CONNECTION_TIMEOUT.get(settings);
|
||||||
this.defaultReadTimeout = settings.getAsTime("xpack.http.default_read_timeout", TimeValue.timeValueSeconds(10));
|
this.defaultReadTimeout = HttpSettings.READ_TIMEOUT.get(settings);
|
||||||
Integer proxyPort = settings.getAsInt(SETTINGS_PROXY_PORT, null);
|
|
||||||
String proxyHost = settings.get(SETTINGS_PROXY_HOST, null);
|
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)) {
|
if (proxyPort != null && Strings.hasText(proxyHost)) {
|
||||||
this.proxy = new HttpProxy(proxyHost, proxyPort);
|
this.proxy = new HttpProxy(proxyHost, proxyPort);
|
||||||
logger.info("Using default proxy for http input and slack/hipchat/pagerduty/webhook actions [{}:{}]", 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) {
|
} else if (proxyPort == null && Strings.hasText(proxyHost) == false) {
|
||||||
this.proxy = HttpProxy.NO_PROXY;
|
this.proxy = HttpProxy.NO_PROXY;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("HTTP Proxy requires both settings: [" + SETTINGS_PROXY_HOST + "] and [" +
|
throw new IllegalArgumentException("HTTP Proxy requires both settings: [" + HttpSettings.PROXY_HOST_KEY + "] and [" +
|
||||||
SETTINGS_PROXY_PORT + "]");
|
HttpSettings.PROXY_PORT_KEY + "]");
|
||||||
}
|
}
|
||||||
Settings sslSettings = settings.getByPrefix(SETTINGS_SSL_PREFIX);
|
Settings sslSettings = settings.getByPrefix(HttpSettings.SSL_KEY_PREFIX);
|
||||||
this.sslSocketFactory = sslService.sslSocketFactory(settings.getByPrefix(SETTINGS_SSL_PREFIX));
|
this.sslSocketFactory = sslService.sslSocketFactory(settings.getByPrefix(HttpSettings.SSL_KEY_PREFIX));
|
||||||
this.isHostnameVerificationEnabled = sslService.getVerificationMode(sslSettings, Settings.EMPTY).isHostnameVerificationEnabled();
|
this.isHostnameVerificationEnabled = sslService.getVerificationMode(sslSettings, Settings.EMPTY).isHostnameVerificationEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
@ -9,10 +9,13 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
|
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
|
||||||
import org.elasticsearch.xpack.security.authc.Realm;
|
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();
|
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.
|
* Returns a handler for authentication failures, or null to use the default handler.
|
||||||
*
|
*
|
||||||
|
@ -13,6 +13,7 @@ import org.elasticsearch.action.support.ActionFilter;
|
|||||||
import org.elasticsearch.action.support.DestructiveOperations;
|
import org.elasticsearch.action.support.DestructiveOperations;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
import org.elasticsearch.common.Booleans;
|
import org.elasticsearch.common.Booleans;
|
||||||
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.inject.Module;
|
import org.elasticsearch.common.inject.Module;
|
||||||
import org.elasticsearch.common.inject.util.Providers;
|
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.XPackPlugin;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.extensions.XPackExtension;
|
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.SecurityActionModule;
|
||||||
import org.elasticsearch.xpack.security.action.filter.SecurityActionFilter;
|
import org.elasticsearch.xpack.security.action.filter.SecurityActionFilter;
|
||||||
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheAction;
|
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.AuthenticationFailureHandler;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
|
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.Realm;
|
||||||
|
import org.elasticsearch.xpack.security.authc.RealmSettings;
|
||||||
import org.elasticsearch.xpack.security.authc.Realms;
|
import org.elasticsearch.xpack.security.authc.Realms;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
|
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
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.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.SecuredString;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
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 AnonymousUser anonymousUser = new AnonymousUser(settings);
|
||||||
final ReservedRealm reservedRealm = new ReservedRealm(env, settings, nativeUsersStore, anonymousUser);
|
final ReservedRealm reservedRealm = new ReservedRealm(env, settings, nativeUsersStore, anonymousUser);
|
||||||
Map<String, Realm.Factory> realmFactories = new HashMap<>();
|
Map<String, Realm.Factory> realmFactories = new HashMap<>();
|
||||||
realmFactories.put(FileRealm.TYPE, config -> new FileRealm(config, resourceWatcherService));
|
realmFactories.putAll(InternalRealms.getFactories(threadPool, resourceWatcherService, sslService, nativeUsersStore));
|
||||||
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));
|
|
||||||
for (XPackExtension extension : extensions) {
|
for (XPackExtension extension : extensions) {
|
||||||
Map<String, Realm.Factory> newRealms = extension.getRealms(resourceWatcherService);
|
Map<String, Realm.Factory> newRealms = extension.getRealms(resourceWatcherService);
|
||||||
for (Map.Entry<String, Realm.Factory> entry : newRealms.entrySet()) {
|
for (Map.Entry<String, Realm.Factory> entry : newRealms.entrySet()) {
|
||||||
@ -380,7 +375,10 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
|||||||
return settingsBuilder.build();
|
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<>();
|
List<Setting<?>> settingsList = new ArrayList<>();
|
||||||
// always register for both client and node modes
|
// always register for both client and node modes
|
||||||
settingsList.add(USER_SETTING);
|
settingsList.add(USER_SETTING);
|
||||||
@ -401,7 +399,7 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
|||||||
|
|
||||||
// authentication settings
|
// authentication settings
|
||||||
AnonymousUser.addSettings(settingsList);
|
AnonymousUser.addSettings(settingsList);
|
||||||
Realms.addSettings(settingsList);
|
RealmSettings.addSettings(settingsList, extensionsService == null ? null : extensionsService.getExtensions());
|
||||||
NativeRolesStore.addSettings(settingsList);
|
NativeRolesStore.addSettings(settingsList);
|
||||||
AuthenticationService.addSettings(settingsList);
|
AuthenticationService.addSettings(settingsList);
|
||||||
AuthorizationService.addSettings(settingsList);
|
AuthorizationService.addSettings(settingsList);
|
||||||
|
@ -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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,8 +29,8 @@ public class RealmConfig {
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.globalSettings = globalSettings;
|
this.globalSettings = globalSettings;
|
||||||
this.env = env;
|
this.env = env;
|
||||||
enabled = settings.getAsBoolean("enabled", true);
|
enabled = RealmSettings.ENABLED_SETTING.get(settings);
|
||||||
order = settings.getAsInt("order", Integer.MAX_VALUE);
|
order = RealmSettings.ORDER_SETTING.get(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String name() {
|
public String name() {
|
||||||
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
@ -18,8 +18,6 @@ import java.util.Set;
|
|||||||
|
|
||||||
import org.elasticsearch.common.collect.MapBuilder;
|
import org.elasticsearch.common.collect.MapBuilder;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
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.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
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.NativeRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.file.FileRealm;
|
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)
|
* Serves as a realms registry (also responsible for ordering the realms appropriately)
|
||||||
*/
|
*/
|
||||||
public class Realms extends AbstractComponent implements Iterable<Realm> {
|
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 Environment env;
|
||||||
private final Map<String, Realm.Factory> factories;
|
private final Map<String, Realm.Factory> factories;
|
||||||
private final XPackLicenseState licenseState;
|
private final XPackLicenseState licenseState;
|
||||||
@ -68,7 +58,7 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
|
|||||||
List<Realm> nativeRealms = new ArrayList<>();
|
List<Realm> nativeRealms = new ArrayList<>();
|
||||||
for (Realm realm : realms) {
|
for (Realm realm : realms) {
|
||||||
// don't add the reserved realm here otherwise we end up with only this realm...
|
// 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);
|
internalRealms.add(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +132,7 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<Realm> initRealms() throws Exception {
|
protected List<Realm> initRealms() throws Exception {
|
||||||
Settings realmsSettings = REALMS_GROUPS_SETTINGS.get(settings);
|
Settings realmsSettings = RealmSettings.get(settings);
|
||||||
Set<String> internalTypes = new HashSet<>();
|
Set<String> internalTypes = new HashSet<>();
|
||||||
List<Realm> realms = new ArrayList<>();
|
List<Realm> realms = new ArrayList<>();
|
||||||
for (String name : realmsSettings.names()) {
|
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) {
|
private static void combineMaps(Map<String, Object> mapA, Map<String, Object> mapB) {
|
||||||
for (Entry<String, Object> entry : mapB.entrySet()) {
|
for (Entry<String, Object> entry : mapB.entrySet()) {
|
||||||
mapA.compute(entry.getKey(), (key, value) -> {
|
mapA.compute(entry.getKey(), (key, value) -> {
|
||||||
@ -274,9 +260,10 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
|
|||||||
case NATIVE:
|
case NATIVE:
|
||||||
return FileRealm.TYPE.equals(type) || NativeRealm.TYPE.equals(type);
|
return FileRealm.TYPE.equals(type) || NativeRealm.TYPE.equals(type);
|
||||||
case DEFAULT:
|
case DEFAULT:
|
||||||
return INTERNAL_REALM_TYPES.contains(type);
|
return InternalRealms.isInternalRealm(type, true);
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("unknown enabled realm type [" + enabledRealmType + "]");
|
throw new IllegalStateException("unknown enabled realm type [" + enabledRealmType + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,15 @@
|
|||||||
package org.elasticsearch.xpack.security.authc.esnative;
|
package org.elasticsearch.xpack.security.authc.esnative;
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
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
|
* 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) {
|
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<User> listener) {
|
||||||
userStore.verifyPassword(token.principal(), token.credentials(), 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.authc.file;
|
package org.elasticsearch.xpack.security.authc.file;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||||
@ -61,4 +64,11 @@ public class FileRealm extends CachingUsernamePasswordRealm {
|
|||||||
stats.put("size", userPasswdStore.usersCount());
|
stats.put("size", userPasswdStore.usersCount());
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link Setting setting configuration} for this realm type
|
||||||
|
*/
|
||||||
|
public static Set<Setting<?>> getSettings() {
|
||||||
|
return CachingUsernamePasswordRealm.getCachingSettings();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import org.elasticsearch.ElasticsearchSecurityException;
|
|||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.common.cache.Cache;
|
import org.elasticsearch.common.cache.Cache;
|
||||||
import org.elasticsearch.common.cache.CacheBuilder;
|
import org.elasticsearch.common.cache.CacheBuilder;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
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.security.authc.support.SecuredString;
|
||||||
import org.elasticsearch.xpack.ssl.SSLService;
|
import org.elasticsearch.xpack.ssl.SSLService;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
|
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
|
||||||
@ -107,6 +110,18 @@ class ActiveDirectorySessionFactory extends SessionFactory {
|
|||||||
return "DC=" + domain.replace(".", ",DC=");
|
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) {
|
ADAuthenticator getADAuthenticator(String username) {
|
||||||
if (username.indexOf('\\') > 0) {
|
if (username.indexOf('\\') > 0) {
|
||||||
return downLevelADAuthenticator;
|
return downLevelADAuthenticator;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.authc.ldap;
|
package org.elasticsearch.xpack.security.authc.ldap;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
@ -13,12 +14,13 @@ import com.unboundid.ldap.sdk.LDAPException;
|
|||||||
import org.apache.lucene.util.IOUtils;
|
import org.apache.lucene.util.IOUtils;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.common.Strings;
|
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.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapLoadBalancing;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapLoadBalancing;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
|
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.ldap.support.SessionFactory;
|
||||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||||
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
|
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
|
||||||
@ -59,13 +61,27 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
|
|||||||
sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
|
sessionFactory = new ActiveDirectorySessionFactory(config, sslService);
|
||||||
} else {
|
} else {
|
||||||
assert LDAP_TYPE.equals(type) : "type [" + type + "] is unknown. expected one of [" + AD_TYPE + ", " + LDAP_TYPE + "]";
|
assert LDAP_TYPE.equals(type) : "type [" + type + "] is unknown. expected one of [" + AD_TYPE + ", " + LDAP_TYPE + "]";
|
||||||
Settings searchSettings = userSearchSettings(config);
|
final boolean hasSearchSettings = LdapUserSearchSessionFactory.hasUserSearchSettings(config);
|
||||||
if (searchSettings.names().isEmpty()) {
|
final boolean hasTemplates = LdapSessionFactory.USER_DN_TEMPLATES_SETTING.exists(config.settings());
|
||||||
sessionFactory = new LdapSessionFactory(config, sslService);
|
if (hasSearchSettings == false) {
|
||||||
} else if (config.settings().getAsArray(LdapSessionFactory.USER_DN_TEMPLATES_SETTING).length > 0) {
|
if(hasTemplates == false) {
|
||||||
throw new IllegalArgumentException("settings were found for both user search and user template modes of operation. " +
|
throw new IllegalArgumentException("settings were not found for either user search [" +
|
||||||
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
|
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.");
|
"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 {
|
} else {
|
||||||
sessionFactory = new LdapUserSearchSessionFactory(config, sslService);
|
sessionFactory = new LdapUserSearchSessionFactory(config, sslService);
|
||||||
}
|
}
|
||||||
@ -73,8 +89,22 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
|
|||||||
return sessionFactory;
|
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();
|
Map<String, Object> usage = super.usageStats();
|
||||||
usage.put("load_balance_type", LdapLoadBalancing.resolve(config.settings()).toString());
|
usage.put("load_balance_type", LdapLoadBalancing.resolve(config.settings()).toString());
|
||||||
usage.put("ssl", sessionFactory.isSslUsed());
|
usage.put("ssl", sessionFactory.isSslUsed());
|
||||||
usage.put("user_search", userSearchSettings(config).isEmpty() == false);
|
usage.put("user_search", LdapUserSearchSessionFactory.hasUserSearchSettings(config));
|
||||||
return usage;
|
return usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,11 @@ import org.apache.logging.log4j.message.ParameterizedMessage;
|
|||||||
import org.apache.logging.log4j.util.Supplier;
|
import org.apache.logging.log4j.util.Supplier;
|
||||||
import org.apache.lucene.util.IOUtils;
|
import org.apache.lucene.util.IOUtils;
|
||||||
import org.elasticsearch.action.ActionListener;
|
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.settings.Settings;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
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;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
||||||
@ -23,7 +26,12 @@ import org.elasticsearch.xpack.ssl.SSLService;
|
|||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.escapedRDNValue;
|
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 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 String[] userDnTemplates;
|
||||||
private final GroupsResolver groupResolver;
|
private final GroupsResolver groupResolver;
|
||||||
@ -43,9 +52,10 @@ public class LdapSessionFactory extends SessionFactory {
|
|||||||
public LdapSessionFactory(RealmConfig config, SSLService sslService) {
|
public LdapSessionFactory(RealmConfig config, SSLService sslService) {
|
||||||
super(config, sslService);
|
super(config, sslService);
|
||||||
Settings settings = config.settings();
|
Settings settings = config.settings();
|
||||||
userDnTemplates = settings.getAsArray(USER_DN_TEMPLATES_SETTING);
|
userDnTemplates = USER_DN_TEMPLATES_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
|
||||||
if (userDnTemplates == null) {
|
if (userDnTemplates.length == 0) {
|
||||||
throw new IllegalArgumentException("missing required LDAP setting [" + USER_DN_TEMPLATES_SETTING + "]");
|
throw new IllegalArgumentException("missing required LDAP setting ["
|
||||||
|
+ RealmSettings.getFullSettingKey(config, USER_DN_TEMPLATES_SETTING) + "]");
|
||||||
}
|
}
|
||||||
groupResolver = groupResolver(settings);
|
groupResolver = groupResolver(settings);
|
||||||
}
|
}
|
||||||
@ -116,10 +126,16 @@ public class LdapSessionFactory extends SessionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static GroupsResolver groupResolver(Settings settings) {
|
static GroupsResolver groupResolver(Settings settings) {
|
||||||
Settings searchSettings = settings.getAsSettings("group_search");
|
if (SearchGroupsResolver.BASE_DN.exists(settings)) {
|
||||||
if (!searchSettings.names().isEmpty()) {
|
return new SearchGroupsResolver(settings);
|
||||||
return new SearchGroupsResolver(searchSettings);
|
|
||||||
}
|
}
|
||||||
return new UserAttributeGroupsResolver(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,11 @@ import com.unboundid.ldap.sdk.SimpleBindRequest;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.lucene.util.IOUtils;
|
import org.apache.lucene.util.IOUtils;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
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.LdapSearchScope;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
|
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.LdapSession.GroupsResolver;
|
||||||
@ -30,6 +32,11 @@ import org.elasticsearch.xpack.ssl.SSLService;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
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.createEqualityFilter;
|
||||||
import static com.unboundid.ldap.sdk.Filter.encodeValue;
|
import static com.unboundid.ldap.sdk.Filter.encodeValue;
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
|
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 String DEFAULT_USERNAME_ATTRIBUTE = "uid";
|
||||||
static final TimeValue DEFAULT_HEALTH_CHECK_INTERVAL = TimeValue.timeValueSeconds(60L);
|
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 String userSearchBaseDn;
|
||||||
private final LdapSearchScope scope;
|
private final LdapSearchScope scope;
|
||||||
private final String userAttribute;
|
private final String userAttribute;
|
||||||
@ -53,14 +84,15 @@ class LdapUserSearchSessionFactory extends SessionFactory {
|
|||||||
LdapUserSearchSessionFactory(RealmConfig config, SSLService sslService) throws LDAPException {
|
LdapUserSearchSessionFactory(RealmConfig config, SSLService sslService) throws LDAPException {
|
||||||
super(config, sslService);
|
super(config, sslService);
|
||||||
Settings settings = config.settings();
|
Settings settings = config.settings();
|
||||||
userSearchBaseDn = settings.get("user_search.base_dn");
|
if (SEARCH_BASE_DN.exists(settings)) {
|
||||||
if (userSearchBaseDn == null) {
|
userSearchBaseDn = SEARCH_BASE_DN.get(settings);
|
||||||
throw new IllegalArgumentException("user_search base_dn must be specified");
|
} else {
|
||||||
|
throw new IllegalArgumentException("[" + RealmSettings.getFullSettingKey(config, SEARCH_BASE_DN) + "] must be specified");
|
||||||
}
|
}
|
||||||
scope = LdapSearchScope.resolve(settings.get("user_search.scope"), LdapSearchScope.SUB_TREE);
|
scope = SEARCH_SCOPE.get(settings);
|
||||||
userAttribute = settings.get("user_search.attribute", DEFAULT_USERNAME_ATTRIBUTE);
|
userAttribute = SEARCH_ATTRIBUTE.get(settings);
|
||||||
groupResolver = groupResolver(config.settings());
|
groupResolver = groupResolver(settings);
|
||||||
useConnectionPool = settings.getAsBoolean("user_search.pool.enabled", true);
|
useConnectionPool = POOL_ENABLED.get(settings);
|
||||||
if (useConnectionPool) {
|
if (useConnectionPool) {
|
||||||
connectionPool = createConnectionPool(config, serverSet, timeout, logger);
|
connectionPool = createConnectionPool(config, serverSet, timeout, logger);
|
||||||
} else {
|
} else {
|
||||||
@ -72,17 +104,16 @@ class LdapUserSearchSessionFactory extends SessionFactory {
|
|||||||
throws LDAPException {
|
throws LDAPException {
|
||||||
Settings settings = config.settings();
|
Settings settings = config.settings();
|
||||||
SimpleBindRequest bindRequest = bindRequest(settings);
|
SimpleBindRequest bindRequest = bindRequest(settings);
|
||||||
final int initialSize = settings.getAsInt("user_search.pool.initial_size", DEFAULT_CONNECTION_POOL_INITIAL_SIZE);
|
final int initialSize = POOL_INITIAL_SIZE.get(settings);
|
||||||
final int size = settings.getAsInt("user_search.pool.size", DEFAULT_CONNECTION_POOL_SIZE);
|
final int size = POOL_SIZE.get(settings);
|
||||||
LDAPConnectionPool pool = null;
|
LDAPConnectionPool pool = null;
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
pool = new LDAPConnectionPool(serverSet, bindRequest, initialSize, size);
|
pool = new LDAPConnectionPool(serverSet, bindRequest, initialSize, size);
|
||||||
pool.setRetryFailedOperationsDueToInvalidConnections(true);
|
pool.setRetryFailedOperationsDueToInvalidConnections(true);
|
||||||
if (settings.getAsBoolean("user_search.pool.health_check.enabled", true)) {
|
if (HEALTH_CHECK_ENABLED.get(settings)) {
|
||||||
String entryDn = settings.get("user_search.pool.health_check.dn", (bindRequest == null) ? null : bindRequest.getBindDN());
|
String entryDn = HEALTH_CHECK_DN.get(settings).orElseGet(() -> bindRequest == null ? null : bindRequest.getBindDN());
|
||||||
final long healthCheckInterval =
|
final long healthCheckInterval = HEALTH_CHECK_INTERVAL.get(settings).millis();
|
||||||
settings.getAsTime("user_search.pool.health_check.interval", DEFAULT_HEALTH_CHECK_INTERVAL).millis();
|
|
||||||
if (entryDn != null) {
|
if (entryDn != null) {
|
||||||
// Checks the status of the LDAP connection at a specified interval in the background. We do not check on
|
// 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
|
// 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.setHealthCheck(healthCheck);
|
||||||
pool.setHealthCheckIntervalMillis(healthCheckInterval);
|
pool.setHealthCheckIntervalMillis(healthCheckInterval);
|
||||||
} else {
|
} 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");
|
"ldap query will be run as a health check");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,13 +141,16 @@ class LdapUserSearchSessionFactory extends SessionFactory {
|
|||||||
|
|
||||||
static SimpleBindRequest bindRequest(Settings settings) {
|
static SimpleBindRequest bindRequest(Settings settings) {
|
||||||
SimpleBindRequest request = null;
|
SimpleBindRequest request = null;
|
||||||
String bindDn = settings.get("bind_dn");
|
if (BIND_DN.exists(settings)) {
|
||||||
if (bindDn != null) {
|
request = new SimpleBindRequest(BIND_DN.get(settings), BIND_PASSWORD.get(settings));
|
||||||
request = new SimpleBindRequest(bindDn, settings.get("bind_password"));
|
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasUserSearchSettings(RealmConfig config) {
|
||||||
|
return config.settings().getByPrefix("user_search.").isEmpty() == false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void session(String user, SecuredString password, ActionListener<LdapSession> listener) {
|
public void session(String user, SecuredString password, ActionListener<LdapSession> listener) {
|
||||||
if (useConnectionPool) {
|
if (useConnectionPool) {
|
||||||
@ -268,10 +303,30 @@ class LdapUserSearchSessionFactory extends SessionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static GroupsResolver groupResolver(Settings settings) {
|
static GroupsResolver groupResolver(Settings settings) {
|
||||||
Settings searchSettings = settings.getAsSettings("group_search");
|
if (SearchGroupsResolver.BASE_DN.exists(settings)) {
|
||||||
if (!searchSettings.names().isEmpty()) {
|
return new SearchGroupsResolver(settings);
|
||||||
return new SearchGroupsResolver(searchSettings);
|
|
||||||
}
|
}
|
||||||
return new UserAttributeGroupsResolver(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import com.unboundid.ldap.sdk.SearchRequest;
|
|||||||
import com.unboundid.ldap.sdk.SearchScope;
|
import com.unboundid.ldap.sdk.SearchScope;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.action.ActionListener;
|
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.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
|
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.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
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.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.createFilter;
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.search;
|
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))" +
|
"(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames)(objectclass=group)(objectclass=posixGroup))" +
|
||||||
"(|(uniqueMember={0})(member={0})(memberUid={0})))";
|
"(|(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 baseDn;
|
||||||
private final String filter;
|
private final String filter;
|
||||||
private final String userAttribute;
|
private final String userAttribute;
|
||||||
private final LdapSearchScope scope;
|
private final LdapSearchScope scope;
|
||||||
|
|
||||||
SearchGroupsResolver(Settings settings) {
|
SearchGroupsResolver(Settings settings) {
|
||||||
baseDn = settings.get("base_dn");
|
if (BASE_DN.exists(settings)) {
|
||||||
if (baseDn == null) {
|
baseDn = BASE_DN.get(settings);
|
||||||
|
} else {
|
||||||
throw new IllegalArgumentException("base_dn must be specified");
|
throw new IllegalArgumentException("base_dn must be specified");
|
||||||
}
|
}
|
||||||
filter = settings.get("filter", GROUP_SEARCH_DEFAULT_FILTER);
|
filter = FILTER.get(settings);
|
||||||
userAttribute = settings.get("user_attribute");
|
userAttribute = USER_ATTRIBUTE.get(settings);
|
||||||
scope = LdapSearchScope.resolve(settings.get("scope"), LdapSearchScope.SUB_TREE);
|
scope = SCOPE.get(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,7 +90,7 @@ class SearchGroupsResolver implements GroupsResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String[] attributes() {
|
public String[] attributes() {
|
||||||
if (userAttribute != null) {
|
if (Strings.hasLength(userAttribute)) {
|
||||||
return new String[] { userAttribute };
|
return new String[] { userAttribute };
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -83,7 +98,7 @@ class SearchGroupsResolver implements GroupsResolver {
|
|||||||
|
|
||||||
private void getUserId(String dn, Collection<Attribute> attributes, LDAPInterface connection, TimeValue timeout,
|
private void getUserId(String dn, Collection<Attribute> attributes, LDAPInterface connection, TimeValue timeout,
|
||||||
ActionListener<String> listener) {
|
ActionListener<String> listener) {
|
||||||
if (userAttribute == null) {
|
if (isNullOrEmpty(userAttribute)) {
|
||||||
listener.onResponse(dn);
|
listener.onResponse(dn);
|
||||||
} else if (attributes != null) {
|
} else if (attributes != null) {
|
||||||
final String value = attributes.stream().filter((attribute) -> attribute.getName().equals(userAttribute))
|
final String value = attributes.stream().filter((attribute) -> attribute.getName().equals(userAttribute))
|
||||||
@ -107,4 +122,13 @@ class SearchGroupsResolver implements GroupsResolver {
|
|||||||
}, listener::onFailure),
|
}, listener::onFailure),
|
||||||
userAttribute);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import com.unboundid.ldap.sdk.LDAPInterface;
|
|||||||
import com.unboundid.ldap.sdk.SearchScope;
|
import com.unboundid.ldap.sdk.SearchScope;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
|
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.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
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.OBJECT_CLASS_PRESENCE_FILTER;
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.searchForEntry;
|
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 {
|
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;
|
private final String attribute;
|
||||||
|
|
||||||
UserAttributeGroupsResolver(Settings settings) {
|
UserAttributeGroupsResolver(Settings settings) {
|
||||||
this(settings.get("user_group_attribute", "memberOf"));
|
this(ATTRIBUTE.get(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserAttributeGroupsResolver(String attribute) {
|
private UserAttributeGroupsResolver(String attribute) {
|
||||||
@ -62,4 +67,8 @@ class UserAttributeGroupsResolver implements GroupsResolver {
|
|||||||
public String[] attributes() {
|
public String[] attributes() {
|
||||||
return new String[] { attribute };
|
return new String[] { attribute };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Set<Setting<?>> getSettings() {
|
||||||
|
return Collections.singleton(ATTRIBUTE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,14 @@ import com.unboundid.ldap.sdk.RoundRobinServerSet;
|
|||||||
import com.unboundid.ldap.sdk.ServerSet;
|
import com.unboundid.ldap.sdk.ServerSet;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.network.InetAddresses;
|
import org.elasticsearch.common.network.InetAddresses;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
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.
|
* 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])) {
|
if (InetAddresses.isInetAddress(addresses[0])) {
|
||||||
throw new IllegalArgumentException(toString() + " can only be used with a DNS name");
|
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],
|
return new RoundRobinDNSServerSet(addresses[0], ports[0],
|
||||||
RoundRobinDNSServerSet.AddressSelectionMode.ROUND_ROBIN, dnsTtl.millis(), null, socketFactory, options);
|
RoundRobinDNSServerSet.AddressSelectionMode.ROUND_ROBIN, dnsTtl.millis(), null, socketFactory, options);
|
||||||
}
|
}
|
||||||
@ -67,7 +70,7 @@ public enum LdapLoadBalancing {
|
|||||||
if (InetAddresses.isInetAddress(addresses[0])) {
|
if (InetAddresses.isInetAddress(addresses[0])) {
|
||||||
throw new IllegalArgumentException(toString() + " can only be used with a DNS name");
|
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],
|
return new RoundRobinDNSServerSet(addresses[0], ports[0],
|
||||||
RoundRobinDNSServerSet.AddressSelectionMode.FAILOVER, dnsTtl.millis(), null, socketFactory, options);
|
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_SETTINGS = "load_balance";
|
||||||
public static final String LOAD_BALANCE_TYPE_SETTING = "type";
|
public static final String LOAD_BALANCE_TYPE_SETTING = "type";
|
||||||
public static final String LOAD_BALANCE_TYPE_DEFAULT = LdapLoadBalancing.FAILOVER.toString();
|
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,
|
abstract ServerSet buildServerSet(String[] addresses, int[] ports, Settings settings, @Nullable SocketFactory socketFactory,
|
||||||
@Nullable LDAPConnectionOptions options);
|
@Nullable LDAPConnectionOptions options);
|
||||||
@ -101,4 +106,11 @@ public enum LdapLoadBalancing {
|
|||||||
Settings loadBalanceSettings = settings.getAsSettings(LOAD_BALANCE_SETTINGS);
|
Settings loadBalanceSettings = settings.getAsSettings(LOAD_BALANCE_SETTINGS);
|
||||||
return loadBalancing.buildServerSet(addresses, ports, loadBalanceSettings, socketFactory, options);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,20 @@ import com.unboundid.util.ssl.HostNameSSLSocketVerifier;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||||
|
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
|
||||||
import org.elasticsearch.xpack.ssl.SSLService;
|
import org.elasticsearch.xpack.ssl.SSLService;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import java.util.Arrays;
|
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;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,6 +155,19 @@ public abstract class SessionFactory {
|
|||||||
return sslUsed;
|
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 {
|
public static class LDAPServers {
|
||||||
|
|
||||||
private final String[] addresses;
|
private final String[] addresses;
|
||||||
|
@ -11,42 +11,48 @@ import org.apache.logging.log4j.util.Supplier;
|
|||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
import org.elasticsearch.xpack.security.Security;
|
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.security.transport.netty4.SecurityNetty4Transport;
|
||||||
import org.elasticsearch.xpack.ssl.CertUtils;
|
import org.elasticsearch.xpack.ssl.CertUtils;
|
||||||
|
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
|
||||||
import org.elasticsearch.xpack.ssl.SSLService;
|
import org.elasticsearch.xpack.ssl.SSLService;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
||||||
import org.elasticsearch.xpack.security.authc.Realm;
|
import org.elasticsearch.xpack.security.authc.Realm;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
|
import org.elasticsearch.xpack.security.authc.RealmSettings;
|
||||||
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
|
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
|
||||||
|
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
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.HTTP_SSL_ENABLED;
|
||||||
import static org.elasticsearch.xpack.XPackSettings.TRANSPORT_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 class PkiRealm extends Realm {
|
||||||
|
|
||||||
public static final String PKI_CERT_HEADER_NAME = "__SECURITY_CLIENT_CERTIFICATE";
|
public static final String PKI_CERT_HEADER_NAME = "__SECURITY_CLIENT_CERTIFICATE";
|
||||||
public static final String TYPE = "pki";
|
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
|
// For client based cert validation, the auth type must be specified but UNKNOWN is an acceptable value
|
||||||
public static final String AUTH_TYPE = "UNKNOWN";
|
public static final String AUTH_TYPE = "UNKNOWN";
|
||||||
@ -64,8 +70,7 @@ public class PkiRealm extends Realm {
|
|||||||
PkiRealm(RealmConfig config, DnRoleMapper roleMapper, SSLService sslService) {
|
PkiRealm(RealmConfig config, DnRoleMapper roleMapper, SSLService sslService) {
|
||||||
super(TYPE, config);
|
super(TYPE, config);
|
||||||
this.trustManager = trustManagers(config);
|
this.trustManager = trustManagers(config);
|
||||||
this.principalPattern = Pattern.compile(config.settings().get("username_pattern", DEFAULT_USERNAME_PATTERN),
|
this.principalPattern = USERNAME_PATTERN_SETTING.get(config.settings());
|
||||||
Pattern.CASE_INSENSITIVE);
|
|
||||||
this.roleMapper = roleMapper;
|
this.roleMapper = roleMapper;
|
||||||
checkSSLEnabled(config, sslService);
|
checkSSLEnabled(config, sslService);
|
||||||
}
|
}
|
||||||
@ -149,31 +154,26 @@ public class PkiRealm extends Realm {
|
|||||||
static X509TrustManager trustManagers(RealmConfig realmConfig) {
|
static X509TrustManager trustManagers(RealmConfig realmConfig) {
|
||||||
final Settings settings = realmConfig.settings();
|
final Settings settings = realmConfig.settings();
|
||||||
final Environment env = realmConfig.env();
|
final Environment env = realmConfig.env();
|
||||||
String[] certificateAuthorities = settings.getAsArray("certificate_authorities", null);
|
String[] certificateAuthorities = settings.getAsArray(SSL_SETTINGS.caPaths.getKey(), null);
|
||||||
String truststorePath = settings.get("truststore.path");
|
String truststorePath = SSL_SETTINGS.truststorePath.get(settings).orElse(null);
|
||||||
if (truststorePath == null && certificateAuthorities == null) {
|
if (truststorePath == null && certificateAuthorities == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (truststorePath != null && certificateAuthorities != null) {
|
} else if (truststorePath != null && certificateAuthorities != null) {
|
||||||
final String settingPrefix = Realms.REALMS_GROUPS_SETTINGS.getKey() + realmConfig.name() + ".";
|
final String pathKey = RealmSettings.getFullSettingKey(realmConfig, SSL_SETTINGS.truststorePath);
|
||||||
throw new IllegalArgumentException("[" + settingPrefix + "truststore.path] and [" + settingPrefix + "certificate_authorities]" +
|
final String caKey = RealmSettings.getFullSettingKey(realmConfig, SSL_SETTINGS.caPaths);
|
||||||
" cannot be used at the same time");
|
throw new IllegalArgumentException("[" + pathKey + "] and [" + caKey + "] cannot be used at the same time");
|
||||||
} else if (truststorePath != null) {
|
} else if (truststorePath != null) {
|
||||||
return trustManagersFromTruststore(realmConfig);
|
return trustManagersFromTruststore(truststorePath, realmConfig);
|
||||||
}
|
}
|
||||||
return trustManagersFromCAs(settings, env);
|
return trustManagersFromCAs(settings, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static X509TrustManager trustManagersFromTruststore(RealmConfig realmConfig) {
|
private static X509TrustManager trustManagersFromTruststore(String truststorePath, RealmConfig realmConfig) {
|
||||||
final Settings settings = realmConfig.settings();
|
final Settings settings = realmConfig.settings();
|
||||||
String truststorePath = settings.get("truststore.path");
|
String password = SSL_SETTINGS.truststorePassword.get(settings).orElseThrow(() -> new IllegalArgumentException(
|
||||||
String password = settings.get("truststore.password");
|
"[" + RealmSettings.getFullSettingKey(realmConfig, SSL_SETTINGS.truststorePassword) + "] is not configured"
|
||||||
if (password == null) {
|
));
|
||||||
final String settingPrefix = Realms.REALMS_GROUPS_SETTINGS.getKey() + realmConfig.name() + ".";
|
String trustStoreAlgorithm = SSL_SETTINGS.truststoreAlgorithm.get(settings);
|
||||||
throw new IllegalArgumentException("[" + settingPrefix + "truststore.password] is not configured");
|
|
||||||
}
|
|
||||||
|
|
||||||
String trustStoreAlgorithm = settings.get("truststore.algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm",
|
|
||||||
TrustManagerFactory.getDefaultAlgorithm()));
|
|
||||||
try {
|
try {
|
||||||
return CertUtils.trustManager(truststorePath, password, trustStoreAlgorithm, realmConfig.env());
|
return CertUtils.trustManager(truststorePath, password, trustStoreAlgorithm, realmConfig.env());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -182,7 +182,7 @@ public class PkiRealm extends Realm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static X509TrustManager trustManagersFromCAs(Settings settings, Environment env) {
|
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;
|
assert certificateAuthorities != null;
|
||||||
try {
|
try {
|
||||||
Certificate[] certificates = CertUtils.readCertificates(Arrays.asList(certificateAuthorities), env);
|
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 " +
|
throw new IllegalStateException("PKI realm [" + config.name() + "] is enabled but cannot be used as neither HTTP or Transport " +
|
||||||
"has SSL with client authentication enabled");
|
"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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,34 +8,41 @@ package org.elasticsearch.xpack.security.authc.support;
|
|||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.common.cache.Cache;
|
import org.elasticsearch.common.cache.Cache;
|
||||||
import org.elasticsearch.common.cache.CacheBuilder;
|
import org.elasticsearch.common.cache.CacheBuilder;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
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.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm implements CachingRealm {
|
public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm implements CachingRealm {
|
||||||
|
|
||||||
public static final String CACHE_HASH_ALGO_SETTING = "cache.hash_algo";
|
public static final Setting<String> CACHE_HASH_ALGO_SETTING = Setting.simpleString("cache.hash_algo", Setting.Property.NodeScope);
|
||||||
public static final String CACHE_TTL_SETTING = "cache.ttl";
|
|
||||||
public static final String CACHE_MAX_USERS_SETTING = "cache.max_users";
|
|
||||||
|
|
||||||
private static final TimeValue DEFAULT_TTL = TimeValue.timeValueMinutes(20);
|
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;
|
private final Cache<String, UserWithHash> cache;
|
||||||
final Hasher hasher;
|
final Hasher hasher;
|
||||||
|
|
||||||
protected CachingUsernamePasswordRealm(String type, RealmConfig config) {
|
protected CachingUsernamePasswordRealm(String type, RealmConfig config) {
|
||||||
super(type, config);
|
super(type, config);
|
||||||
hasher = Hasher.resolve(config.settings().get(CACHE_HASH_ALGO_SETTING, null), Hasher.SSHA256);
|
hasher = Hasher.resolve(CACHE_HASH_ALGO_SETTING.get(config.settings()), Hasher.SSHA256);
|
||||||
TimeValue ttl = config.settings().getAsTime(CACHE_TTL_SETTING, DEFAULT_TTL);
|
TimeValue ttl = CACHE_TTL_SETTING.get(config.settings());
|
||||||
if (ttl.getNanos() > 0) {
|
if (ttl.getNanos() > 0) {
|
||||||
cache = CacheBuilder.<String, UserWithHash>builder()
|
cache = CacheBuilder.<String, UserWithHash>builder()
|
||||||
.setExpireAfterAccess(ttl)
|
.setExpireAfterAccess(ttl)
|
||||||
.setMaximumWeight(config.settings().getAsInt(CACHE_MAX_USERS_SETTING, DEFAULT_MAX_USERS))
|
.setMaximumWeight(CACHE_MAX_USERS_SETTING.get(config.settings()))
|
||||||
.build();
|
.build();
|
||||||
} else {
|
} else {
|
||||||
cache = null;
|
cache = null;
|
||||||
@ -172,6 +179,13 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
|||||||
|
|
||||||
protected abstract void doLookupUser(String username, ActionListener<User> listener);
|
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 {
|
private static class UserWithHash {
|
||||||
User user;
|
User user;
|
||||||
char[] hash;
|
char[] hash;
|
||||||
|
@ -11,6 +11,7 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||||
import org.apache.logging.log4j.util.Supplier;
|
import org.apache.logging.log4j.util.Supplier;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.watcher.FileChangesListener;
|
import org.elasticsearch.watcher.FileChangesListener;
|
||||||
@ -30,6 +31,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static java.util.Collections.unmodifiableMap;
|
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 class DnRoleMapper {
|
||||||
|
|
||||||
public static final String DEFAULT_FILE_NAME = "role_mapping.yml";
|
private static final String DEFAULT_FILE_NAME = "role_mapping.yml";
|
||||||
public static final String ROLE_MAPPING_FILE_SETTING = "files.role_mapping";
|
public static final Setting<String> ROLE_MAPPING_FILE_SETTING = new Setting<>("files.role_mapping", DEFAULT_FILE_NAME,
|
||||||
public static final String USE_UNMAPPED_GROUPS_AS_ROLES_SETTING = "unmapped_groups_as_roles";
|
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 Logger logger;
|
||||||
protected final RealmConfig config;
|
protected final RealmConfig config;
|
||||||
@ -61,7 +66,7 @@ public class DnRoleMapper {
|
|||||||
this.logger = config.logger(getClass());
|
this.logger = config.logger(getClass());
|
||||||
this.listeners = new CopyOnWriteArrayList<>(Collections.singleton(listener));
|
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());
|
file = resolveFile(config.settings(), config.env());
|
||||||
dnRoles = parseFileLenient(file, logger, realmType, config.name());
|
dnRoles = parseFileLenient(file, logger, realmType, config.name());
|
||||||
FileWatcher watcher = new FileWatcher(file.getParent());
|
FileWatcher watcher = new FileWatcher(file.getParent());
|
||||||
@ -78,7 +83,7 @@ public class DnRoleMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Path resolveFile(Settings settings, Environment env) {
|
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);
|
return XPackPlugin.resolveConfigFile(env, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +190,11 @@ public class DnRoleMapper {
|
|||||||
listeners.forEach(Runnable::run);
|
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 {
|
private class FileListener implements FileChangesListener {
|
||||||
@Override
|
@Override
|
||||||
public void onFileCreated(Path file) {
|
public void onFileCreated(Path file) {
|
||||||
|
@ -5,22 +5,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.ssl;
|
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.Nullable;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
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
|
* 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
|
// 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
|
// implemented as optional settings, which provides a declarative manner for fallback as we typically fallback to values from a
|
||||||
// different configuration
|
// different configuration
|
||||||
private static final Setting<List<String>> CIPHERS_SETTING = Setting.listSetting("cipher_suites", Collections.emptyList(), s -> s);
|
private static final SSLConfigurationSettings SETTINGS_PARSER = SSLConfigurationSettings.withoutPrefix();
|
||||||
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 final KeyConfig keyConfig;
|
private final KeyConfig keyConfig;
|
||||||
private final TrustConfig trustConfig;
|
private final TrustConfig trustConfig;
|
||||||
@ -87,10 +43,10 @@ class SSLConfiguration {
|
|||||||
SSLConfiguration(Settings settings) {
|
SSLConfiguration(Settings settings) {
|
||||||
this.keyConfig = createKeyConfig(settings, null);
|
this.keyConfig = createKeyConfig(settings, null);
|
||||||
this.trustConfig = createTrustConfig(settings, keyConfig, null);
|
this.trustConfig = createTrustConfig(settings, keyConfig, null);
|
||||||
this.ciphers = getListOrDefault(CIPHERS_SETTING, settings, XPackSettings.DEFAULT_CIPHERS);
|
this.ciphers = getListOrDefault(SETTINGS_PARSER.ciphers, settings, XPackSettings.DEFAULT_CIPHERS);
|
||||||
this.supportedProtocols = getListOrDefault(SUPPORTED_PROTOCOLS_SETTING, settings, XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS);
|
this.supportedProtocols = getListOrDefault(SETTINGS_PARSER.supportedProtocols, settings, XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS);
|
||||||
this.sslClientAuth = CLIENT_AUTH_SETTING.get(settings).orElse(XPackSettings.CLIENT_AUTH_DEFAULT);
|
this.sslClientAuth = SETTINGS_PARSER.clientAuth.get(settings).orElse(XPackSettings.CLIENT_AUTH_DEFAULT);
|
||||||
this.verificationMode = VERIFICATION_MODE_SETTING.get(settings).orElse(XPackSettings.VERIFICATION_MODE_DEFAULT);
|
this.verificationMode = SETTINGS_PARSER.verificationMode.get(settings).orElse(XPackSettings.VERIFICATION_MODE_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,10 +59,11 @@ class SSLConfiguration {
|
|||||||
Objects.requireNonNull(globalSSLConfiguration);
|
Objects.requireNonNull(globalSSLConfiguration);
|
||||||
this.keyConfig = createKeyConfig(settings, globalSSLConfiguration);
|
this.keyConfig = createKeyConfig(settings, globalSSLConfiguration);
|
||||||
this.trustConfig = createTrustConfig(settings, keyConfig, globalSSLConfiguration);
|
this.trustConfig = createTrustConfig(settings, keyConfig, globalSSLConfiguration);
|
||||||
this.ciphers = getListOrDefault(CIPHERS_SETTING, settings, globalSSLConfiguration.cipherSuites());
|
this.ciphers = getListOrDefault(SETTINGS_PARSER.ciphers, settings, globalSSLConfiguration.cipherSuites());
|
||||||
this.supportedProtocols = getListOrDefault(SUPPORTED_PROTOCOLS_SETTING, settings, globalSSLConfiguration.supportedProtocols());
|
this.supportedProtocols = getListOrDefault(SETTINGS_PARSER.supportedProtocols, settings,
|
||||||
this.sslClientAuth = CLIENT_AUTH_SETTING.get(settings).orElse(globalSSLConfiguration.sslClientAuth());
|
globalSSLConfiguration.supportedProtocols());
|
||||||
this.verificationMode = VERIFICATION_MODE_SETTING.get(settings).orElse(globalSSLConfiguration.verificationMode());
|
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) {
|
private static KeyConfig createKeyConfig(Settings settings, SSLConfiguration global) {
|
||||||
String keyStorePath = KEYSTORE_PATH_SETTING.get(settings).orElse(null);
|
String keyStorePath = SETTINGS_PARSER.keystorePath.get(settings).orElse(null);
|
||||||
String keyPath = KEY_PATH_SETTING.get(settings).orElse(null);
|
String keyPath = SETTINGS_PARSER.keyPath.get(settings).orElse(null);
|
||||||
if (keyPath != null && keyStorePath != null) {
|
if (keyPath != null && keyStorePath != null) {
|
||||||
throw new IllegalArgumentException("you cannot specify a keystore and key file");
|
throw new IllegalArgumentException("you cannot specify a keystore and key file");
|
||||||
} else if (keyStorePath == null && keyPath == null) {
|
} else if (keyStorePath == null && keyPath == null) {
|
||||||
@ -233,29 +190,29 @@ class SSLConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (keyPath != null) {
|
if (keyPath != null) {
|
||||||
String keyPassword = KEY_PASSWORD_SETTING.get(settings).orElse(null);
|
String keyPassword = SETTINGS_PARSER.keyPassword.get(settings).orElse(null);
|
||||||
String certPath = CERT_SETTING.get(settings).orElse(null);
|
String certPath = SETTINGS_PARSER.cert.get(settings).orElse(null);
|
||||||
if (certPath == null) {
|
if (certPath == null) {
|
||||||
throw new IllegalArgumentException("you must specify the certificates to use with the key");
|
throw new IllegalArgumentException("you must specify the certificates to use with the key");
|
||||||
}
|
}
|
||||||
return new PEMKeyConfig(keyPath, keyPassword, certPath);
|
return new PEMKeyConfig(keyPath, keyPassword, certPath);
|
||||||
} else {
|
} else {
|
||||||
String keyStorePassword = KEYSTORE_PASSWORD_SETTING.get(settings).orElse(null);
|
String keyStorePassword = SETTINGS_PARSER.keystorePassword.get(settings).orElse(null);
|
||||||
String keyStoreAlgorithm = KEYSTORE_ALGORITHM_SETTING.get(settings);
|
String keyStoreAlgorithm = SETTINGS_PARSER.keystoreAlgorithm.get(settings);
|
||||||
String keyStoreKeyPassword = KEYSTORE_KEY_PASSWORD_SETTING.get(settings).orElse(keyStorePassword);
|
String keyStoreKeyPassword = SETTINGS_PARSER.keystoreKeyPassword.get(settings).orElse(keyStorePassword);
|
||||||
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
|
String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings);
|
||||||
return new StoreKeyConfig(keyStorePath, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, trustStoreAlgorithm);
|
return new StoreKeyConfig(keyStorePath, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, trustStoreAlgorithm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TrustConfig createTrustConfig(Settings settings, KeyConfig keyConfig, SSLConfiguration global) {
|
private static TrustConfig createTrustConfig(Settings settings, KeyConfig keyConfig, SSLConfiguration global) {
|
||||||
String trustStorePath = TRUSTSTORE_PATH_SETTING.get(settings).orElse(null);
|
String trustStorePath = SETTINGS_PARSER.truststorePath.get(settings).orElse(null);
|
||||||
List<String> caPaths = getListOrNull(CA_PATHS_SETTING, settings);
|
List<String> caPaths = getListOrNull(SETTINGS_PARSER.caPaths, settings);
|
||||||
if (trustStorePath != null && caPaths != null) {
|
if (trustStorePath != null && caPaths != null) {
|
||||||
throw new IllegalArgumentException("you cannot specify a truststore and ca files");
|
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) {
|
if (global != null) {
|
||||||
return global.verificationMode();
|
return global.verificationMode();
|
||||||
}
|
}
|
||||||
@ -266,8 +223,8 @@ class SSLConfiguration {
|
|||||||
} else if (caPaths != null) {
|
} else if (caPaths != null) {
|
||||||
return new PEMTrustConfig(caPaths);
|
return new PEMTrustConfig(caPaths);
|
||||||
} else if (trustStorePath != null) {
|
} else if (trustStorePath != null) {
|
||||||
String trustStorePassword = TRUSTSTORE_PASSWORD_SETTING.get(settings).orElse(null);
|
String trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings).orElse(null);
|
||||||
String trustStoreAlgorithm = TRUSTSTORE_ALGORITHM_SETTING.get(settings);
|
String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings);
|
||||||
return new StoreTrustConfig(trustStorePath, trustStorePassword, trustStoreAlgorithm);
|
return new StoreTrustConfig(trustStorePath, trustStorePassword, trustStoreAlgorithm);
|
||||||
} else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) {
|
} else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) {
|
||||||
return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"),
|
return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"),
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.transport.TransportSettings;
|
import org.elasticsearch.transport.TransportSettings;
|
||||||
import org.elasticsearch.xpack.XPackSettings;
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
|
import org.elasticsearch.xpack.security.Security;
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
@ -62,7 +63,7 @@ public class SSLService extends AbstractComponent {
|
|||||||
public SSLService(Settings settings, Environment environment) {
|
public SSLService(Settings settings, Environment environment) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.env = environment;
|
this.env = environment;
|
||||||
this.globalSSLConfiguration = new SSLConfiguration(settings.getByPrefix("xpack.ssl."));
|
this.globalSSLConfiguration = new SSLConfiguration(settings.getByPrefix(XPackSettings.GLOBAL_SSL_PREFIX));
|
||||||
this.sslContexts = loadSSLConfigurations();
|
this.sslContexts = loadSSLConfigurations();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +388,7 @@ public class SSLService extends AbstractComponent {
|
|||||||
Map<SSLConfiguration, SSLContextHolder> sslConfigurations = new HashMap<>();
|
Map<SSLConfiguration, SSLContextHolder> sslConfigurations = new HashMap<>();
|
||||||
sslConfigurations.put(globalSSLConfiguration, createSslContext(globalSSLConfiguration));
|
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<>();
|
List<Settings> sslSettingsList = new ArrayList<>();
|
||||||
sslSettingsList.add(transportSSLSettings);
|
sslSettingsList.add(transportSSLSettings);
|
||||||
sslSettingsList.add(getHttpTransportSSLSettings(settings));
|
sslSettingsList.add(getHttpTransportSSLSettings(settings));
|
||||||
@ -741,7 +742,7 @@ public class SSLService extends AbstractComponent {
|
|||||||
|
|
||||||
private static List<Settings> getRealmsSSLSettings(Settings settings) {
|
private static List<Settings> getRealmsSSLSettings(Settings settings) {
|
||||||
List<Settings> sslSettings = new ArrayList<>();
|
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()) {
|
for (String name : realmsSettings.names()) {
|
||||||
Settings realmSSLSettings = realmsSettings.getAsSettings(name).getByPrefix("ssl.");
|
Settings realmSSLSettings = realmsSettings.getAsSettings(name).getByPrefix("ssl.");
|
||||||
if (realmSSLSettings.isEmpty() == false) {
|
if (realmSSLSettings.isEmpty() == false) {
|
||||||
@ -764,7 +765,7 @@ public class SSLService extends AbstractComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Settings getHttpTransportSSLSettings(Settings settings) {
|
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()) {
|
if (httpSSLSettings.isEmpty()) {
|
||||||
return httpSSLSettings;
|
return httpSSLSettings;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class SettingsFilterTests extends ESTestCase {
|
|||||||
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.type", "ldap");
|
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.type", "ldap");
|
||||||
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.enabled", "false");
|
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.enabled", "false");
|
||||||
configureUnfilteredSetting("xpack.security.authc.realms.ldap1.url", "ldap://host.domain");
|
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_dn", randomAsciiOfLength(5));
|
||||||
configureFilteredSetting("xpack.security.authc.realms.ldap1.bind_password", 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.type", "active_directory");
|
||||||
configureUnfilteredSetting("xpack.security.authc.realms.ad1.enabled", "false");
|
configureUnfilteredSetting("xpack.security.authc.realms.ad1.enabled", "false");
|
||||||
configureUnfilteredSetting("xpack.security.authc.realms.ad1.url", "ldap://host.domain");
|
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
|
// pki filtering
|
||||||
configureUnfilteredSetting("xpack.security.authc.realms.pki1.type", "pki");
|
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) {
|
private void configureUnfilteredSetting(String settingName, String value) {
|
||||||
configureSetting(settingName, value, is(value));
|
configureSetting(settingName, value, is(value));
|
||||||
}
|
}
|
||||||
|
@ -284,8 +284,8 @@ public class HttpClientTests extends ESTestCase {
|
|||||||
proxyServer.enqueue(new MockResponse().setResponseCode(200).setBody("fullProxiedContent"));
|
proxyServer.enqueue(new MockResponse().setResponseCode(200).setBody("fullProxiedContent"));
|
||||||
proxyServer.start();
|
proxyServer.start();
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put(HttpClient.SETTINGS_PROXY_HOST, "localhost")
|
.put(HttpSettings.PROXY_HOST.getKey(), "localhost")
|
||||||
.put(HttpClient.SETTINGS_PROXY_PORT, proxyServer.getPort())
|
.put(HttpSettings.PROXY_PORT.getKey(), proxyServer.getPort())
|
||||||
.build();
|
.build();
|
||||||
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
|
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.enqueue(new MockResponse().setResponseCode(200).setBody("fullProxiedContent"));
|
||||||
proxyServer.start();
|
proxyServer.start();
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put(HttpClient.SETTINGS_PROXY_HOST, "localhost")
|
.put(HttpSettings.PROXY_HOST.getKey(), "localhost")
|
||||||
.put(HttpClient.SETTINGS_PROXY_PORT, proxyServer.getPort() + 1)
|
.put(HttpSettings.PROXY_PORT.getKey(), proxyServer.getPort() + 1)
|
||||||
.build();
|
.build();
|
||||||
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
|
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ public class SecurityTests extends ESTestCase {
|
|||||||
ThreadPool threadPool = mock(ThreadPool.class);
|
ThreadPool threadPool = mock(ThreadPool.class);
|
||||||
ClusterService clusterService = mock(ClusterService.class);
|
ClusterService clusterService = mock(ClusterService.class);
|
||||||
settings = Security.additionalSettings(settings, false);
|
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);
|
allowedSettings.addAll(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
|
||||||
ClusterSettings clusterSettings = new ClusterSettings(settings, allowedSettings);
|
ClusterSettings clusterSettings = new ClusterSettings(settings, allowedSettings);
|
||||||
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
|
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -64,6 +64,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
public class ActiveDirectoryRealmTests extends ESTestCase {
|
public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
|
|
||||||
private static final String PASSWORD = "password";
|
private static final String PASSWORD = "password";
|
||||||
|
private static final String ROLE_MAPPING_FILE_SETTING = DnRoleMapper.ROLE_MAPPING_FILE_SETTING.getKey();
|
||||||
|
|
||||||
static int numberOfLdapServers;
|
static int numberOfLdapServers;
|
||||||
InMemoryDirectoryServer[] directoryServers;
|
InMemoryDirectoryServer[] directoryServers;
|
||||||
@ -168,7 +169,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAuthenticateCachingCanBeDisabled() throws Exception {
|
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);
|
RealmConfig config = new RealmConfig("testAuthenticateCachingCanBeDisabled", settings, globalSettings);
|
||||||
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null));
|
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null));
|
||||||
DnRoleMapper roleMapper = new DnRoleMapper(LdapRealm.AD_TYPE, config, resourceWatcherService, () -> {});
|
DnRoleMapper roleMapper = new DnRoleMapper(LdapRealm.AD_TYPE, config, resourceWatcherService, () -> {});
|
||||||
@ -216,7 +217,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testRealmMapsGroupsToRoles() throws Exception {
|
public void testRealmMapsGroupsToRoles() throws Exception {
|
||||||
Settings settings = settings(Settings.builder()
|
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());
|
.build());
|
||||||
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
|
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
||||||
@ -232,7 +233,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testRealmMapsUsersToRoles() throws Exception {
|
public void testRealmMapsUsersToRoles() throws Exception {
|
||||||
Settings settings = settings(Settings.builder()
|
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());
|
.build());
|
||||||
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
|
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
|
||||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
||||||
@ -249,7 +250,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||||||
public void testRealmUsageStats() throws Exception {
|
public void testRealmUsageStats() throws Exception {
|
||||||
String loadBalanceType = randomFrom("failover", "round_robin");
|
String loadBalanceType = randomFrom("failover", "round_robin");
|
||||||
Settings settings = settings(Settings.builder()
|
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)
|
.put("load_balance.type", loadBalanceType)
|
||||||
.build());
|
.build());
|
||||||
RealmConfig config = new RealmConfig("testRealmUsageStats", settings, globalSettings);
|
RealmConfig config = new RealmConfig("testRealmUsageStats", settings, globalSettings);
|
||||||
@ -274,7 +275,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||||||
return Settings.builder()
|
return Settings.builder()
|
||||||
.putArray(URLS_SETTING, ldapUrls())
|
.putArray(URLS_SETTING, ldapUrls())
|
||||||
.put(ActiveDirectorySessionFactory.AD_DOMAIN_NAME_SETTING, "ad.test.elasticsearch.com")
|
.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(HOSTNAME_VERIFICATION_SETTING, false)
|
||||||
.put(extraSettings)
|
.put(extraSettings)
|
||||||
.build();
|
.build();
|
||||||
|
@ -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.LdapSearchScope;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapTestCase;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapTestCase;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
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.DnRoleMapper;
|
||||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||||
import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
|
import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
|
||||||
@ -23,9 +24,9 @@ import org.elasticsearch.watcher.ResourceWatcherService;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
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.HOSTNAME_VERIFICATION_SETTING;
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory.URLS_SETTING;
|
import static org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory.URLS_SETTING;
|
||||||
import static org.hamcrest.Matchers.arrayContaining;
|
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 VALID_USERNAME = "Thomas Masterman Hardy";
|
||||||
public static final String PASSWORD = "pass";
|
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 ThreadPool threadPool;
|
||||||
private ResourceWatcherService resourceWatcherService;
|
private ResourceWatcherService resourceWatcherService;
|
||||||
private Settings globalSettings;
|
private Settings globalSettings;
|
||||||
@ -95,7 +98,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||||||
ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)), future);
|
ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)), future);
|
||||||
User user = future.actionGet();
|
User user = future.actionGet();
|
||||||
assertThat(user, notNullValue());
|
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 {
|
public void testAuthenticateCaching() throws Exception {
|
||||||
@ -158,7 +161,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||||||
String userTemplate = VALID_USER_TEMPLATE;
|
String userTemplate = VALID_USER_TEMPLATE;
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put(LdapRealm.CACHE_TTL_SETTING, -1)
|
.put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING.getKey(), -1)
|
||||||
.build();
|
.build();
|
||||||
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
|
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
|
||||||
|
|
||||||
@ -182,7 +185,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||||||
String userTemplate = VALID_USER_TEMPLATE;
|
String userTemplate = VALID_USER_TEMPLATE;
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.putArray(URLS_SETTING, ldapUrls())
|
.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.base_dn", groupSearchBase)
|
||||||
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
||||||
.put(HOSTNAME_VERIFICATION_SETTING, false)
|
.put(HOSTNAME_VERIFICATION_SETTING, false)
|
||||||
@ -215,7 +218,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||||||
public void testLdapRealmThrowsExceptionForUserTemplateAndSearchSettings() throws Exception {
|
public void testLdapRealmThrowsExceptionForUserTemplateAndSearchSettings() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.putArray(URLS_SETTING, ldapUrls())
|
.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("user_search.base_dn", "cn=bar")
|
||||||
.put("group_search.base_dn", "")
|
.put("group_search.base_dn", "")
|
||||||
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
.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);
|
RealmConfig config = new RealmConfig("test-ldap-realm-user-search", settings, globalSettings);
|
||||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||||
() -> LdapRealm.sessionFactory(config, null, LdapRealm.LDAP_TYPE));
|
() -> 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 {
|
public void testLdapRealmMapsUserDNToRole() throws Exception {
|
||||||
@ -232,7 +254,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||||||
String userTemplate = VALID_USER_TEMPLATE;
|
String userTemplate = VALID_USER_TEMPLATE;
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.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"))
|
getDataPath("/org/elasticsearch/xpack/security/authc/support/role_mapping.yml"))
|
||||||
.build();
|
.build();
|
||||||
RealmConfig config = new RealmConfig("test-ldap-realm-userdn", settings, globalSettings);
|
RealmConfig config = new RealmConfig("test-ldap-realm-userdn", settings, globalSettings);
|
||||||
@ -256,6 +278,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||||||
.put("bind_password", PASSWORD)
|
.put("bind_password", PASSWORD)
|
||||||
.put("group_search.base_dn", groupSearchBase)
|
.put("group_search.base_dn", groupSearchBase)
|
||||||
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
||||||
|
.put(LdapSessionFactory.USER_DN_TEMPLATES_SETTING.getKey(), "--")
|
||||||
.put(HOSTNAME_VERIFICATION_SETTING, false);
|
.put(HOSTNAME_VERIFICATION_SETTING, false);
|
||||||
|
|
||||||
int order = randomIntBetween(0, 10);
|
int order = randomIntBetween(0, 10);
|
||||||
|
@ -27,7 +27,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testResolveSubTree() throws Exception {
|
public void testResolveSubTree() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
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();
|
.build();
|
||||||
|
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
@ -42,8 +42,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testResolveOneLevel() throws Exception {
|
public void testResolveOneLevel() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put("base_dn", "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
.put("group_search.base_dn", "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
||||||
.put("scope", LdapSearchScope.ONE_LEVEL)
|
.put("group_search.scope", LdapSearchScope.ONE_LEVEL)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
@ -58,8 +58,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testResolveBase() throws Exception {
|
public void testResolveBase() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put("base_dn", "cn=Avengers,ou=People,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
.put("group_search.base_dn", "cn=Avengers,ou=People,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
||||||
.put("scope", LdapSearchScope.BASE)
|
.put("group_search.scope", LdapSearchScope.BASE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
@ -70,9 +70,9 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testResolveCustomFilter() throws Exception {
|
public void testResolveCustomFilter() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
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")
|
||||||
.put("filter", "(&(objectclass=posixGroup)(memberUID={0}))")
|
.put("group_search.filter", "(&(objectclass=posixGroup)(memberUID={0}))")
|
||||||
.put("user_attribute", "uid")
|
.put("group_search.user_attribute", "uid")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
@ -84,8 +84,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testFilterIncludesPosixGroups() throws Exception {
|
public void testFilterIncludesPosixGroups() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
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")
|
||||||
.put("user_attribute", "uid")
|
.put("group_search.user_attribute", "uid")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
@ -97,7 +97,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testCreateWithoutSpecifyingBaseDN() throws Exception {
|
public void testCreateWithoutSpecifyingBaseDN() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put("scope", LdapSearchScope.SUB_TREE)
|
.put("group_search.scope", LdapSearchScope.SUB_TREE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -110,8 +110,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testReadUserAttributeUid() throws Exception {
|
public void testReadUserAttributeUid() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
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")
|
||||||
.put("user_attribute", "uid").build();
|
.put("group_search.user_attribute", "uid").build();
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
PlainActionFuture<String> future = new PlainActionFuture<>();
|
PlainActionFuture<String> future = new PlainActionFuture<>();
|
||||||
resolver.readUserAttribute(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(5), future);
|
resolver.readUserAttribute(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(5), future);
|
||||||
@ -120,8 +120,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testReadUserAttributeCn() throws Exception {
|
public void testReadUserAttributeCn() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
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")
|
||||||
.put("user_attribute", "cn")
|
.put("group_search.user_attribute", "cn")
|
||||||
.build();
|
.build();
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
|
|
||||||
@ -132,8 +132,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testReadNonExistentUserAttribute() throws Exception {
|
public void testReadNonExistentUserAttribute() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
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")
|
||||||
.put("user_attribute", "doesntExists")
|
.put("group_search.user_attribute", "doesntExists")
|
||||||
.build();
|
.build();
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
|
|
||||||
@ -144,8 +144,8 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||||||
|
|
||||||
public void testReadBinaryUserAttribute() throws Exception {
|
public void testReadBinaryUserAttribute() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
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")
|
||||||
.put("user_attribute", "userPassword")
|
.put("group_search.user_attribute", "userPassword")
|
||||||
.build();
|
.build();
|
||||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import org.elasticsearch.common.Strings;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
|
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.xpack.security.authc.support.DnRoleMapper;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
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.HOSTNAME_VERIFICATION_SETTING;
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.LdapSessionFactory.URLS_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 {
|
public abstract class LdapTestCase extends ESTestCase {
|
||||||
|
|
||||||
|
private static final String USER_DN_TEMPLATES_SETTING_KEY = LdapSessionFactory.USER_DN_TEMPLATES_SETTING.getKey();
|
||||||
|
|
||||||
static int numberOfLdapServers;
|
static int numberOfLdapServers;
|
||||||
protected InMemoryDirectoryServer[] ldapServers;
|
protected InMemoryDirectoryServer[] ldapServers;
|
||||||
|
|
||||||
@ -86,7 +88,7 @@ public abstract class LdapTestCase extends ESTestCase {
|
|||||||
LdapLoadBalancing serverSetType) {
|
LdapLoadBalancing serverSetType) {
|
||||||
Settings.Builder builder = Settings.builder()
|
Settings.Builder builder = Settings.builder()
|
||||||
.putArray(URLS_SETTING, ldapUrl)
|
.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.base_dn", groupSearchBase)
|
||||||
.put("group_search.scope", scope)
|
.put("group_search.scope", scope)
|
||||||
.put(HOSTNAME_VERIFICATION_SETTING, false);
|
.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) {
|
public static Settings buildLdapSettings(String[] ldapUrl, String userTemplate, boolean hostnameVerification) {
|
||||||
return Settings.builder()
|
return Settings.builder()
|
||||||
.putArray(URLS_SETTING, ldapUrl)
|
.putArray(URLS_SETTING, ldapUrl)
|
||||||
.putArray(USER_DN_TEMPLATES_SETTING, userTemplate)
|
.putArray(USER_DN_TEMPLATES_SETTING_KEY, userTemplate)
|
||||||
.put(HOSTNAME_VERIFICATION_SETTING, hostnameVerification)
|
.put(HOSTNAME_VERIFICATION_SETTING, hostnameVerification)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DnRoleMapper buildGroupAsRoleMapper(ResourceWatcherService resourceWatcherService) {
|
protected DnRoleMapper buildGroupAsRoleMapper(ResourceWatcherService resourceWatcherService) {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
|
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING.getKey(), true)
|
||||||
.build();
|
.build();
|
||||||
Settings global = Settings.builder().put("path.home", createTempDir()).build();
|
Settings global = Settings.builder().put("path.home", createTempDir()).build();
|
||||||
RealmConfig config = new RealmConfig("ldap1", settings, global);
|
RealmConfig config = new RealmConfig("ldap1", settings, global);
|
||||||
|
@ -42,9 +42,9 @@ public class CachingUsernamePasswordRealmTests extends ESTestCase {
|
|||||||
int maxUsers = randomIntBetween(10, 100);
|
int maxUsers = randomIntBetween(10, 100);
|
||||||
TimeValue ttl = TimeValue.timeValueMinutes(randomIntBetween(10, 20));
|
TimeValue ttl = TimeValue.timeValueMinutes(randomIntBetween(10, 20));
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put(CachingUsernamePasswordRealm.CACHE_HASH_ALGO_SETTING, hashAlgo)
|
.put(CachingUsernamePasswordRealm.CACHE_HASH_ALGO_SETTING.getKey(), hashAlgo)
|
||||||
.put(CachingUsernamePasswordRealm.CACHE_MAX_USERS_SETTING, maxUsers)
|
.put(CachingUsernamePasswordRealm.CACHE_MAX_USERS_SETTING.getKey(), maxUsers)
|
||||||
.put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING, ttl)
|
.put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING.getKey(), ttl)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
RealmConfig config = new RealmConfig("test_realm", settings, globalSettings);
|
RealmConfig config = new RealmConfig("test_realm", settings, globalSettings);
|
||||||
|
@ -47,6 +47,9 @@ import static org.hamcrest.Matchers.notNullValue;
|
|||||||
|
|
||||||
public class DnRoleMapperTests extends ESTestCase {
|
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[] {
|
private static final String[] STARK_GROUP_DNS = new String[] {
|
||||||
//groups can be named by different attributes, depending on the directory,
|
//groups can be named by different attributes, depending on the directory,
|
||||||
//we don't care what it is named by
|
//we don't care what it is named by
|
||||||
@ -230,7 +233,7 @@ public class DnRoleMapperTests extends ESTestCase {
|
|||||||
public void testYaml() throws Exception {
|
public void testYaml() throws Exception {
|
||||||
Path file = getDataPath("role_mapping.yml");
|
Path file = getDataPath("role_mapping.yml");
|
||||||
Settings ldapSettings = Settings.builder()
|
Settings ldapSettings = Settings.builder()
|
||||||
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
|
.put(ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
|
||||||
.build();
|
.build();
|
||||||
RealmConfig config = new RealmConfig("ldap1", ldapSettings, settings);
|
RealmConfig config = new RealmConfig("ldap1", ldapSettings, settings);
|
||||||
|
|
||||||
@ -244,7 +247,7 @@ public class DnRoleMapperTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testRelativeDN() {
|
public void testRelativeDN() {
|
||||||
Settings ldapSettings = Settings.builder()
|
Settings ldapSettings = Settings.builder()
|
||||||
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, true)
|
.put(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING_KEY, true)
|
||||||
.build();
|
.build();
|
||||||
RealmConfig config = new RealmConfig("ldap1", ldapSettings, settings);
|
RealmConfig config = new RealmConfig("ldap1", ldapSettings, settings);
|
||||||
|
|
||||||
@ -257,8 +260,8 @@ public class DnRoleMapperTests extends ESTestCase {
|
|||||||
public void testUserDNMapping() throws Exception {
|
public void testUserDNMapping() throws Exception {
|
||||||
Path file = getDataPath("role_mapping.yml");
|
Path file = getDataPath("role_mapping.yml");
|
||||||
Settings ldapSettings = Settings.builder()
|
Settings ldapSettings = Settings.builder()
|
||||||
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
|
.put(ROLE_MAPPING_FILE_SETTING, file.toAbsolutePath())
|
||||||
.put(DnRoleMapper.USE_UNMAPPED_GROUPS_AS_ROLES_SETTING, false)
|
.put(USE_UNMAPPED_GROUPS_AS_ROLES_SETTING_KEY, false)
|
||||||
.build();
|
.build();
|
||||||
RealmConfig config = new RealmConfig("ldap-userdn-role", ldapSettings, settings);
|
RealmConfig config = new RealmConfig("ldap-userdn-role", ldapSettings, settings);
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -37,6 +37,7 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
|
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
|
||||||
@ -236,6 +237,21 @@ public class SSLServiceTests extends ESTestCase {
|
|||||||
settings.getByPrefix("xpack.security.transport.ssl.")));
|
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 {
|
public void testThatTruststorePasswordIsRequired() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put("xpack.ssl.keystore.path", testnodeStore)
|
.put("xpack.ssl.keystore.path", testnodeStore)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user