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:
Yang Wang 2022-08-23 16:25:57 +10:00 committed by GitHub
parent c9b2cc4c1a
commit 8f52a5512f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 13 deletions

View file

@ -0,0 +1,5 @@
pr: 89492
summary: More accurate error message for LDAP user modes
area: Authentication
type: enhancement
issues: []

View file

@ -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));
} }

View file

@ -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();
} }
/** /**

View file

@ -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]"
) )
); );