Security: remove password hash bootstrap check (#32440)
This change removes the PasswordHashingBootstrapCheck and replaces it with validation on the setting itself. This ensures we always get a valid value from the setting when it is used.
This commit is contained in:
parent
7d8a64d703
commit
ac5ef8c389
|
@ -14,6 +14,7 @@ import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings;
|
||||||
import org.elasticsearch.xpack.core.ssl.VerificationMode;
|
import org.elasticsearch.xpack.core.ssl.VerificationMode;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKeyFactory;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -127,8 +128,16 @@ public class XPackSettings {
|
||||||
public static final Setting<String> PASSWORD_HASHING_ALGORITHM = new Setting<>(
|
public static final Setting<String> PASSWORD_HASHING_ALGORITHM = new Setting<>(
|
||||||
"xpack.security.authc.password_hashing.algorithm", "bcrypt", Function.identity(), (v, s) -> {
|
"xpack.security.authc.password_hashing.algorithm", "bcrypt", Function.identity(), (v, s) -> {
|
||||||
if (Hasher.getAvailableAlgoStoredHash().contains(v.toLowerCase(Locale.ROOT)) == false) {
|
if (Hasher.getAvailableAlgoStoredHash().contains(v.toLowerCase(Locale.ROOT)) == false) {
|
||||||
throw new IllegalArgumentException("Invalid algorithm: " + v + ". Only pbkdf2 or bcrypt family algorithms can be used for " +
|
throw new IllegalArgumentException("Invalid algorithm: " + v + ". Valid values for password hashing are " +
|
||||||
"password hashing.");
|
Hasher.getAvailableAlgoStoredHash().toString());
|
||||||
|
} else if (v.regionMatches(true, 0, "pbkdf2", 0, "pbkdf2".length())) {
|
||||||
|
try {
|
||||||
|
SecretKeyFactory.getInstance("PBKDF2withHMACSHA512");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Support for PBKDF2WithHMACSHA512 must be available in order to use any of the " +
|
||||||
|
"PBKDF2 algorithms for the [xpack.security.authc.password_hashing.algorithm] setting.", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, Setting.Property.NodeScope);
|
}, Setting.Property.NodeScope);
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,14 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.core;
|
package org.elasticsearch.xpack.core;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKeyFactory;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.hasItem;
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
|
||||||
|
@ -25,4 +30,30 @@ public class XPackSettingsTests extends ESTestCase {
|
||||||
assertThat(XPackSettings.DEFAULT_CIPHERS, not(hasItem("TLS_RSA_WITH_AES_256_CBC_SHA")));
|
assertThat(XPackSettings.DEFAULT_CIPHERS, not(hasItem("TLS_RSA_WITH_AES_256_CBC_SHA")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPasswordHashingAlgorithmSettingValidation() {
|
||||||
|
final boolean isPBKDF2Available = isSecretkeyFactoryAlgoAvailable("PBKDF2WithHMACSHA512");
|
||||||
|
final String pbkdf2Algo = randomFrom("PBKDF2_10000", "PBKDF2");
|
||||||
|
final Settings settings = Settings.builder().put(XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey(), pbkdf2Algo).build();
|
||||||
|
if (isPBKDF2Available) {
|
||||||
|
assertEquals(pbkdf2Algo, XPackSettings.PASSWORD_HASHING_ALGORITHM.get(settings));
|
||||||
|
} else {
|
||||||
|
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> XPackSettings.PASSWORD_HASHING_ALGORITHM.get(settings));
|
||||||
|
assertThat(e.getMessage(), containsString("Support for PBKDF2WithHMACSHA512 must be available"));
|
||||||
|
}
|
||||||
|
|
||||||
|
final String bcryptAlgo = randomFrom("BCRYPT", "BCRYPT11");
|
||||||
|
assertEquals(bcryptAlgo, XPackSettings.PASSWORD_HASHING_ALGORITHM.get(
|
||||||
|
Settings.builder().put(XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey(), bcryptAlgo).build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSecretkeyFactoryAlgoAvailable(String algorithmId) {
|
||||||
|
try {
|
||||||
|
SecretKeyFactory.getInstance(algorithmId);
|
||||||
|
return true;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.xpack.security;
|
|
||||||
|
|
||||||
import org.elasticsearch.bootstrap.BootstrapCheck;
|
|
||||||
import org.elasticsearch.bootstrap.BootstrapContext;
|
|
||||||
import org.elasticsearch.xpack.core.XPackSettings;
|
|
||||||
|
|
||||||
import javax.crypto.SecretKeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrap check to ensure that one of the allowed password hashing algorithms is
|
|
||||||
* selected and that it is available.
|
|
||||||
*/
|
|
||||||
public class PasswordHashingAlgorithmBootstrapCheck implements BootstrapCheck {
|
|
||||||
@Override
|
|
||||||
public BootstrapCheckResult check(BootstrapContext context) {
|
|
||||||
final String selectedAlgorithm = XPackSettings.PASSWORD_HASHING_ALGORITHM.get(context.settings);
|
|
||||||
if (selectedAlgorithm.toLowerCase(Locale.ROOT).startsWith("pbkdf2")) {
|
|
||||||
try {
|
|
||||||
SecretKeyFactory.getInstance("PBKDF2withHMACSHA512");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
final String errorMessage = String.format(Locale.ROOT,
|
|
||||||
"Support for PBKDF2WithHMACSHA512 must be available in order to use any of the " +
|
|
||||||
"PBKDF2 algorithms for the [%s] setting.", XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey());
|
|
||||||
return BootstrapCheckResult.failure(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return BootstrapCheckResult.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean alwaysEnforce() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -300,7 +300,6 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
|
||||||
new TokenSSLBootstrapCheck(),
|
new TokenSSLBootstrapCheck(),
|
||||||
new PkiRealmBootstrapCheck(getSslService()),
|
new PkiRealmBootstrapCheck(getSslService()),
|
||||||
new TLSLicenseBootstrapCheck(),
|
new TLSLicenseBootstrapCheck(),
|
||||||
new PasswordHashingAlgorithmBootstrapCheck(),
|
|
||||||
new FIPS140SecureSettingsBootstrapCheck(settings, env),
|
new FIPS140SecureSettingsBootstrapCheck(settings, env),
|
||||||
new FIPS140JKSKeystoreBootstrapCheck(settings),
|
new FIPS140JKSKeystoreBootstrapCheck(settings),
|
||||||
new FIPS140PasswordHashingAlgorithmBootstrapCheck(settings)));
|
new FIPS140PasswordHashingAlgorithmBootstrapCheck(settings)));
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.xpack.security;
|
|
||||||
|
|
||||||
import org.elasticsearch.bootstrap.BootstrapContext;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
|
||||||
import org.elasticsearch.xpack.core.XPackSettings;
|
|
||||||
|
|
||||||
import javax.crypto.SecretKeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public class PasswordHashingAlgorithmBootstrapCheckTests extends ESTestCase {
|
|
||||||
|
|
||||||
public void testPasswordHashingAlgorithmBootstrapCheck() {
|
|
||||||
Settings settings = Settings.EMPTY;
|
|
||||||
assertFalse(new PasswordHashingAlgorithmBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
|
|
||||||
// The following two will always pass because for now we only test in environments where PBKDF2WithHMACSHA512 is supported
|
|
||||||
assertTrue(isSecretkeyFactoryAlgoAvailable("PBKDF2WithHMACSHA512"));
|
|
||||||
settings = Settings.builder().put(XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey(), "PBKDF2_10000").build();
|
|
||||||
assertFalse(new PasswordHashingAlgorithmBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
|
|
||||||
|
|
||||||
settings = Settings.builder().put(XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey(), "PBKDF2").build();
|
|
||||||
assertFalse(new PasswordHashingAlgorithmBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
|
|
||||||
|
|
||||||
settings = Settings.builder().put(XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey(), "BCRYPT").build();
|
|
||||||
assertFalse(new PasswordHashingAlgorithmBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
|
|
||||||
|
|
||||||
settings = Settings.builder().put(XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey(), "BCRYPT11").build();
|
|
||||||
assertFalse(new PasswordHashingAlgorithmBootstrapCheck().check(new BootstrapContext(settings, null)).isFailure());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSecretkeyFactoryAlgoAvailable(String algorithmId) {
|
|
||||||
try {
|
|
||||||
SecretKeyFactory.getInstance(algorithmId);
|
|
||||||
return true;
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -83,7 +83,7 @@ public class ReservedRealmTests extends ESTestCase {
|
||||||
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> new ReservedRealm(mock(Environment.class),
|
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> new ReservedRealm(mock(Environment.class),
|
||||||
invalidSettings, usersStore, new AnonymousUser(Settings.EMPTY), securityIndex, threadPool));
|
invalidSettings, usersStore, new AnonymousUser(Settings.EMPTY), securityIndex, threadPool));
|
||||||
assertThat(exception.getMessage(), containsString(invalidAlgoId));
|
assertThat(exception.getMessage(), containsString(invalidAlgoId));
|
||||||
assertThat(exception.getMessage(), containsString("Only pbkdf2 or bcrypt family algorithms can be used for password hashing"));
|
assertThat(exception.getMessage(), containsString("Invalid algorithm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReservedUserEmptyPasswordAuthenticationFails() throws Throwable {
|
public void testReservedUserEmptyPasswordAuthenticationFails() throws Throwable {
|
||||||
|
|
Loading…
Reference in New Issue