Skip to content

Commit 47d5e87

Browse files
authored
Prevent boot if bind DN is set without password (#118366)
LDAP/AD authentication realms can be configured to authenticate through LDAP via a bind user. For this it's necessary to set a bind DN (via `bind_dn`) together with a bind password (via `bind_password` or `secure_bind_password`). Setting a bind DN without a bind password will cause all LDAP/AD realm authentication to fail, leaving the node non-operational. This PR adds a bootstrap check to prevent a misconfigured node from starting. This behavior was deprecated in #85326. Closes: ES-9749
1 parent fb6d7db commit 47d5e87

File tree

3 files changed

+46
-42
lines changed

3 files changed

+46
-42
lines changed

docs/changelog/118366.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
pr: 118366
2+
summary: |-
3+
Configuring a bind DN in an LDAP or Active Directory (AD) realm without a corresponding bind password
4+
will prevent node from starting
5+
area: Authentication
6+
type: breaking
7+
issues: []
8+
breaking:
9+
title: -|
10+
Configuring a bind DN in an LDAP or Active Directory (AD) realm without
11+
a corresponding bind password will prevent node from starting
12+
area: Cluster and node setting
13+
details: -|
14+
For LDAP or AD authentication realms, setting a bind DN (via the
15+
`xpack.security.authc.realms.ldap.*.bind_dn` or `xpack.security.authc.realms.active_directory.*.bind_dn`
16+
realm settings) without a bind password is a misconfiguration that may prevent successful authentication
17+
to the node. Nodes will fail to start if a bind DN is specified without a password.
18+
impact: -|
19+
If you have a bind DN configured for an LDAP or AD authentication
20+
realm, set a bind password for {ref}/ldap-realm.html#ldap-realm-configuration[LDAP]
21+
or {ref}/active-directory-realm.html#ad-realm-configuration[Active Directory].
22+
Configuring a bind DN without a password prevents the misconfigured node from starting.

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ldap/PoolingSessionFactory.java

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import org.apache.logging.log4j.Logger;
1818
import org.elasticsearch.action.ActionListener;
19-
import org.elasticsearch.common.logging.DeprecationCategory;
2019
import org.elasticsearch.common.settings.SecureString;
2120
import org.elasticsearch.common.settings.Setting;
2221
import org.elasticsearch.common.settings.Settings;
@@ -74,7 +73,7 @@ abstract class PoolingSessionFactory extends SessionFactory implements Releasabl
7473
super(config, sslService, threadPool);
7574
this.groupResolver = groupResolver;
7675
this.bindDn = bindDn;
77-
this.bindRequest = new AtomicReference<>(buildBindRequest(config.settings(), false));
76+
this.bindRequest = new AtomicReference<>(buildBindRequest(config.settings()));
7877
this.useConnectionPool = config.getSetting(poolingEnabled);
7978
if (useConnectionPool) {
8079
this.connectionPool = createConnectionPool(config, serverSet, timeout, logger, bindRequest.get(), healthCheckDNSupplier);
@@ -93,11 +92,9 @@ abstract class PoolingSessionFactory extends SessionFactory implements Releasabl
9392
* will perform a setting consistency validation and throw {@link SettingsException} in case of violation.
9493
* Due to legacy reasons and BWC, when {@code reloadRequest} is se to {@code false}, this method will only log a warning message.
9594
*
96-
* @param reloadRequest {@code true} if this method is called during reloading of secure settings,
97-
* {@code false} if it is called during bootstrapping.
9895
* @return A new {@link SimpleBindRequest} that contains configured bind DN and password.
9996
*/
100-
private SimpleBindRequest buildBindRequest(Settings settings, boolean reloadRequest) {
97+
private SimpleBindRequest buildBindRequest(Settings settings) {
10198
final byte[] bindPassword;
10299
final Setting<SecureString> legacyPasswordSetting = config.getConcreteSetting(LEGACY_BIND_PASSWORD);
103100
final Setting<SecureString> securePasswordSetting = config.getConcreteSetting(SECURE_BIND_PASSWORD);
@@ -119,27 +116,13 @@ private SimpleBindRequest buildBindRequest(Settings settings, boolean reloadRequ
119116
return new SimpleBindRequest();
120117
} else {
121118
if (bindPassword == null) {
122-
if (reloadRequest) {
123-
throw new SettingsException(
124-
"[{}] is set but no bind password is specified. Without a corresponding bind password, "
125-
+ "all {} realm authentication will fail. Specify a bind password via [{}].",
126-
RealmSettings.getFullSettingKey(config, PoolingSessionFactorySettings.BIND_DN),
127-
config.type(),
128-
RealmSettings.getFullSettingKey(config, SECURE_BIND_PASSWORD)
129-
);
130-
} else {
131-
deprecationLogger.critical(
132-
DeprecationCategory.SECURITY,
133-
"bind_dn_set_without_password",
134-
"[{}] is set but no bind password is specified. Without a corresponding bind password, "
135-
+ "all {} realm authentication will fail. Specify a bind password via [{}] or [{}]. "
136-
+ "In the next major release, nodes with incomplete bind credentials will fail to start.",
137-
RealmSettings.getFullSettingKey(config, PoolingSessionFactorySettings.BIND_DN),
138-
config.type(),
139-
RealmSettings.getFullSettingKey(config, SECURE_BIND_PASSWORD),
140-
RealmSettings.getFullSettingKey(config, LEGACY_BIND_PASSWORD)
141-
);
142-
}
119+
throw new SettingsException(
120+
"[{}] is set but no bind password is specified. Without a corresponding bind password, "
121+
+ "all {} realm authentication will fail. Specify a bind password via [{}].",
122+
RealmSettings.getFullSettingKey(config, PoolingSessionFactorySettings.BIND_DN),
123+
config.type(),
124+
RealmSettings.getFullSettingKey(config, SECURE_BIND_PASSWORD)
125+
);
143126
}
144127
return new SimpleBindRequest(this.bindDn, bindPassword);
145128
}
@@ -148,7 +131,7 @@ private SimpleBindRequest buildBindRequest(Settings settings, boolean reloadRequ
148131
@Override
149132
public void reload(Settings settings) {
150133
final SimpleBindRequest oldRequest = bindRequest.get();
151-
final SimpleBindRequest newRequest = buildBindRequest(settings, true);
134+
final SimpleBindRequest newRequest = buildBindRequest(settings);
152135
if (bindRequestEquals(newRequest, oldRequest) == false) {
153136
if (bindRequest.compareAndSet(oldRequest, newRequest)) {
154137
if (connectionPool != null) {

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactoryTests.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.common.settings.SecureString;
2222
import org.elasticsearch.common.settings.Setting;
2323
import org.elasticsearch.common.settings.Settings;
24+
import org.elasticsearch.common.settings.SettingsException;
2425
import org.elasticsearch.common.util.concurrent.ThreadContext;
2526
import org.elasticsearch.core.TimeValue;
2627
import org.elasticsearch.env.TestEnvironment;
@@ -45,7 +46,6 @@
4546
import java.util.Locale;
4647

4748
import static org.elasticsearch.xpack.core.security.authc.RealmSettings.getFullSettingKey;
48-
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.BIND_DN;
4949
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.LEGACY_BIND_PASSWORD;
5050
import static org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings.SECURE_BIND_PASSWORD;
5151
import static org.hamcrest.Matchers.containsString;
@@ -199,7 +199,7 @@ public void testUserSearchBaseScopeFailsWithWrongBaseDN() throws Exception {
199199
assertDeprecationWarnings(config.identifier(), useAttribute, useLegacyBindPassword);
200200
}
201201

202-
public void testConstructorLogsErrorIfBindDnSetWithoutPassword() throws Exception {
202+
public void testConstructorThrowsIfBindDnSetWithoutPassword() throws Exception {
203203
String groupSearchBase = "o=sevenSeas";
204204
String userSearchBase = "cn=William Bush,ou=people,o=sevenSeas";
205205

@@ -216,19 +216,18 @@ public void testConstructorLogsErrorIfBindDnSetWithoutPassword() throws Exceptio
216216
new ThreadContext(globalSettings)
217217
);
218218

219-
try (LdapUserSearchSessionFactory ignored = getLdapUserSearchSessionFactory(config, sslService, threadPool)) {
220-
assertCriticalWarnings(
221-
String.format(
222-
Locale.ROOT,
223-
"[%s] is set but no bind password is specified. Without a corresponding bind password, "
224-
+ "all ldap realm authentication will fail. Specify a bind password via [%s] or [%s]. "
225-
+ "In the next major release, nodes with incomplete bind credentials will fail to start.",
226-
RealmSettings.getFullSettingKey(config, BIND_DN),
227-
RealmSettings.getFullSettingKey(config, SECURE_BIND_PASSWORD),
228-
RealmSettings.getFullSettingKey(config, LEGACY_BIND_PASSWORD)
229-
)
230-
);
231-
}
219+
Exception ex = expectThrows(SettingsException.class, () -> getLdapUserSearchSessionFactory(config, sslService, threadPool));
220+
assertEquals(
221+
String.format(
222+
Locale.ROOT,
223+
"[%s] is set but no bind password is specified. Without a corresponding bind password, "
224+
+ "all %s realm authentication will fail. Specify a bind password via [%s].",
225+
RealmSettings.getFullSettingKey(config, PoolingSessionFactorySettings.BIND_DN),
226+
config.type(),
227+
RealmSettings.getFullSettingKey(config, SECURE_BIND_PASSWORD)
228+
),
229+
ex.getMessage()
230+
);
232231
}
233232

234233
public void testConstructorThrowsIfBothLegacyAndSecureBindPasswordSet() throws Exception {

0 commit comments

Comments
 (0)