Add secure_bind_password to LDAP realm (elastic/x-pack-elasticsearch#4192)
Adds a SecureSetting option for the "bind_password" in LDAP/AD realms and deprecates the non-secure version. LDAP bind passwords should now be configured with the setting `xpack.security.authc.realms.REALM_NAME.secure_bind_password` in the elasticsearch keystore. Original commit: elastic/x-pack-elasticsearch@1a0cebd77e
This commit is contained in:
parent
01ab4782a1
commit
e69c5d4d48
|
@ -93,7 +93,7 @@ connections to Active Directory. These pooled connection reduce the number of
|
||||||
resources that must be created and destroyed with every user authentication.
|
resources that must be created and destroyed with every user authentication.
|
||||||
|
|
||||||
The following example shows the configuration of a bind user through the user of the
|
The following example shows the configuration of a bind user through the user of the
|
||||||
`bind_dn` and `bind_password` settings.
|
`bind_dn` and `secure_bind_password` settings.
|
||||||
|
|
||||||
[source, yaml]
|
[source, yaml]
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
|
@ -107,12 +107,20 @@ xpack:
|
||||||
domain_name: ad.example.com
|
domain_name: ad.example.com
|
||||||
url: ldaps://ad.example.com:636
|
url: ldaps://ad.example.com:636
|
||||||
bind_dn: es_svc_user@ad.example.com <1>
|
bind_dn: es_svc_user@ad.example.com <1>
|
||||||
bind_password: es_svc_user_password
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
<1> This is the user that all Active Directory search requests are executed as.
|
<1> This is the user that all Active Directory search requests are executed as.
|
||||||
Without a bind user configured, all requests run as the user that is authenticating
|
Without a bind user configured, all requests run as the user that is authenticating
|
||||||
with Elasticsearch.
|
with Elasticsearch.
|
||||||
|
|
||||||
|
The password for the `bind_dn` user should be configured by adding the appropriate
|
||||||
|
`secure_bind_password` setting to the {es} keystore.
|
||||||
|
For example, the following command adds the password for the example realm above:
|
||||||
|
|
||||||
|
[source, shell]
|
||||||
|
------------------------------------------------------------
|
||||||
|
bin/elasticsearch-keystore add xpack.security.authc.realms.active_directory.secure_bind_password
|
||||||
|
------------------------------------------------------------
|
||||||
|
|
||||||
When a bind user is configured, connection pooling is enabled by default.
|
When a bind user is configured, connection pooling is enabled by default.
|
||||||
Connection pooling can be disabled using the `user_search.pool.enabled` setting.
|
Connection pooling can be disabled using the `user_search.pool.enabled` setting.
|
||||||
|
|
||||||
|
@ -223,6 +231,9 @@ operation are supported: failover and load balancing
|
||||||
Active Directory. Due to its potential security impact,
|
Active Directory. Due to its potential security impact,
|
||||||
`bind_password` is not exposed via the
|
`bind_password` is not exposed via the
|
||||||
{ref}/cluster-nodes-info.html#cluster-nodes-info[nodes info API].
|
{ref}/cluster-nodes-info.html#cluster-nodes-info[nodes info API].
|
||||||
|
*Deprecated.* Use `secure_bind_password` instead.
|
||||||
|
| `secure_bind_password` | no | ({ref}/secure-settings.html[Secure])
|
||||||
|
The password for the user that is used to bind to Active Directory.
|
||||||
| `load_balance.type` | no | The behavior to use when there are multiple LDAP URLs defined.
|
| `load_balance.type` | no | The behavior to use when there are multiple LDAP URLs defined.
|
||||||
For supported values see <<ad-load-balancing>>.
|
For supported values see <<ad-load-balancing>>.
|
||||||
| `load_balance.cache_ttl` | no | When using `dns_failover` or `dns_round_robin` as the load
|
| `load_balance.cache_ttl` | no | When using `dns_failover` or `dns_round_robin` as the load
|
||||||
|
|
|
@ -61,7 +61,6 @@ xpack:
|
||||||
order: 0
|
order: 0
|
||||||
url: "ldaps://ldap.example.com:636"
|
url: "ldaps://ldap.example.com:636"
|
||||||
bind_dn: "cn=ldapuser, ou=users, o=services, dc=example, dc=com"
|
bind_dn: "cn=ldapuser, ou=users, o=services, dc=example, dc=com"
|
||||||
bind_password: x-pack-test-password
|
|
||||||
user_search:
|
user_search:
|
||||||
base_dn: "dc=example,dc=com"
|
base_dn: "dc=example,dc=com"
|
||||||
attribute: cn
|
attribute: cn
|
||||||
|
@ -72,6 +71,15 @@ xpack:
|
||||||
unmapped_groups_as_roles: false
|
unmapped_groups_as_roles: false
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
+
|
+
|
||||||
|
The password for the `bind_dn` user should be configured by adding the appropriate
|
||||||
|
`secure_bind_password` setting to the {es} keystore.
|
||||||
|
For example, the following command adds the password for the example realm above:
|
||||||
|
+
|
||||||
|
[source, shell]
|
||||||
|
------------------------------------------------------------
|
||||||
|
bin/elasticsearch-keystore add xpack.security.authc.realms.ldap1.secure_bind_password
|
||||||
|
------------------------------------------------------------
|
||||||
|
+
|
||||||
IMPORTANT: When you configure realms in `elasticsearch.yml`, only the
|
IMPORTANT: When you configure realms in `elasticsearch.yml`, only the
|
||||||
realms you specify are used for authentication. If you also want to use the
|
realms you specify are used for authentication. If you also want to use the
|
||||||
`native` or `file` realms, you must include them in the realm chain.
|
`native` or `file` realms, you must include them in the realm chain.
|
||||||
|
@ -281,9 +289,12 @@ failover and load balancing modes of operation.
|
||||||
impact, `bind_dn` is not exposed via the
|
impact, `bind_dn` is not exposed via the
|
||||||
{ref}/cluster-nodes-info.html#cluster-nodes-info[nodes info API].
|
{ref}/cluster-nodes-info.html#cluster-nodes-info[nodes info API].
|
||||||
| `bind_password` | no | The password for the user that is used to bind to the
|
| `bind_password` | no | The password for the user that is used to bind to the
|
||||||
LDAP. Due to its potential security impact,
|
LDAP directory. Due to its potential security impact,
|
||||||
`bind_password` is not exposed via the
|
`bind_password` is not exposed via the
|
||||||
{ref}/cluster-nodes-info.html#cluster-nodes-info[nodes info API].
|
{ref}/cluster-nodes-info.html#cluster-nodes-info[nodes info API].
|
||||||
|
*Deprecated.* Use `secure_bind_password` instead.
|
||||||
|
| `secure_bind_password` | no | ({ref}/secure-settings.html[Secure])
|
||||||
|
The password for the user that is used to bind to LDAP directory.
|
||||||
| `user_search.base_dn` | yes | Specifies a container DN to search for users.
|
| `user_search.base_dn` | yes | Specifies a container DN to search for users.
|
||||||
| `user_search.scope` | no | The scope of the user search. Valid values are `sub_tree`,
|
| `user_search.scope` | no | The scope of the user search. Valid values are `sub_tree`,
|
||||||
`one_level` or `base`. `one_level` only searches objects
|
`one_level` or `base`. `one_level` only searches objects
|
||||||
|
|
|
@ -10,8 +10,8 @@ To "run as" (impersonate) another user, you must be able to retrieve the user fr
|
||||||
the realm you use to authenticate. Both the internal `native` and `file` realms
|
the realm you use to authenticate. Both the internal `native` and `file` realms
|
||||||
support this out of the box. The LDAP realm must be configured to run in
|
support this out of the box. The LDAP realm must be configured to run in
|
||||||
<<ldap-user-search, _user search_ mode>>. The Active Directory realm must be
|
<<ldap-user-search, _user search_ mode>>. The Active Directory realm must be
|
||||||
<<ad-settings,configured with a `bind_dn` and `bind_password`>> to support _run as_.
|
<<ad-settings,configured with a `bind_dn` and `secure_bind_password`>> to support
|
||||||
The PKI realm does not support _run as_.
|
_run as_. The PKI realm does not support _run as_.
|
||||||
|
|
||||||
To submit requests on behalf of other users, you need to have the `run_as`
|
To submit requests on behalf of other users, you need to have the `run_as`
|
||||||
permission. For example, the following role grants permission to submit request
|
permission. For example, the following role grants permission to submit request
|
||||||
|
|
|
@ -182,7 +182,12 @@ If this is not specified, an anonymous bind will be attempted.
|
||||||
Defaults to Empty.
|
Defaults to Empty.
|
||||||
|
|
||||||
`bind_password`::
|
`bind_password`::
|
||||||
The password for the user that will be used to bind to the LDAP.
|
The password for the user that will be used to bind to the LDAP directory.
|
||||||
|
Defaults to Empty.
|
||||||
|
*Deprecated.* Use `secure_bind_password` instead.
|
||||||
|
|
||||||
|
`secure_bind_password` (<<secure-settings,Secure>>)::
|
||||||
|
The password for the user that will be used to bind to the LDAP directory.
|
||||||
Defaults to Empty.
|
Defaults to Empty.
|
||||||
|
|
||||||
`user_dn_templates`::
|
`user_dn_templates`::
|
||||||
|
@ -410,6 +415,11 @@ Defaults to Empty.
|
||||||
`bind_password`::
|
`bind_password`::
|
||||||
The password for the user that will be used to bind to Active Directory.
|
The password for the user that will be used to bind to Active Directory.
|
||||||
Defaults to Empty.
|
Defaults to Empty.
|
||||||
|
*Deprecated.* Use `secure_bind_password` instead.
|
||||||
|
|
||||||
|
`secure_bind_password` (<<secure-settings,Secure>>)::
|
||||||
|
The password for the user that will be used to bind to Active Directory.
|
||||||
|
Defaults to Empty.
|
||||||
|
|
||||||
`unmapped_groups_as_roles`::
|
`unmapped_groups_as_roles`::
|
||||||
Takes a boolean variable. When this element is set to `true`, the names of any
|
Takes a boolean variable. When this element is set to `true`, the names of any
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.core.security.authc.ldap;
|
package org.elasticsearch.xpack.core.security.authc.ldap;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.util.set.Sets;
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
|
@ -12,11 +13,15 @@ import org.elasticsearch.common.util.set.Sets;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.elasticsearch.common.settings.SecureSetting.secureString;
|
||||||
|
|
||||||
public final class PoolingSessionFactorySettings {
|
public final class PoolingSessionFactorySettings {
|
||||||
public static final TimeValue DEFAULT_HEALTH_CHECK_INTERVAL = TimeValue.timeValueSeconds(60L);
|
public static final TimeValue DEFAULT_HEALTH_CHECK_INTERVAL = TimeValue.timeValueSeconds(60L);
|
||||||
public static final Setting<String> BIND_DN = Setting.simpleString("bind_dn", Setting.Property.NodeScope, Setting.Property.Filtered);
|
public static final Setting<String> BIND_DN = Setting.simpleString("bind_dn", Setting.Property.NodeScope, Setting.Property.Filtered);
|
||||||
public static final Setting<String> BIND_PASSWORD = Setting.simpleString("bind_password", Setting.Property.NodeScope,
|
public static final Setting<SecureString> LEGACY_BIND_PASSWORD = new Setting<>("bind_password", "", SecureString::new,
|
||||||
Setting.Property.Filtered);
|
Setting.Property.NodeScope, Setting.Property.Filtered, Setting.Property.Deprecated);
|
||||||
|
public static final Setting<SecureString> SECURE_BIND_PASSWORD = secureString("secure_bind_password", LEGACY_BIND_PASSWORD);
|
||||||
|
|
||||||
public static final int DEFAULT_CONNECTION_POOL_INITIAL_SIZE = 0;
|
public static final int DEFAULT_CONNECTION_POOL_INITIAL_SIZE = 0;
|
||||||
public static final Setting<Integer> POOL_INITIAL_SIZE = Setting.intSetting("user_search.pool.initial_size",
|
public static final Setting<Integer> POOL_INITIAL_SIZE = Setting.intSetting("user_search.pool.initial_size",
|
||||||
DEFAULT_CONNECTION_POOL_INITIAL_SIZE, 0, Setting.Property.NodeScope);
|
DEFAULT_CONNECTION_POOL_INITIAL_SIZE, 0, Setting.Property.NodeScope);
|
||||||
|
@ -34,6 +39,6 @@ public final class PoolingSessionFactorySettings {
|
||||||
|
|
||||||
public static Set<Setting<?>> getSettings() {
|
public static Set<Setting<?>> getSettings() {
|
||||||
return Sets.newHashSet(POOL_INITIAL_SIZE, POOL_SIZE, HEALTH_CHECK_ENABLED, HEALTH_CHECK_INTERVAL, HEALTH_CHECK_DN, BIND_DN,
|
return Sets.newHashSet(POOL_INITIAL_SIZE, POOL_SIZE, HEALTH_CHECK_ENABLED, HEALTH_CHECK_INTERVAL, HEALTH_CHECK_DN, BIND_DN,
|
||||||
BIND_PASSWORD);
|
SECURE_BIND_PASSWORD, LEGACY_BIND_PASSWORD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import com.unboundid.ldap.sdk.SearchResultEntry;
|
||||||
import com.unboundid.ldap.sdk.SimpleBindRequest;
|
import com.unboundid.ldap.sdk.SimpleBindRequest;
|
||||||
import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl;
|
import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.core.internal.io.IOUtils;
|
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.ActionRunnable;
|
import org.elasticsearch.action.ActionRunnable;
|
||||||
|
@ -25,6 +24,7 @@ import org.elasticsearch.common.settings.SecureString;
|
||||||
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.common.util.concurrent.AbstractRunnable;
|
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
||||||
|
import org.elasticsearch.core.internal.io.IOUtils;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
||||||
|
@ -66,14 +66,9 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
|
||||||
|
|
||||||
ActiveDirectorySessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool) throws LDAPException {
|
ActiveDirectorySessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool) throws LDAPException {
|
||||||
super(config, sslService, new ActiveDirectoryGroupsResolver(config.settings()),
|
super(config, sslService, new ActiveDirectoryGroupsResolver(config.settings()),
|
||||||
ActiveDirectorySessionFactorySettings.POOL_ENABLED, () -> {
|
ActiveDirectorySessionFactorySettings.POOL_ENABLED,
|
||||||
if (PoolingSessionFactorySettings.BIND_DN.exists(config.settings())) {
|
PoolingSessionFactorySettings.BIND_DN.exists(config.settings())? getBindDN(config.settings()) : null,
|
||||||
return new SimpleBindRequest(getBindDN(config.settings()),
|
() -> {
|
||||||
PoolingSessionFactorySettings.BIND_PASSWORD.get(config.settings()));
|
|
||||||
} else {
|
|
||||||
return new SimpleBindRequest();
|
|
||||||
}
|
|
||||||
}, () -> {
|
|
||||||
if (PoolingSessionFactorySettings.BIND_DN.exists(config.settings())) {
|
if (PoolingSessionFactorySettings.BIND_DN.exists(config.settings())) {
|
||||||
final String healthCheckDn = PoolingSessionFactorySettings.BIND_DN.get(config.settings());
|
final String healthCheckDn = PoolingSessionFactorySettings.BIND_DN.get(config.settings());
|
||||||
if (healthCheckDn.isEmpty() && healthCheckDn.indexOf('=') > 0) {
|
if (healthCheckDn.isEmpty() && healthCheckDn.indexOf('=') > 0) {
|
||||||
|
@ -155,9 +150,7 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final LDAPConnection connection = LdapUtils.privilegedConnect(serverSet::getConnection);
|
final LDAPConnection connection = LdapUtils.privilegedConnect(serverSet::getConnection);
|
||||||
final SimpleBindRequest bind = new SimpleBindRequest(getBindDN(config.settings()),
|
LdapUtils.maybeForkThenBind(connection, bindCredentials, threadPool, new AbstractRunnable() {
|
||||||
PoolingSessionFactorySettings.BIND_PASSWORD.get(config.settings()));
|
|
||||||
LdapUtils.maybeForkThenBind(connection, bind, threadPool, new AbstractRunnable() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
|
@ -225,7 +218,7 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
|
||||||
final LdapSearchScope userSearchScope;
|
final LdapSearchScope userSearchScope;
|
||||||
final String userSearchFilter;
|
final String userSearchFilter;
|
||||||
final String bindDN;
|
final String bindDN;
|
||||||
final String bindPassword; // TODO this needs to be a setting in the secure settings store!
|
final SecureString bindPassword;
|
||||||
final ThreadPool threadPool;
|
final ThreadPool threadPool;
|
||||||
|
|
||||||
ADAuthenticator(RealmConfig realm, TimeValue timeout, boolean ignoreReferralErrors, Logger logger, GroupsResolver groupsResolver,
|
ADAuthenticator(RealmConfig realm, TimeValue timeout, boolean ignoreReferralErrors, Logger logger, GroupsResolver groupsResolver,
|
||||||
|
@ -239,7 +232,7 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
|
||||||
this.metaDataResolver = metaDataResolver;
|
this.metaDataResolver = metaDataResolver;
|
||||||
final Settings settings = realm.settings();
|
final Settings settings = realm.settings();
|
||||||
this.bindDN = getBindDN(settings);
|
this.bindDN = getBindDN(settings);
|
||||||
this.bindPassword = PoolingSessionFactorySettings.BIND_PASSWORD.get(settings);
|
this.bindPassword = PoolingSessionFactorySettings.SECURE_BIND_PASSWORD.get(settings);
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
userSearchDN = settings.get(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_BASEDN_SETTING, domainDN);
|
userSearchDN = settings.get(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_BASEDN_SETTING, domainDN);
|
||||||
userSearchScope = LdapSearchScope.resolve(settings.get(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_SCOPE_SETTING),
|
userSearchScope = LdapSearchScope.resolve(settings.get(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_SCOPE_SETTING),
|
||||||
|
@ -274,7 +267,7 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
|
||||||
if (bindDN.isEmpty()) {
|
if (bindDN.isEmpty()) {
|
||||||
searchRunnable.run();
|
searchRunnable.run();
|
||||||
} else {
|
} else {
|
||||||
final SimpleBindRequest bind = new SimpleBindRequest(bindDN, bindPassword);
|
final SimpleBindRequest bind = new SimpleBindRequest(bindDN, CharArrays.toUtf8Bytes(bindPassword.getChars()));
|
||||||
LdapUtils.maybeForkThenBind(connection, bind, threadPool, searchRunnable);
|
LdapUtils.maybeForkThenBind(connection, bind, threadPool, searchRunnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,7 +432,7 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
|
||||||
final byte[] passwordBytes = CharArrays.toUtf8Bytes(password.getChars());
|
final byte[] passwordBytes = CharArrays.toUtf8Bytes(password.getChars());
|
||||||
final SimpleBindRequest bind = bindDN.isEmpty()
|
final SimpleBindRequest bind = bindDN.isEmpty()
|
||||||
? new SimpleBindRequest(username, passwordBytes)
|
? new SimpleBindRequest(username, passwordBytes)
|
||||||
: new SimpleBindRequest(bindDN, bindPassword);
|
: new SimpleBindRequest(bindDN, CharArrays.toUtf8Bytes(bindPassword.getChars()));
|
||||||
LdapUtils.maybeForkThenBind(searchConnection, bind, threadPool, new ActionRunnable<String>(listener) {
|
LdapUtils.maybeForkThenBind(searchConnection, bind, threadPool, new ActionRunnable<String>(listener) {
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() throws Exception {
|
protected void doRun() throws Exception {
|
||||||
|
|
|
@ -12,17 +12,16 @@ import com.unboundid.ldap.sdk.LDAPException;
|
||||||
import com.unboundid.ldap.sdk.LDAPInterface;
|
import com.unboundid.ldap.sdk.LDAPInterface;
|
||||||
import com.unboundid.ldap.sdk.SearchResultEntry;
|
import com.unboundid.ldap.sdk.SearchResultEntry;
|
||||||
import com.unboundid.ldap.sdk.SimpleBindRequest;
|
import com.unboundid.ldap.sdk.SimpleBindRequest;
|
||||||
import org.elasticsearch.core.internal.io.IOUtils;
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.ActionRunnable;
|
import org.elasticsearch.action.ActionRunnable;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
||||||
|
import org.elasticsearch.core.internal.io.IOUtils;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.ldap.LdapUserSearchSessionFactorySettings;
|
import org.elasticsearch.xpack.core.security.authc.ldap.LdapUserSearchSessionFactorySettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
|
|
||||||
import org.elasticsearch.xpack.core.security.authc.ldap.SearchGroupsResolverSettings;
|
import org.elasticsearch.xpack.core.security.authc.ldap.SearchGroupsResolverSettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope;
|
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope;
|
||||||
import org.elasticsearch.xpack.core.security.authc.support.CharArrays;
|
import org.elasticsearch.xpack.core.security.authc.support.CharArrays;
|
||||||
|
@ -31,6 +30,7 @@ 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.LdapUtils;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.BIND_DN;
|
||||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
|
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
|
||||||
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.searchForEntry;
|
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.searchForEntry;
|
||||||
|
@ -45,10 +45,10 @@ class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
||||||
|
|
||||||
LdapUserSearchSessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool) throws LDAPException {
|
LdapUserSearchSessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool) throws LDAPException {
|
||||||
super(config, sslService, groupResolver(config.settings()), LdapUserSearchSessionFactorySettings.POOL_ENABLED,
|
super(config, sslService, groupResolver(config.settings()), LdapUserSearchSessionFactorySettings.POOL_ENABLED,
|
||||||
() -> LdapUserSearchSessionFactory.bindRequest(config.settings()),
|
BIND_DN.exists(config.settings()) ? BIND_DN.get(config.settings()) : null,
|
||||||
() -> {
|
() -> {
|
||||||
if (PoolingSessionFactorySettings.BIND_DN.exists(config.settings())) {
|
if (BIND_DN.exists(config.settings())) {
|
||||||
return PoolingSessionFactorySettings.BIND_DN.get(config.settings());
|
return BIND_DN.get(config.settings());
|
||||||
} else {
|
} else {
|
||||||
return LdapUserSearchSessionFactorySettings.SEARCH_BASE_DN.get(config.settings());
|
return LdapUserSearchSessionFactorySettings.SEARCH_BASE_DN.get(config.settings());
|
||||||
}
|
}
|
||||||
|
@ -66,15 +66,6 @@ class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
||||||
config.name(), userSearchBaseDn, searchFilter);
|
config.name(), userSearchBaseDn, searchFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SimpleBindRequest bindRequest(Settings settings) {
|
|
||||||
if (PoolingSessionFactorySettings.BIND_DN.exists(settings)) {
|
|
||||||
return new SimpleBindRequest(PoolingSessionFactorySettings.BIND_DN.get(settings),
|
|
||||||
PoolingSessionFactorySettings.BIND_PASSWORD.get(settings));
|
|
||||||
} else {
|
|
||||||
return new SimpleBindRequest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean hasUserSearchSettings(RealmConfig config) {
|
static boolean hasUserSearchSettings(RealmConfig config) {
|
||||||
return config.settings().getByPrefix("user_search.").isEmpty() == false;
|
return config.settings().getByPrefix("user_search.").isEmpty() == false;
|
||||||
}
|
}
|
||||||
|
@ -118,8 +109,7 @@ class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
||||||
void getSessionWithoutPool(String user, SecureString password, ActionListener<LdapSession> listener) {
|
void getSessionWithoutPool(String user, SecureString password, ActionListener<LdapSession> listener) {
|
||||||
try {
|
try {
|
||||||
final LDAPConnection connection = LdapUtils.privilegedConnect(serverSet::getConnection);
|
final LDAPConnection connection = LdapUtils.privilegedConnect(serverSet::getConnection);
|
||||||
final SimpleBindRequest bind = bindRequest(config.settings());
|
LdapUtils.maybeForkThenBind(connection, bindCredentials, threadPool, new AbstractRunnable() {
|
||||||
LdapUtils.maybeForkThenBind(connection, bind, threadPool, new AbstractRunnable() {
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() throws Exception {
|
protected void doRun() throws Exception {
|
||||||
findUser(user, connection, ActionListener.wrap((entry) -> {
|
findUser(user, connection, ActionListener.wrap((entry) -> {
|
||||||
|
@ -133,7 +123,7 @@ class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
||||||
LdapUtils.maybeForkThenBind(connection, userBind, threadPool, new AbstractRunnable() {
|
LdapUtils.maybeForkThenBind(connection, userBind, threadPool, new AbstractRunnable() {
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() throws Exception {
|
protected void doRun() throws Exception {
|
||||||
LdapUtils.maybeForkThenBind(connection, bind, threadPool, new AbstractRunnable() {
|
LdapUtils.maybeForkThenBind(connection, bindCredentials, threadPool, new AbstractRunnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() throws Exception {
|
protected void doRun() throws Exception {
|
||||||
|
@ -195,8 +185,7 @@ class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
||||||
void getUnauthenticatedSessionWithoutPool(String user, ActionListener<LdapSession> listener) {
|
void getUnauthenticatedSessionWithoutPool(String user, ActionListener<LdapSession> listener) {
|
||||||
try {
|
try {
|
||||||
final LDAPConnection connection = LdapUtils.privilegedConnect(serverSet::getConnection);
|
final LDAPConnection connection = LdapUtils.privilegedConnect(serverSet::getConnection);
|
||||||
final SimpleBindRequest bind = bindRequest(config.settings());
|
LdapUtils.maybeForkThenBind(connection, bindCredentials, threadPool, new AbstractRunnable() {
|
||||||
LdapUtils.maybeForkThenBind(connection, bind, threadPool, new AbstractRunnable() {
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() throws Exception {
|
protected void doRun() throws Exception {
|
||||||
findUser(user, connection, ActionListener.wrap((entry) -> {
|
findUser(user, connection, ActionListener.wrap((entry) -> {
|
||||||
|
|
|
@ -11,9 +11,11 @@ import com.unboundid.ldap.sdk.LDAPConnectionPool;
|
||||||
import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck;
|
import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck;
|
||||||
import com.unboundid.ldap.sdk.LDAPException;
|
import com.unboundid.ldap.sdk.LDAPException;
|
||||||
import com.unboundid.ldap.sdk.ServerSet;
|
import com.unboundid.ldap.sdk.ServerSet;
|
||||||
|
import com.unboundid.ldap.sdk.SimpleBindRequest;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.lease.Releasable;
|
import org.elasticsearch.common.lease.Releasable;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
|
@ -23,6 +25,7 @@ import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
|
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
|
||||||
|
import org.elasticsearch.xpack.core.security.authc.support.CharArrays;
|
||||||
import org.elasticsearch.xpack.core.ssl.SSLService;
|
import org.elasticsearch.xpack.core.ssl.SSLService;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapMetaDataResolver;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapMetaDataResolver;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
|
||||||
|
@ -31,6 +34,9 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.LEGACY_BIND_PASSWORD;
|
||||||
|
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.SECURE_BIND_PASSWORD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for LDAP session factories that can make use of a connection pool
|
* Base class for LDAP session factories that can make use of a connection pool
|
||||||
*/
|
*/
|
||||||
|
@ -39,6 +45,7 @@ abstract class PoolingSessionFactory extends SessionFactory implements Releasabl
|
||||||
private final boolean useConnectionPool;
|
private final boolean useConnectionPool;
|
||||||
private final LDAPConnectionPool connectionPool;
|
private final LDAPConnectionPool connectionPool;
|
||||||
|
|
||||||
|
final SimpleBindRequest bindCredentials;
|
||||||
final LdapMetaDataResolver metaDataResolver;
|
final LdapMetaDataResolver metaDataResolver;
|
||||||
final LdapSession.GroupsResolver groupResolver;
|
final LdapSession.GroupsResolver groupResolver;
|
||||||
|
|
||||||
|
@ -48,19 +55,41 @@ abstract class PoolingSessionFactory extends SessionFactory implements Releasabl
|
||||||
* @param sslService the ssl service to get a socket factory or context from
|
* @param sslService the ssl service to get a socket factory or context from
|
||||||
* @param groupResolver the resolver to use to find groups belonging to a user
|
* @param groupResolver the resolver to use to find groups belonging to a user
|
||||||
* @param poolingEnabled the setting that should be used to determine if connection pooling is enabled
|
* @param poolingEnabled the setting that should be used to determine if connection pooling is enabled
|
||||||
* @param bindRequestSupplier the supplier for a bind requests that should be used for pooled connections
|
* @param bindDn the DN of the user to be used for pooled connections (or null to perform anonymous bind)
|
||||||
* @param healthCheckDNSupplier a supplier for the dn to query for health checks
|
* @param healthCheckDNSupplier a supplier for the dn to query for health checks
|
||||||
* @param threadPool a thread pool used for async queries execution
|
* @param threadPool a thread pool used for async queries execution
|
||||||
*/
|
*/
|
||||||
PoolingSessionFactory(RealmConfig config, SSLService sslService, LdapSession.GroupsResolver groupResolver,
|
PoolingSessionFactory(RealmConfig config, SSLService sslService, LdapSession.GroupsResolver groupResolver,
|
||||||
Setting<Boolean> poolingEnabled, Supplier<BindRequest> bindRequestSupplier, Supplier<String> healthCheckDNSupplier,
|
Setting<Boolean> poolingEnabled, @Nullable String bindDn, Supplier<String> healthCheckDNSupplier,
|
||||||
ThreadPool threadPool) throws LDAPException {
|
ThreadPool threadPool) throws LDAPException {
|
||||||
super(config, sslService, threadPool);
|
super(config, sslService, threadPool);
|
||||||
this.groupResolver = groupResolver;
|
this.groupResolver = groupResolver;
|
||||||
this.metaDataResolver = new LdapMetaDataResolver(config.settings(), ignoreReferralErrors);
|
this.metaDataResolver = new LdapMetaDataResolver(config.settings(), ignoreReferralErrors);
|
||||||
|
|
||||||
|
final byte[] bindPassword;
|
||||||
|
if (LEGACY_BIND_PASSWORD.exists(config.settings())) {
|
||||||
|
if (SECURE_BIND_PASSWORD.exists(config.settings())) {
|
||||||
|
throw new IllegalArgumentException("You cannot specify both ["
|
||||||
|
+ RealmSettings.getFullSettingKey(config, LEGACY_BIND_PASSWORD) + "] and ["
|
||||||
|
+ RealmSettings.getFullSettingKey(config, SECURE_BIND_PASSWORD) + "]");
|
||||||
|
} else {
|
||||||
|
bindPassword = CharArrays.toUtf8Bytes(LEGACY_BIND_PASSWORD.get(config.settings()).getChars());
|
||||||
|
}
|
||||||
|
} else if (SECURE_BIND_PASSWORD.exists(config.settings())) {
|
||||||
|
bindPassword = CharArrays.toUtf8Bytes(SECURE_BIND_PASSWORD.get(config.settings()).getChars());
|
||||||
|
} else {
|
||||||
|
bindPassword = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindDn == null) {
|
||||||
|
bindCredentials = new SimpleBindRequest();
|
||||||
|
} else {
|
||||||
|
bindCredentials = new SimpleBindRequest(bindDn, bindPassword);
|
||||||
|
}
|
||||||
|
|
||||||
this.useConnectionPool = poolingEnabled.get(config.settings());
|
this.useConnectionPool = poolingEnabled.get(config.settings());
|
||||||
if (useConnectionPool) {
|
if (useConnectionPool) {
|
||||||
this.connectionPool = createConnectionPool(config, serverSet, timeout, logger, bindRequestSupplier, healthCheckDNSupplier);
|
this.connectionPool = createConnectionPool(config, serverSet, timeout, logger, bindCredentials, healthCheckDNSupplier);
|
||||||
} else {
|
} else {
|
||||||
this.connectionPool = null;
|
this.connectionPool = null;
|
||||||
}
|
}
|
||||||
|
@ -111,10 +140,9 @@ abstract class PoolingSessionFactory extends SessionFactory implements Releasabl
|
||||||
* Creates the connection pool that will be used by the session factory and initializes the health check support
|
* Creates the connection pool that will be used by the session factory and initializes the health check support
|
||||||
*/
|
*/
|
||||||
static LDAPConnectionPool createConnectionPool(RealmConfig config, ServerSet serverSet, TimeValue timeout, Logger logger,
|
static LDAPConnectionPool createConnectionPool(RealmConfig config, ServerSet serverSet, TimeValue timeout, Logger logger,
|
||||||
Supplier<BindRequest> bindRequestSupplier,
|
BindRequest bindRequest,
|
||||||
Supplier<String> healthCheckDnSupplier) throws LDAPException {
|
Supplier<String> healthCheckDnSupplier) throws LDAPException {
|
||||||
Settings settings = config.settings();
|
Settings settings = config.settings();
|
||||||
BindRequest bindRequest = bindRequestSupplier.get();
|
|
||||||
final int initialSize = PoolingSessionFactorySettings.POOL_INITIAL_SIZE.get(settings);
|
final int initialSize = PoolingSessionFactorySettings.POOL_INITIAL_SIZE.get(settings);
|
||||||
final int size = PoolingSessionFactorySettings.POOL_SIZE.get(settings);
|
final int size = PoolingSessionFactorySettings.POOL_SIZE.get(settings);
|
||||||
LDAPConnectionPool pool = null;
|
LDAPConnectionPool pool = null;
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.test;
|
package org.elasticsearch.test;
|
||||||
|
|
||||||
import org.bouncycastle.operator.OperatorCreationException;
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.inject.Guice;
|
import org.elasticsearch.common.inject.Guice;
|
||||||
import org.elasticsearch.common.inject.Injector;
|
import org.elasticsearch.common.inject.Injector;
|
||||||
|
@ -14,17 +13,13 @@ import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.settings.SettingsFilter;
|
import org.elasticsearch.common.settings.SettingsFilter;
|
||||||
import org.elasticsearch.common.settings.SettingsModule;
|
import org.elasticsearch.common.settings.SettingsModule;
|
||||||
import org.elasticsearch.xpack.core.XPackPlugin;
|
|
||||||
import org.elasticsearch.xpack.core.XPackSettings;
|
import org.elasticsearch.xpack.core.XPackSettings;
|
||||||
|
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
|
||||||
import org.elasticsearch.xpack.security.LocalStateSecurity;
|
import org.elasticsearch.xpack.security.LocalStateSecurity;
|
||||||
import org.elasticsearch.xpack.security.Security;
|
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import javax.security.auth.DestroyFailedException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -40,6 +35,8 @@ public class SettingsFilterTests extends ESTestCase {
|
||||||
private MockSecureSettings mockSecureSettings = new MockSecureSettings();
|
private MockSecureSettings mockSecureSettings = new MockSecureSettings();
|
||||||
|
|
||||||
public void testFiltering() throws Exception {
|
public void testFiltering() throws Exception {
|
||||||
|
final boolean useLegacyLdapBindPassword = randomBoolean();
|
||||||
|
|
||||||
configureUnfilteredSetting("xpack.security.authc.realms.file.type", "file");
|
configureUnfilteredSetting("xpack.security.authc.realms.file.type", "file");
|
||||||
|
|
||||||
// ldap realm filtering
|
// ldap realm filtering
|
||||||
|
@ -48,7 +45,11 @@ public class SettingsFilterTests extends ESTestCase {
|
||||||
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", Boolean.toString(randomBoolean()));
|
configureFilteredSetting("xpack.security.authc.realms.ldap1.hostname_verification", Boolean.toString(randomBoolean()));
|
||||||
configureFilteredSetting("xpack.security.authc.realms.ldap1.bind_dn", randomAlphaOfLength(5));
|
configureFilteredSetting("xpack.security.authc.realms.ldap1.bind_dn", randomAlphaOfLength(5));
|
||||||
|
if (useLegacyLdapBindPassword) {
|
||||||
configureFilteredSetting("xpack.security.authc.realms.ldap1.bind_password", randomAlphaOfLength(5));
|
configureFilteredSetting("xpack.security.authc.realms.ldap1.bind_password", randomAlphaOfLength(5));
|
||||||
|
} else {
|
||||||
|
configureSecureSetting("xpack.security.authc.realms.ldap1.secure_bind_password", randomAlphaOfLengthBetween(3, 8));
|
||||||
|
}
|
||||||
|
|
||||||
// active directory filtering
|
// active directory filtering
|
||||||
configureUnfilteredSetting("xpack.security.authc.realms.ad1.type", "active_directory");
|
configureUnfilteredSetting("xpack.security.authc.realms.ad1.type", "active_directory");
|
||||||
|
@ -93,7 +94,7 @@ public class SettingsFilterTests extends ESTestCase {
|
||||||
|
|
||||||
// custom settings, potentially added by a plugin
|
// custom settings, potentially added by a plugin
|
||||||
configureFilteredSetting("foo.bar", "_secret");
|
configureFilteredSetting("foo.bar", "_secret");
|
||||||
configureFilteredSetting("foo.baz", "_secret");;
|
configureFilteredSetting("foo.baz", "_secret");
|
||||||
configureFilteredSetting("bar.baz", "_secret");
|
configureFilteredSetting("bar.baz", "_secret");
|
||||||
configureUnfilteredSetting("baz.foo", "_not_a_secret");
|
configureUnfilteredSetting("baz.foo", "_not_a_secret");
|
||||||
configureFilteredSetting("xpack.security.hide_settings", "foo.*,bar.baz");
|
configureFilteredSetting("xpack.security.hide_settings", "foo.*,bar.baz");
|
||||||
|
@ -124,6 +125,10 @@ public class SettingsFilterTests extends ESTestCase {
|
||||||
for (Map.Entry<String, Matcher> entry : settingsMatcherMap.entrySet()) {
|
for (Map.Entry<String, Matcher> entry : settingsMatcherMap.entrySet()) {
|
||||||
assertThat(filteredSettings.get(entry.getKey()), entry.getValue());
|
assertThat(filteredSettings.get(entry.getKey()), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useLegacyLdapBindPassword) {
|
||||||
|
assertSettingDeprecationsAndWarnings(new Setting<?>[]{PoolingSessionFactorySettings.LEGACY_BIND_PASSWORD});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureUnfilteredSetting(String settingName, String value) {
|
private void configureUnfilteredSetting(String settingName, String value) {
|
||||||
|
|
|
@ -12,9 +12,9 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.SecuritySettingsSource;
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.core.XPackSettings;
|
import org.elasticsearch.xpack.core.XPackSettings;
|
||||||
|
import org.elasticsearch.xpack.core.security.SecurityExtension;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
|
||||||
import org.elasticsearch.xpack.core.security.SecurityExtension;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -138,9 +138,12 @@ public class RealmSettingsTests extends ESTestCase {
|
||||||
private Settings.Builder ldapSettings(boolean userSearch, boolean groupSearch) {
|
private Settings.Builder ldapSettings(boolean userSearch, boolean groupSearch) {
|
||||||
final Settings.Builder builder = commonLdapSettings("ldap", true)
|
final Settings.Builder builder = commonLdapSettings("ldap", true)
|
||||||
.put("bind_dn", "elasticsearch")
|
.put("bind_dn", "elasticsearch")
|
||||||
.put("bind_password", "t0p_s3cr3t")
|
|
||||||
.put("follow_referrals", randomBoolean());
|
.put("follow_referrals", randomBoolean());
|
||||||
|
|
||||||
|
SecuritySettingsSource.addSecureSettings(builder, secureSettings -> {
|
||||||
|
secureSettings.setString("bind_password", "t0p_s3cr3t");
|
||||||
|
});
|
||||||
|
|
||||||
if (userSearch) {
|
if (userSearch) {
|
||||||
builder.put("user_search.base_dn", "o=people, dc=example, dc=com");
|
builder.put("user_search.base_dn", "o=people, dc=example, dc=com");
|
||||||
builder.put("user_search.scope", "sub_tree");
|
builder.put("user_search.scope", "sub_tree");
|
||||||
|
|
|
@ -14,7 +14,9 @@ import com.unboundid.ldap.sdk.schema.Schema;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.settings.MockSecureSettings;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
|
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.TestEnvironment;
|
import org.elasticsearch.env.TestEnvironment;
|
||||||
|
@ -63,12 +65,12 @@ import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Active Directory Realm tests that use the UnboundID In Memory Directory Server
|
* Active Directory Realm tests that use the UnboundID In Memory Directory Server
|
||||||
*
|
* <p>
|
||||||
* AD is not LDAPv3 compliant so a workaround is needed
|
* AD is not LDAPv3 compliant so a workaround is needed
|
||||||
* AD realm binds with userPrincipalName but this is not a valid DN, so we have to add a second userPrincipalName to the
|
* AD realm binds with userPrincipalName but this is not a valid DN, so we have to add a second userPrincipalName to the
|
||||||
* users in the ldif in the form of CN=user@domain.com or a set the sAMAccountName to CN=user when testing authentication
|
* users in the ldif in the form of CN=user@domain.com or a set the sAMAccountName to CN=user when testing authentication
|
||||||
* with the sAMAccountName field.
|
* with the sAMAccountName field.
|
||||||
*
|
* <p>
|
||||||
* The username used to authenticate then has to be in the form of CN=user. Finally the username needs to be added as an
|
* The username used to authenticate then has to be in the form of CN=user. Finally the username needs to be added as an
|
||||||
* additional bind DN with a password in the test setup since it really is not a DN in the ldif file
|
* additional bind DN with a password in the test setup since it really is not a DN in the ldif file
|
||||||
*/
|
*/
|
||||||
|
@ -248,11 +250,18 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doUnauthenticatedLookup(boolean pooled) throws Exception {
|
private void doUnauthenticatedLookup(boolean pooled) throws Exception {
|
||||||
Settings settings = settings(Settings.builder()
|
final Settings.Builder builder = Settings.builder()
|
||||||
.put(ActiveDirectorySessionFactorySettings.POOL_ENABLED.getKey(), pooled)
|
.put(ActiveDirectorySessionFactorySettings.POOL_ENABLED.getKey(), pooled)
|
||||||
.put(PoolingSessionFactorySettings.BIND_DN.getKey(), "CN=ironman@ad.test.elasticsearch.com")
|
.put(PoolingSessionFactorySettings.BIND_DN.getKey(), "CN=ironman@ad.test.elasticsearch.com");
|
||||||
.put(PoolingSessionFactorySettings.BIND_PASSWORD.getKey(), PASSWORD)
|
final boolean useLegacyBindPassword = randomBoolean();
|
||||||
.build());
|
if (useLegacyBindPassword) {
|
||||||
|
builder.put(PoolingSessionFactorySettings.LEGACY_BIND_PASSWORD.getKey(), PASSWORD);
|
||||||
|
} else {
|
||||||
|
final MockSecureSettings secureSettings = new MockSecureSettings();
|
||||||
|
secureSettings.setString(PoolingSessionFactorySettings.SECURE_BIND_PASSWORD.getKey(), PASSWORD);
|
||||||
|
builder.setSecureSettings(secureSettings);
|
||||||
|
}
|
||||||
|
Settings settings = settings(builder.build());
|
||||||
RealmConfig config = new RealmConfig("testUnauthenticatedLookupWithConnectionPool", settings, globalSettings,
|
RealmConfig config = new RealmConfig("testUnauthenticatedLookupWithConnectionPool", settings, globalSettings,
|
||||||
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
try (ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool)) {
|
try (ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool)) {
|
||||||
|
|
|
@ -8,6 +8,8 @@ package org.elasticsearch.xpack.security.authc.ldap;
|
||||||
import com.unboundid.ldap.sdk.LDAPURL;
|
import com.unboundid.ldap.sdk.LDAPURL;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
|
import org.elasticsearch.common.settings.MockSecureSettings;
|
||||||
|
import org.elasticsearch.common.settings.SecureSettings;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
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;
|
||||||
|
@ -229,7 +231,7 @@ public class LdapRealmTests extends LdapTestCase {
|
||||||
.putList(URLS_SETTING, ldapUrls())
|
.putList(URLS_SETTING, ldapUrls())
|
||||||
.put("user_search.base_dn", "")
|
.put("user_search.base_dn", "")
|
||||||
.put("bind_dn", "cn=Thomas Masterman Hardy,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Thomas Masterman Hardy,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", PASSWORD)
|
.setSecureSettings(secureSettings("secure_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("ssl.verification_mode", VerificationMode.CERTIFICATE)
|
.put("ssl.verification_mode", VerificationMode.CERTIFICATE)
|
||||||
|
@ -243,6 +245,12 @@ public class LdapRealmTests extends LdapTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MockSecureSettings secureSettings(String key, String value) {
|
||||||
|
final MockSecureSettings secureSettings = new MockSecureSettings();
|
||||||
|
secureSettings.setString(key, value);
|
||||||
|
return secureSettings;
|
||||||
|
}
|
||||||
|
|
||||||
public void testLdapRealmThrowsExceptionForUserTemplateAndSearchSettings() throws Exception {
|
public void testLdapRealmThrowsExceptionForUserTemplateAndSearchSettings() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.putList(URLS_SETTING, ldapUrls())
|
.putList(URLS_SETTING, ldapUrls())
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
package org.elasticsearch.xpack.security.authc.ldap;
|
package org.elasticsearch.xpack.security.authc.ldap;
|
||||||
|
|
||||||
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
||||||
import com.unboundid.ldap.sdk.BindRequest;
|
|
||||||
import com.unboundid.ldap.sdk.GetEntryLDAPConnectionPoolHealthCheck;
|
import com.unboundid.ldap.sdk.GetEntryLDAPConnectionPoolHealthCheck;
|
||||||
import com.unboundid.ldap.sdk.LDAPConnectionPool;
|
import com.unboundid.ldap.sdk.LDAPConnectionPool;
|
||||||
import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck;
|
import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck;
|
||||||
|
@ -39,6 +38,8 @@ import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
@ -87,8 +88,8 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, "", LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, "", LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", "")
|
.put("user_search.base_dn", "")
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.pool.enabled", randomBoolean());
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(builder);
|
||||||
if (useAttribute) {
|
if (useAttribute) {
|
||||||
builder.put("user_search.attribute", "cn");
|
builder.put("user_search.attribute", "cn");
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,11 +106,8 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAttribute) {
|
assertDeprecationWarnings(useAttribute, useLegacyBindPassword);
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void testUserSearchSubTree() throws Exception {
|
public void testUserSearchSubTree() throws Exception {
|
||||||
String groupSearchBase = "o=sevenSeas";
|
String groupSearchBase = "o=sevenSeas";
|
||||||
|
@ -120,8 +118,8 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.pool.enabled", randomBoolean());
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(builder);
|
||||||
if (useAttribute) {
|
if (useAttribute) {
|
||||||
builder.put("user_search.attribute", "cn");
|
builder.put("user_search.attribute", "cn");
|
||||||
} else {
|
} else {
|
||||||
|
@ -134,19 +132,18 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
|
|
||||||
String user = "William Bush";
|
String user = "William Bush";
|
||||||
SecureString userPass = new SecureString("pass");
|
SecureString userPass = new SecureString("pass");
|
||||||
final SimpleBindRequest bindDNBindRequest = LdapUserSearchSessionFactory.bindRequest(config.settings());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// auth
|
// auth
|
||||||
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString(user));
|
assertThat(dn, containsString(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
//lookup
|
//lookup
|
||||||
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString(user));
|
assertThat(dn, containsString(user));
|
||||||
}
|
}
|
||||||
|
@ -154,9 +151,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAttribute) {
|
assertDeprecationWarnings(useAttribute, useLegacyBindPassword);
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserSearchBaseScopeFailsWithWrongBaseDN() throws Exception {
|
public void testUserSearchBaseScopeFailsWithWrongBaseDN() throws Exception {
|
||||||
|
@ -168,9 +163,9 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.scope", LdapSearchScope.BASE)
|
.put("user_search.scope", LdapSearchScope.BASE)
|
||||||
.put("user_search.pool.enabled", randomBoolean());
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(builder);
|
||||||
if (useAttribute) {
|
if (useAttribute) {
|
||||||
builder.put("user_search.attribute", "cn");
|
builder.put("user_search.attribute", "cn");
|
||||||
} else {
|
} else {
|
||||||
|
@ -191,9 +186,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAttribute) {
|
assertDeprecationWarnings(useAttribute, useLegacyBindPassword);
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserSearchBaseScopePassesWithCorrectBaseDN() throws Exception {
|
public void testUserSearchBaseScopePassesWithCorrectBaseDN() throws Exception {
|
||||||
|
@ -204,9 +197,9 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.scope", LdapSearchScope.BASE)
|
.put("user_search.scope", LdapSearchScope.BASE)
|
||||||
.put("user_search.pool.enabled", randomBoolean());
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(builder);
|
||||||
final boolean useAttribute = randomBoolean();
|
final boolean useAttribute = randomBoolean();
|
||||||
if (useAttribute) {
|
if (useAttribute) {
|
||||||
builder.put("user_search.attribute", "cn");
|
builder.put("user_search.attribute", "cn");
|
||||||
|
@ -220,19 +213,18 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
|
|
||||||
String user = "William Bush";
|
String user = "William Bush";
|
||||||
SecureString userPass = new SecureString("pass");
|
SecureString userPass = new SecureString("pass");
|
||||||
final SimpleBindRequest bindDNBindRequest = LdapUserSearchSessionFactory.bindRequest(config.settings());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// auth
|
// auth
|
||||||
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString(user));
|
assertThat(dn, containsString(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
//lookup
|
//lookup
|
||||||
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString(user));
|
assertThat(dn, containsString(user));
|
||||||
}
|
}
|
||||||
|
@ -240,9 +232,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAttribute) {
|
assertDeprecationWarnings(useAttribute, useLegacyBindPassword);
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserSearchOneLevelScopeFailsWithWrongBaseDN() throws Exception {
|
public void testUserSearchOneLevelScopeFailsWithWrongBaseDN() throws Exception {
|
||||||
|
@ -253,9 +243,9 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
||||||
.put("user_search.pool.enabled", randomBoolean());
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(builder);
|
||||||
final boolean useAttribute = randomBoolean();
|
final boolean useAttribute = randomBoolean();
|
||||||
if (useAttribute) {
|
if (useAttribute) {
|
||||||
builder.put("user_search.attribute", "cn");
|
builder.put("user_search.attribute", "cn");
|
||||||
|
@ -277,9 +267,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAttribute) {
|
assertDeprecationWarnings(useAttribute, useLegacyBindPassword);
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserSearchOneLevelScopePassesWithCorrectBaseDN() throws Exception {
|
public void testUserSearchOneLevelScopePassesWithCorrectBaseDN() throws Exception {
|
||||||
|
@ -290,9 +278,9 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
||||||
.put("user_search.pool.enabled", randomBoolean());
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(builder);
|
||||||
final boolean useAttribute = randomBoolean();
|
final boolean useAttribute = randomBoolean();
|
||||||
if (useAttribute) {
|
if (useAttribute) {
|
||||||
builder.put("user_search.attribute", "cn");
|
builder.put("user_search.attribute", "cn");
|
||||||
|
@ -306,19 +294,18 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
|
|
||||||
String user = "William Bush";
|
String user = "William Bush";
|
||||||
SecureString userPass = new SecureString("pass");
|
SecureString userPass = new SecureString("pass");
|
||||||
final SimpleBindRequest bindDNBindRequest = LdapUserSearchSessionFactory.bindRequest(config.settings());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//auth
|
//auth
|
||||||
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString(user));
|
assertThat(dn, containsString(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
//lookup
|
//lookup
|
||||||
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString(user));
|
assertThat(dn, containsString(user));
|
||||||
}
|
}
|
||||||
|
@ -326,9 +313,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAttribute) {
|
assertDeprecationWarnings(useAttribute, useLegacyBindPassword);
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserSearchWithBadAttributeFails() throws Exception {
|
public void testUserSearchWithBadAttributeFails() throws Exception {
|
||||||
|
@ -339,8 +324,8 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.pool.enabled", randomBoolean());
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(builder);
|
||||||
final boolean useAttribute = randomBoolean();
|
final boolean useAttribute = randomBoolean();
|
||||||
if (useAttribute) {
|
if (useAttribute) {
|
||||||
builder.put("user_search.attribute", "uid1");
|
builder.put("user_search.attribute", "uid1");
|
||||||
|
@ -362,61 +347,62 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAttribute) {
|
assertDeprecationWarnings(useAttribute, useLegacyBindPassword);
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserSearchWithoutAttributePasses() throws Exception {
|
public void testUserSearchWithoutAttributePasses() throws Exception {
|
||||||
String groupSearchBase = "o=sevenSeas";
|
String groupSearchBase = "o=sevenSeas";
|
||||||
String userSearchBase = "o=sevenSeas";
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
|
||||||
RealmConfig config = new RealmConfig("ldap_realm", Settings.builder()
|
final Settings.Builder realmSettings = Settings.builder()
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
.put("user_search.pool.enabled", randomBoolean())
|
final boolean useLegacyBindPassword = configureBindPassword(realmSettings);
|
||||||
.build(), globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
RealmConfig config = new RealmConfig("ldap_realm", realmSettings.build(), globalSettings,
|
||||||
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
|
|
||||||
LdapUserSearchSessionFactory sessionFactory = getLdapUserSearchSessionFactory(config, sslService, threadPool);
|
LdapUserSearchSessionFactory sessionFactory = getLdapUserSearchSessionFactory(config, sslService, threadPool);
|
||||||
|
|
||||||
String user = "wbush";
|
String user = "wbush";
|
||||||
SecureString userPass = new SecureString("pass");
|
SecureString userPass = new SecureString("pass");
|
||||||
final SimpleBindRequest bindDNBindRequest = LdapUserSearchSessionFactory.bindRequest(config.settings());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//auth
|
//auth
|
||||||
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
try (LdapSession ldap = session(sessionFactory, user, userPass)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString("William Bush"));
|
assertThat(dn, containsString("William Bush"));
|
||||||
}
|
}
|
||||||
|
|
||||||
//lookup
|
//lookup
|
||||||
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
try (LdapSession ldap = unauthenticatedSession(sessionFactory, user)) {
|
||||||
assertConnectionValid(ldap.getConnection(), bindDNBindRequest);
|
assertConnectionValid(ldap.getConnection(), sessionFactory.bindCredentials);
|
||||||
String dn = ldap.userDn();
|
String dn = ldap.userDn();
|
||||||
assertThat(dn, containsString("William Bush"));
|
assertThat(dn, containsString("William Bush"));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
sessionFactory.close();
|
sessionFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertDeprecationWarnings(false, useLegacyBindPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testConnectionPoolDefaultSettings() throws Exception {
|
public void testConnectionPoolDefaultSettings() throws Exception {
|
||||||
String groupSearchBase = "o=sevenSeas";
|
String groupSearchBase = "o=sevenSeas";
|
||||||
String userSearchBase = "o=sevenSeas";
|
String userSearchBase = "o=sevenSeas";
|
||||||
RealmConfig config = new RealmConfig("ldap_realm", Settings.builder()
|
final Settings.Builder realmSettings = Settings.builder()
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas");
|
||||||
.put("bind_password", "pass")
|
configureBindPassword(realmSettings);
|
||||||
.build(), globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
RealmConfig config = new RealmConfig("ldap_realm", realmSettings.build(), globalSettings,
|
||||||
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
|
|
||||||
LDAPConnectionPool connectionPool = LdapUserSearchSessionFactory.createConnectionPool(config, new SingleServerSet("localhost",
|
LDAPConnectionPool connectionPool = LdapUserSearchSessionFactory.createConnectionPool(config, new SingleServerSet("localhost",
|
||||||
randomFrom(ldapServers).getListenPort()), TimeValue.timeValueSeconds(5), NoOpLogger.INSTANCE,
|
randomFrom(ldapServers).getListenPort()), TimeValue.timeValueSeconds(5), NoOpLogger.INSTANCE,
|
||||||
() -> new SimpleBindRequest("cn=Horatio Hornblower,ou=people,o=sevenSeas", "pass"),
|
new SimpleBindRequest("cn=Horatio Hornblower,ou=people,o=sevenSeas", "pass"),
|
||||||
() -> "cn=Horatio Hornblower,ou=people,o=sevenSeas");
|
() -> "cn=Horatio Hornblower,ou=people,o=sevenSeas");
|
||||||
try {
|
try {
|
||||||
assertThat(connectionPool.getCurrentAvailableConnections(),
|
assertThat(connectionPool.getCurrentAvailableConnections(),
|
||||||
|
@ -435,19 +421,20 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
public void testConnectionPoolSettings() throws Exception {
|
public void testConnectionPoolSettings() throws Exception {
|
||||||
String groupSearchBase = "o=sevenSeas";
|
String groupSearchBase = "o=sevenSeas";
|
||||||
String userSearchBase = "o=sevenSeas";
|
String userSearchBase = "o=sevenSeas";
|
||||||
RealmConfig config = new RealmConfig("ldap_realm", Settings.builder()
|
final Settings.Builder realmSettings = Settings.builder()
|
||||||
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||||
.put("bind_password", "pass")
|
|
||||||
.put("user_search.pool.initial_size", 10)
|
.put("user_search.pool.initial_size", 10)
|
||||||
.put("user_search.pool.size", 12)
|
.put("user_search.pool.size", 12)
|
||||||
.put("user_search.pool.health_check.enabled", false)
|
.put("user_search.pool.health_check.enabled", false);
|
||||||
.build(), globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
configureBindPassword(realmSettings);
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", realmSettings.build(), globalSettings,
|
||||||
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
|
|
||||||
LDAPConnectionPool connectionPool = LdapUserSearchSessionFactory.createConnectionPool(config, new SingleServerSet("localhost",
|
LDAPConnectionPool connectionPool = LdapUserSearchSessionFactory.createConnectionPool(config, new SingleServerSet("localhost",
|
||||||
randomFrom(ldapServers).getListenPort()), TimeValue.timeValueSeconds(5), NoOpLogger.INSTANCE,
|
randomFrom(ldapServers).getListenPort()), TimeValue.timeValueSeconds(5), NoOpLogger.INSTANCE,
|
||||||
() -> new SimpleBindRequest("cn=Horatio Hornblower,ou=people,o=sevenSeas", "pass"),
|
new SimpleBindRequest("cn=Horatio Hornblower,ou=people,o=sevenSeas", "pass"),
|
||||||
() -> "cn=Horatio Hornblower,ou=people,o=sevenSeas");
|
() -> "cn=Horatio Hornblower,ou=people,o=sevenSeas");
|
||||||
try {
|
try {
|
||||||
assertThat(connectionPool.getCurrentAvailableConnections(), is(10));
|
assertThat(connectionPool.getCurrentAvailableConnections(), is(10));
|
||||||
|
@ -476,6 +463,8 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
searchSessionFactory.close();
|
searchSessionFactory.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertDeprecationWarnings(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatEmptyBindDNAndDisabledPoolingDoesNotThrow() throws Exception {
|
public void testThatEmptyBindDNAndDisabledPoolingDoesNotThrow() throws Exception {
|
||||||
|
@ -499,22 +488,41 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
searchSessionFactory.close();
|
searchSessionFactory.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertDeprecationWarnings(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEmptyBindDNReturnsAnonymousBindRequest() {
|
public void testEmptyBindDNReturnsAnonymousBindRequest() throws LDAPException {
|
||||||
SimpleBindRequest request = LdapUserSearchSessionFactory.bindRequest(Settings.builder().put("bind_password", "password").build());
|
String groupSearchBase = "o=sevenSeas";
|
||||||
assertThat(request, is(notNullValue()));
|
String userSearchBase = "o=sevenSeas";
|
||||||
assertThat(request.getBindDN(), isEmptyString());
|
final Settings.Builder realmSettings = Settings.builder()
|
||||||
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
|
.put("user_search.base_dn", userSearchBase);
|
||||||
|
final boolean useLegacyBindPassword = configureBindPassword(realmSettings);
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", realmSettings.build(), globalSettings,
|
||||||
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
|
try (LdapUserSearchSessionFactory searchSessionFactory = getLdapUserSearchSessionFactory(config, sslService, threadPool)) {
|
||||||
|
assertThat(searchSessionFactory.bindCredentials, notNullValue());
|
||||||
|
assertThat(searchSessionFactory.bindCredentials.getBindDN(), isEmptyString());
|
||||||
|
}
|
||||||
|
assertDeprecationWarnings(false, useLegacyBindPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatBindRequestReturnsSimpleBindRequest() {
|
public void testThatBindRequestReturnsSimpleBindRequest() throws LDAPException {
|
||||||
BindRequest request = LdapUserSearchSessionFactory.bindRequest(Settings.builder()
|
String groupSearchBase = "o=sevenSeas";
|
||||||
.put("bind_password", "password")
|
String userSearchBase = "o=sevenSeas";
|
||||||
|
final Settings.Builder realmSettings = Settings.builder()
|
||||||
|
.put(buildLdapSettings(ldapUrls(), Strings.EMPTY_ARRAY, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("bind_dn", "cn=ironman")
|
.put("bind_dn", "cn=ironman")
|
||||||
.build());
|
.put("user_search.base_dn", userSearchBase);
|
||||||
assertEquals(request.getClass(), SimpleBindRequest.class);
|
final boolean useLegacyBindPassword = configureBindPassword(realmSettings);
|
||||||
SimpleBindRequest simpleBindRequest = (SimpleBindRequest) request;
|
RealmConfig config = new RealmConfig("ldap_realm", realmSettings.build(), globalSettings,
|
||||||
assertThat(simpleBindRequest.getBindDN(), is("cn=ironman"));
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
|
try (LdapUserSearchSessionFactory searchSessionFactory = getLdapUserSearchSessionFactory(config, sslService, threadPool)) {
|
||||||
|
assertThat(searchSessionFactory.bindCredentials, notNullValue());
|
||||||
|
assertThat(searchSessionFactory.bindCredentials.getBindDN(), is("cn=ironman"));
|
||||||
|
}
|
||||||
|
assertDeprecationWarnings(false, useLegacyBindPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatConnectErrorIsNotThrownOnConstruction() throws Exception {
|
public void testThatConnectErrorIsNotThrownOnConstruction() throws Exception {
|
||||||
|
@ -526,20 +534,20 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
String ldapUrl = new LDAPURL("ldap", "localhost", inMemoryDirectoryServer.getListenPort(), null, null, null, null).toString();
|
String ldapUrl = new LDAPURL("ldap", "localhost", inMemoryDirectoryServer.getListenPort(), null, null, null, null).toString();
|
||||||
inMemoryDirectoryServer.shutDown(true);
|
inMemoryDirectoryServer.shutDown(true);
|
||||||
|
|
||||||
Settings ldapSettings = Settings.builder()
|
final Settings.Builder ldapSettingsBuilder = Settings.builder()
|
||||||
.put(LdapTestCase.buildLdapSettings(new String[] { ldapUrl }, Strings.EMPTY_ARRAY,
|
.put(LdapTestCase.buildLdapSettings(new String[]{ldapUrl}, Strings.EMPTY_ARRAY,
|
||||||
groupSearchBase, LdapSearchScope.SUB_TREE))
|
groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("bind_dn", "ironman@ad.test.elasticsearch.com")
|
.put("bind_dn", "ironman@ad.test.elasticsearch.com")
|
||||||
.put("bind_password", "password")
|
|
||||||
.put("user_search.attribute", "cn")
|
.put("user_search.attribute", "cn")
|
||||||
.put("timeout.tcp_connect", "500ms")
|
.put("timeout.tcp_connect", "500ms")
|
||||||
.put("type", "ldap")
|
.put("type", "ldap")
|
||||||
.put("user_search.pool.health_check.enabled", false)
|
.put("user_search.pool.health_check.enabled", false)
|
||||||
.put("user_search.pool.enabled", randomBoolean())
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
.build();
|
|
||||||
|
|
||||||
RealmConfig config = new RealmConfig("ldap_realm", ldapSettings, globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
final boolean useLegacyBindPassword = configureBindPassword(ldapSettingsBuilder);
|
||||||
|
RealmConfig config = new RealmConfig("ldap_realm", ldapSettingsBuilder.build(), globalSettings,
|
||||||
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
LdapUserSearchSessionFactory searchSessionFactory = null;
|
LdapUserSearchSessionFactory searchSessionFactory = null;
|
||||||
try {
|
try {
|
||||||
searchSessionFactory = getLdapUserSearchSessionFactory(config, sslService, threadPool);
|
searchSessionFactory = getLdapUserSearchSessionFactory(config, sslService, threadPool);
|
||||||
|
@ -549,7 +557,30 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertSettingDeprecationsAndWarnings(new Setting[] { LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE });
|
assertDeprecationWarnings(true, useLegacyBindPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertDeprecationWarnings(boolean useAttribute, boolean legacyBindPassword) {
|
||||||
|
List<Setting<?>> deprecatedSettings = new ArrayList<>();
|
||||||
|
if (useAttribute) {
|
||||||
|
deprecatedSettings.add(LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
if (legacyBindPassword) {
|
||||||
|
deprecatedSettings.add(PoolingSessionFactorySettings.LEGACY_BIND_PASSWORD);
|
||||||
|
}
|
||||||
|
if (deprecatedSettings.size() > 0) {
|
||||||
|
assertSettingDeprecationsAndWarnings(deprecatedSettings.toArray(new Setting<?>[deprecatedSettings.size()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean configureBindPassword(Settings.Builder builder) {
|
||||||
|
final boolean useLegacyBindPassword = randomBoolean();
|
||||||
|
if (useLegacyBindPassword) {
|
||||||
|
builder.put("bind_password", "pass");
|
||||||
|
} else {
|
||||||
|
builder.setSecureSettings(newSecureSettings("secure_bind_password", "pass"));
|
||||||
|
}
|
||||||
|
return useLegacyBindPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LdapUserSearchSessionFactory getLdapUserSearchSessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool)
|
static LdapUserSearchSessionFactory getLdapUserSearchSessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.settings.MockSecureSettings;
|
import org.elasticsearch.common.settings.MockSecureSettings;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
|
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.TestEnvironment;
|
import org.elasticsearch.env.TestEnvironment;
|
||||||
|
@ -17,6 +18,7 @@ import org.elasticsearch.test.OpenLdapTests;
|
||||||
import org.elasticsearch.threadpool.TestThreadPool;
|
import org.elasticsearch.threadpool.TestThreadPool;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
|
||||||
|
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope;
|
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope;
|
||||||
import org.elasticsearch.xpack.core.ssl.SSLService;
|
import org.elasticsearch.xpack.core.ssl.SSLService;
|
||||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
|
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
|
||||||
|
@ -41,6 +43,7 @@ public class OpenLdapUserSearchSessionFactoryTests extends ESTestCase {
|
||||||
|
|
||||||
private Settings globalSettings;
|
private Settings globalSettings;
|
||||||
private ThreadPool threadPool;
|
private ThreadPool threadPool;
|
||||||
|
private MockSecureSettings globalSecureSettings;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
|
@ -50,10 +53,11 @@ public class OpenLdapUserSearchSessionFactoryTests extends ESTestCase {
|
||||||
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
* If we re-use a SSLContext, previously connected sessions can get re-established which breaks hostname
|
||||||
* verification tests since a re-established connection does not perform hostname verification.
|
* verification tests since a re-established connection does not perform hostname verification.
|
||||||
*/
|
*/
|
||||||
|
globalSecureSettings = newSecureSettings("xpack.ssl.truststore.secure_password", "changeit");
|
||||||
globalSettings = Settings.builder()
|
globalSettings = Settings.builder()
|
||||||
.put("path.home", createTempDir())
|
.put("path.home", createTempDir())
|
||||||
.put("xpack.ssl.truststore.path", keystore)
|
.put("xpack.ssl.truststore.path", keystore)
|
||||||
.setSecureSettings(newSecureSettings("xpack.ssl.truststore.secure_password", "changeit"))
|
.setSecureSettings(globalSecureSettings)
|
||||||
.build();
|
.build();
|
||||||
threadPool = new TestThreadPool("LdapUserSearchSessionFactoryTests");
|
threadPool = new TestThreadPool("LdapUserSearchSessionFactoryTests");
|
||||||
}
|
}
|
||||||
|
@ -63,26 +67,40 @@ public class OpenLdapUserSearchSessionFactoryTests extends ESTestCase {
|
||||||
terminate(threadPool);
|
terminate(threadPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUserSearchwithBindUserOpenLDAP() throws Exception {
|
public void testUserSearchWithBindUserOpenLDAP() throws Exception {
|
||||||
|
final boolean useSecureBindPassword = randomBoolean();
|
||||||
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||||
String userSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
String userSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||||
RealmConfig config = new RealmConfig("oldap-test", Settings.builder()
|
final Settings.Builder realmSettings = Settings.builder()
|
||||||
.put(LdapTestCase.buildLdapSettings(new String[] { OpenLdapTests.OPEN_LDAP_DNS_URL }, Strings.EMPTY_ARRAY, groupSearchBase,
|
.put(LdapTestCase.buildLdapSettings(new String[]{OpenLdapTests.OPEN_LDAP_DNS_URL}, Strings.EMPTY_ARRAY, groupSearchBase,
|
||||||
LdapSearchScope.ONE_LEVEL))
|
LdapSearchScope.ONE_LEVEL))
|
||||||
.put("user_search.base_dn", userSearchBase)
|
.put("user_search.base_dn", userSearchBase)
|
||||||
.put("group_search.user_attribute", "uid")
|
.put("group_search.user_attribute", "uid")
|
||||||
.put("bind_dn", "uid=blackwidow,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
.put("bind_dn", "uid=blackwidow,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
||||||
.put("bind_password", OpenLdapTests.PASSWORD)
|
.put("user_search.pool.enabled", randomBoolean());
|
||||||
.put("user_search.pool.enabled", randomBoolean())
|
if (useSecureBindPassword) {
|
||||||
.build(), globalSettings, TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
final MockSecureSettings secureSettings = new MockSecureSettings();
|
||||||
|
secureSettings.setString("secure_bind_password", OpenLdapTests.PASSWORD);
|
||||||
|
realmSettings.setSecureSettings(secureSettings);
|
||||||
|
} else {
|
||||||
|
realmSettings.put("bind_password", OpenLdapTests.PASSWORD);
|
||||||
|
}
|
||||||
|
RealmConfig config = new RealmConfig("oldap-test", realmSettings.build(), globalSettings,
|
||||||
|
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
|
||||||
Settings.Builder builder = Settings.builder()
|
Settings.Builder builder = Settings.builder()
|
||||||
.put(globalSettings);
|
.put(globalSettings, false);
|
||||||
builder.put(Settings.builder().put(config.settings()).normalizePrefix("xpack.security.authc.realms.ldap.").build());
|
builder.put(Settings.builder().put(config.settings(), false).normalizePrefix("xpack.security.authc.realms.ldap.").build());
|
||||||
|
final MockSecureSettings secureSettings = new MockSecureSettings();
|
||||||
|
secureSettings.merge(globalSecureSettings);
|
||||||
|
if (useSecureBindPassword) {
|
||||||
|
secureSettings.setString("xpack.security.authc.realms.ldap.secure_bind_password", OpenLdapTests.PASSWORD);
|
||||||
|
}
|
||||||
|
builder.setSecureSettings(secureSettings);
|
||||||
Settings settings = builder.build();
|
Settings settings = builder.build();
|
||||||
SSLService sslService = new SSLService(settings, TestEnvironment.newEnvironment(settings));
|
SSLService sslService = new SSLService(settings, TestEnvironment.newEnvironment(settings));
|
||||||
|
|
||||||
|
|
||||||
String[] users = new String[] { "cap", "hawkeye", "hulk", "ironman", "thor" };
|
String[] users = new String[]{"cap", "hawkeye", "hulk", "ironman", "thor"};
|
||||||
try (LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, sslService, threadPool)) {
|
try (LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, sslService, threadPool)) {
|
||||||
for (String user : users) {
|
for (String user : users) {
|
||||||
//auth
|
//auth
|
||||||
|
@ -100,6 +118,10 @@ public class OpenLdapUserSearchSessionFactoryTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useSecureBindPassword == false) {
|
||||||
|
assertSettingDeprecationsAndWarnings(new Setting<?>[]{PoolingSessionFactorySettings.LEGACY_BIND_PASSWORD});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MockSecureSettings newSecureSettings(String key, String value) {
|
private MockSecureSettings newSecureSettings(String key, String value) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.action.ActionFuture;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.common.collect.MapBuilder;
|
import org.elasticsearch.common.collect.MapBuilder;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.core.security.action.user.AuthenticateAction;
|
import org.elasticsearch.xpack.core.security.action.user.AuthenticateAction;
|
||||||
import org.elasticsearch.xpack.core.security.action.user.AuthenticateRequest;
|
import org.elasticsearch.xpack.core.security.action.user.AuthenticateRequest;
|
||||||
import org.elasticsearch.xpack.core.security.action.user.AuthenticateResponse;
|
import org.elasticsearch.xpack.core.security.action.user.AuthenticateResponse;
|
||||||
|
@ -28,19 +29,30 @@ import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswo
|
||||||
*/
|
*/
|
||||||
public class ActiveDirectoryRunAsIT extends AbstractAdLdapRealmTestCase {
|
public class ActiveDirectoryRunAsIT extends AbstractAdLdapRealmTestCase {
|
||||||
|
|
||||||
|
private static boolean useLegacyBindPassword;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void selectRealmConfig() {
|
public static void selectRealmConfig() {
|
||||||
realmConfig = RealmConfig.AD;
|
realmConfig = RealmConfig.AD;
|
||||||
|
useLegacyBindPassword = randomBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Settings nodeSettings(int nodeOrdinal) {
|
protected Settings nodeSettings(int nodeOrdinal) {
|
||||||
|
useLegacyBindPassword = randomBoolean();
|
||||||
final Settings.Builder builder = Settings.builder().put(super.nodeSettings(nodeOrdinal));
|
final Settings.Builder builder = Settings.builder().put(super.nodeSettings(nodeOrdinal));
|
||||||
switch (realmConfig) {
|
switch (realmConfig) {
|
||||||
case AD:
|
case AD:
|
||||||
builder.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".bind_dn", "ironman@ad.test.elasticsearch.com")
|
builder.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".bind_dn", "ironman@ad.test.elasticsearch.com")
|
||||||
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".bind_password", ActiveDirectorySessionFactoryTests.PASSWORD)
|
|
||||||
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".user_search.pool.enabled", false);
|
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".user_search.pool.enabled", false);
|
||||||
|
if (useLegacyBindPassword) {
|
||||||
|
builder.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".bind_password", ActiveDirectorySessionFactoryTests.PASSWORD);
|
||||||
|
} else {
|
||||||
|
SecuritySettingsSource.addSecureSettings(builder, secureSettings -> {
|
||||||
|
secureSettings.setString(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".secure_bind_password",
|
||||||
|
ActiveDirectorySessionFactoryTests.PASSWORD);
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unknown realm config " + realmConfig);
|
throw new IllegalStateException("Unknown realm config " + realmConfig);
|
||||||
|
|
Loading…
Reference in New Issue