mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-29 01:44:36 -04:00
More accurate error message for LDAP user modes (#89492)
When a ldap relam is configured with settings for both user search and user dn modes, ES node will fail to start. This PR improves the error mesesage so it lists the exact conflicting user-search settings instead of always printing out base_dn.
This commit is contained in:
parent
c9b2cc4c1a
commit
8f52a5512f
4 changed files with 51 additions and 13 deletions
5
docs/changelog/89492.yaml
Normal file
5
docs/changelog/89492.yaml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pr: 89492
|
||||||
|
summary: More accurate error message for LDAP user modes
|
||||||
|
area: Authentication
|
||||||
|
type: enhancement
|
||||||
|
issues: []
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.ElasticsearchTimeoutException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.ContextPreservingActionListener;
|
import org.elasticsearch.action.support.ContextPreservingActionListener;
|
||||||
import org.elasticsearch.common.collect.MapBuilder;
|
import org.elasticsearch.common.collect.MapBuilder;
|
||||||
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.core.IOUtils;
|
import org.elasticsearch.core.IOUtils;
|
||||||
|
@ -41,14 +42,17 @@ import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupp
|
||||||
import org.elasticsearch.xpack.security.authc.support.mapper.CompositeRoleMapper;
|
import org.elasticsearch.xpack.security.authc.support.mapper.CompositeRoleMapper;
|
||||||
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
|
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.elasticsearch.core.Strings.format;
|
import static org.elasticsearch.core.Strings.format;
|
||||||
|
import static org.elasticsearch.xpack.security.authc.ldap.LdapUserSearchSessionFactory.configuredUserSearchSettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticates username/password tokens against ldap, locates groups and maps them to roles.
|
* Authenticates username/password tokens against ldap, locates groups and maps them to roles.
|
||||||
|
@ -101,9 +105,9 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
|
||||||
+ ", "
|
+ ", "
|
||||||
+ LdapRealmSettings.LDAP_TYPE
|
+ LdapRealmSettings.LDAP_TYPE
|
||||||
+ "]";
|
+ "]";
|
||||||
final boolean hasSearchSettings = LdapUserSearchSessionFactory.hasUserSearchSettings(config);
|
final List<? extends Setting.AffixSetting<?>> configuredSearchSettings = configuredUserSearchSettings(config);
|
||||||
final boolean hasTemplates = config.hasSetting(LdapSessionFactorySettings.USER_DN_TEMPLATES_SETTING);
|
final boolean hasTemplates = config.hasSetting(LdapSessionFactorySettings.USER_DN_TEMPLATES_SETTING);
|
||||||
if (hasSearchSettings == false) {
|
if (configuredSearchSettings.isEmpty()) {
|
||||||
if (hasTemplates == false) {
|
if (hasTemplates == false) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"settings were not found for either user search ["
|
"settings were not found for either user search ["
|
||||||
|
@ -119,7 +123,9 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
|
||||||
} else if (hasTemplates) {
|
} else if (hasTemplates) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"settings were found for both user search ["
|
"settings were found for both user search ["
|
||||||
+ RealmSettings.getFullSettingKey(config, LdapUserSearchSessionFactorySettings.SEARCH_BASE_DN)
|
+ configuredSearchSettings.stream()
|
||||||
|
.map(s -> RealmSettings.getFullSettingKey(config, s))
|
||||||
|
.collect(Collectors.joining(","))
|
||||||
+ "] and user template ["
|
+ "] and user template ["
|
||||||
+ RealmSettings.getFullSettingKey(config, LdapSessionFactorySettings.USER_DN_TEMPLATES_SETTING)
|
+ RealmSettings.getFullSettingKey(config, LdapSessionFactorySettings.USER_DN_TEMPLATES_SETTING)
|
||||||
+ "] modes of operation. "
|
+ "] modes of operation. "
|
||||||
|
@ -206,7 +212,7 @@ public final class LdapRealm extends CachingUsernamePasswordRealm {
|
||||||
usage.put("size", getCacheSize());
|
usage.put("size", getCacheSize());
|
||||||
usage.put("load_balance_type", LdapLoadBalancing.resolve(config).toString());
|
usage.put("load_balance_type", LdapLoadBalancing.resolve(config).toString());
|
||||||
usage.put("ssl", sessionFactory.isSslUsed());
|
usage.put("ssl", sessionFactory.isSslUsed());
|
||||||
usage.put("user_search", LdapUserSearchSessionFactory.hasUserSearchSettings(config));
|
usage.put("user_search", false == configuredUserSearchSettings(config).isEmpty());
|
||||||
listener.onResponse(usage);
|
listener.onResponse(usage);
|
||||||
}, listener::onFailure));
|
}, listener::onFailure));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import com.unboundid.ldap.sdk.SimpleBindRequest;
|
||||||
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.Setting;
|
||||||
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
||||||
import org.elasticsearch.core.CharArrays;
|
import org.elasticsearch.core.CharArrays;
|
||||||
import org.elasticsearch.core.IOUtils;
|
import org.elasticsearch.core.IOUtils;
|
||||||
|
@ -31,6 +32,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 java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.BIND_DN;
|
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.BIND_DN;
|
||||||
|
@ -41,7 +43,6 @@ import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.sear
|
||||||
class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
||||||
|
|
||||||
static final String SEARCH_PREFIX = "user_search.";
|
static final String SEARCH_PREFIX = "user_search.";
|
||||||
|
|
||||||
private final String userSearchBaseDn;
|
private final String userSearchBaseDn;
|
||||||
private final LdapSearchScope scope;
|
private final LdapSearchScope scope;
|
||||||
private final String searchFilter;
|
private final String searchFilter;
|
||||||
|
@ -66,14 +67,14 @@ class LdapUserSearchSessionFactory extends PoolingSessionFactory {
|
||||||
logger.info("Realm [{}] is in user-search mode - base_dn=[{}], search filter=[{}]", config.name(), userSearchBaseDn, searchFilter);
|
logger.info("Realm [{}] is in user-search mode - base_dn=[{}], search filter=[{}]", config.name(), userSearchBaseDn, searchFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean hasUserSearchSettings(RealmConfig config) {
|
static List<? extends Setting.AffixSetting<?>> configuredUserSearchSettings(RealmConfig config) {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
LdapUserSearchSessionFactorySettings.SEARCH_BASE_DN,
|
LdapUserSearchSessionFactorySettings.SEARCH_BASE_DN,
|
||||||
LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE,
|
LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE,
|
||||||
LdapUserSearchSessionFactorySettings.SEARCH_SCOPE,
|
LdapUserSearchSessionFactorySettings.SEARCH_SCOPE,
|
||||||
LdapUserSearchSessionFactorySettings.SEARCH_FILTER,
|
LdapUserSearchSessionFactorySettings.SEARCH_FILTER,
|
||||||
LdapUserSearchSessionFactorySettings.POOL_ENABLED
|
LdapUserSearchSessionFactorySettings.POOL_ENABLED
|
||||||
).anyMatch(config::hasSetting);
|
).filter(config::hasSetting).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -59,9 +59,11 @@ import org.junit.Before;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.elasticsearch.test.ActionListenerUtils.anyActionListener;
|
import static org.elasticsearch.test.ActionListenerUtils.anyActionListener;
|
||||||
import static org.elasticsearch.xpack.core.security.authc.RealmSettings.getFullSettingKey;
|
import static org.elasticsearch.xpack.core.security.authc.RealmSettings.getFullSettingKey;
|
||||||
|
@ -368,23 +370,47 @@ public class LdapRealmTests extends LdapTestCase {
|
||||||
LdapRealmSettings.LDAP_TYPE,
|
LdapRealmSettings.LDAP_TYPE,
|
||||||
"test-ldap-realm-user-search"
|
"test-ldap-realm-user-search"
|
||||||
);
|
);
|
||||||
Settings settings = Settings.builder()
|
|
||||||
|
final List<? extends Setting.AffixSetting<?>> userSearchSettings = List.of(
|
||||||
|
LdapUserSearchSessionFactorySettings.SEARCH_BASE_DN,
|
||||||
|
LdapUserSearchSessionFactorySettings.SEARCH_ATTRIBUTE,
|
||||||
|
LdapUserSearchSessionFactorySettings.SEARCH_SCOPE,
|
||||||
|
LdapUserSearchSessionFactorySettings.SEARCH_FILTER,
|
||||||
|
LdapUserSearchSessionFactorySettings.POOL_ENABLED
|
||||||
|
);
|
||||||
|
final List<? extends Setting.AffixSetting<?>> configuredUserSearchSettings = randomNonEmptySubsetOf(userSearchSettings).stream()
|
||||||
|
.sorted(Comparator.comparing(userSearchSettings::indexOf))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final Settings.Builder settingsBuilder = Settings.builder()
|
||||||
.put(defaultGlobalSettings)
|
.put(defaultGlobalSettings)
|
||||||
.putList(getFullSettingKey(identifier, URLS_SETTING), ldapUrls())
|
.putList(getFullSettingKey(identifier, URLS_SETTING), ldapUrls())
|
||||||
.putList(getFullSettingKey(identifier.getName(), LdapSessionFactorySettings.USER_DN_TEMPLATES_SETTING), "cn=foo")
|
.putList(getFullSettingKey(identifier.getName(), LdapSessionFactorySettings.USER_DN_TEMPLATES_SETTING), "cn=foo")
|
||||||
.put(getFullSettingKey(identifier.getName(), LdapUserSearchSessionFactorySettings.SEARCH_BASE_DN), "cn=bar")
|
|
||||||
.put(getFullSettingKey(identifier, SearchGroupsResolverSettings.BASE_DN), "")
|
.put(getFullSettingKey(identifier, SearchGroupsResolverSettings.BASE_DN), "")
|
||||||
.put(getFullSettingKey(identifier, SearchGroupsResolverSettings.SCOPE), LdapSearchScope.SUB_TREE)
|
.put(getFullSettingKey(identifier, SearchGroupsResolverSettings.SCOPE), LdapSearchScope.SUB_TREE)
|
||||||
.put(getFullSettingKey(identifier, VERIFICATION_MODE_SETTING_REALM), SslVerificationMode.CERTIFICATE)
|
.put(getFullSettingKey(identifier, VERIFICATION_MODE_SETTING_REALM), SslVerificationMode.CERTIFICATE)
|
||||||
.put(getFullSettingKey(identifier, RealmSettings.ORDER_SETTING), 0)
|
.put(getFullSettingKey(identifier, RealmSettings.ORDER_SETTING), 0);
|
||||||
.build();
|
|
||||||
RealmConfig config = getRealmConfig(identifier, settings);
|
configuredUserSearchSettings.forEach(s -> {
|
||||||
|
final String key = getFullSettingKey(identifier.getName(), s);
|
||||||
|
settingsBuilder.put(key, key.endsWith(".enabled") ? String.valueOf(randomBoolean()) : randomAlphaOfLengthBetween(8, 18));
|
||||||
|
});
|
||||||
|
RealmConfig config = getRealmConfig(identifier, settingsBuilder.build());
|
||||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> LdapRealm.sessionFactory(config, null, threadPool));
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> LdapRealm.sessionFactory(config, null, threadPool));
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
containsString(
|
containsString(
|
||||||
"settings were found for both"
|
"settings were found for both"
|
||||||
+ " user search [xpack.security.authc.realms.ldap.test-ldap-realm-user-search.user_search.base_dn] and"
|
+ " user search ["
|
||||||
|
+ configuredUserSearchSettings.stream()
|
||||||
|
.map(Setting::getKey)
|
||||||
|
.map(
|
||||||
|
key -> "xpack.security.authc.realms.ldap.test-ldap-realm-user-search"
|
||||||
|
+ key.substring(key.lastIndexOf(".user_search."))
|
||||||
|
)
|
||||||
|
.collect(Collectors.joining(","))
|
||||||
|
+ "] and"
|
||||||
+ " user template [xpack.security.authc.realms.ldap.test-ldap-realm-user-search.user_dn_templates]"
|
+ " user template [xpack.security.authc.realms.ldap.test-ldap-realm-user-search.user_dn_templates]"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue