Validate hashing algorithm in users tool (#55628) (#55734)

This change adds validation when running the users tool so that
if Elasticsearch is expected to run in a JVM that is configured to
be in FIPS 140 mode and the password hashing algorithm is not
compliant, we would throw an error.
Users tool uses the configuration from the node and this validation
would also happen upon node startup but users might be added in the
file realm before the node is started and we would have the
opportunity to notify the user of this misconfiguration.
The changes in #55544 make this much less probable to happen in 8
since the default algorithm will be compliant but this change can
act as a fallback in anycase and makes for a better user experience.
This commit is contained in:
Ioannis Kakavas 2020-04-27 12:23:41 +03:00 committed by GitHub
parent 38b55f06ba
commit d56f25acb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 8 deletions

View File

@ -439,6 +439,10 @@ public class UsersTool extends LoggingAwareMultiCommand {
private static char[] getPasswordHash(Terminal terminal, Environment env, String cliPasswordValue) throws UserException {
final Hasher hasher = Hasher.resolve(XPackSettings.PASSWORD_HASHING_ALGORITHM.get(env.settings()));
if (XPackSettings.FIPS_MODE_ENABLED.get(env.settings()) && hasher.name().toLowerCase(Locale.ROOT).startsWith("pbkdf2") == false) {
throw new UserException(ExitCodes.CONFIG, "Only PBKDF2 is allowed for password hashing in a FIPS 140 JVM. Please set the " +
"appropriate value for [ " + XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey() + " ] setting.");
}
final char[] passwordHash;
try (SecureString password = parsePassword(terminal, cliPasswordValue)) {
passwordHash = hasher.hash(password);

View File

@ -70,7 +70,8 @@ public class UsersToolTests extends CommandTestCase {
IOUtils.rm(homeDir);
confDir = homeDir.resolve("config");
Files.createDirectories(confDir);
hasher = Hasher.resolve(randomFrom("bcrypt", "pbkdf2"));
hasher = inFipsJvm() ? randomFrom(Hasher.PBKDF2, Hasher.PBKDF2_1000)
: randomFrom(Hasher.PBKDF2_1000, Hasher.PBKDF2, Hasher.BCRYPT, Hasher.BCRYPT9);
String defaultPassword = SecuritySettingsSourceField.TEST_PASSWORD;
Files.write(confDir.resolve("users"), Arrays.asList(
"existing_user:" + new String(hasher.hash(SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING)),
@ -90,10 +91,11 @@ public class UsersToolTests extends CommandTestCase {
" cluster: all"
), StandardCharsets.UTF_8);
settings =
Settings.builder()
.put("path.home", homeDir)
.put("xpack.security.authc.realms.file.file.order", 0)
.build();
Settings.builder()
.put("path.home", homeDir)
.put("xpack.security.authc.realms.file.file.order", 0)
.put("xpack.security.authc.password_hashing.algorithm", hasher.name())
.build();
pathHomeParameter = "-Epath.home=" + homeDir;
fileOrderParameter = "-Expack.security.authc.realms.file.file.order=0";
}
@ -174,9 +176,7 @@ public class UsersToolTests extends CommandTestCase {
}
String gotHash = usernameHash[1];
SecureString expectedHash = new SecureString(password.toCharArray());
// CommandTestCase#execute runs passwd with default settings, so bcrypt with cost of 10
Hasher bcryptHasher = Hasher.resolve("bcrypt");
assertTrue("Could not validate password for user", bcryptHasher.verify(expectedHash, gotHash.toCharArray()));
assertTrue("Could not validate password for user", hasher.verify(expectedHash, gotHash.toCharArray()));
return;
}
fail("Could not find username " + username + " in users file:\n" + lines.toString());
@ -363,6 +363,23 @@ public class UsersToolTests extends CommandTestCase {
assertTrue(lines.toString(), lines.isEmpty());
}
public void testAddUserWithInvalidHashingAlgorithmInFips() throws Exception {
settings =
Settings.builder()
.put(settings)
.put("xpack.security.authc.password_hashing.algorithm", "bcrypt")
.put("xpack.security.fips_mode.enabled", true)
.build();
UserException e = expectThrows(UserException.class, () -> {
execute("useradd", pathHomeParameter, fileOrderParameter, randomAlphaOfLength(12), "-p",
SecuritySettingsSourceField.TEST_PASSWORD);
});
assertEquals(ExitCodes.CONFIG, e.exitCode);
assertEquals("Only PBKDF2 is allowed for password hashing in a FIPS 140 JVM. " +
"Please set the appropriate value for [ xpack.security.authc.password_hashing.algorithm ] setting.", e.getMessage());
}
public void testUserdelUnknownUser() throws Exception {
UserException e = expectThrows(UserException.class, () -> {
execute("userdel", pathHomeParameter, fileOrderParameter, "unknown");
@ -398,6 +415,21 @@ public class UsersToolTests extends CommandTestCase {
assertRole("test_admin", "existing_user"); // roles unchanged
}
public void testPasswdWithInvalidHashingAlgorithmInFips() throws Exception {
settings =
Settings.builder()
.put(settings)
.put("xpack.security.authc.password_hashing.algorithm", "bcrypt")
.put("xpack.security.fips_mode.enabled", true)
.build();
UserException e = expectThrows(UserException.class, () -> {
execute("passwd", pathHomeParameter, fileOrderParameter, "existing_user", "-p", "newpassword");
});
assertEquals(ExitCodes.CONFIG, e.exitCode);
assertEquals("Only PBKDF2 is allowed for password hashing in a FIPS 140 JVM. " +
"Please set the appropriate value for [ xpack.security.authc.password_hashing.algorithm ] setting.", e.getMessage());
}
public void testRolesUnknownUser() throws Exception {
UserException e = expectThrows(UserException.class, () -> {
execute("roles", pathHomeParameter, fileOrderParameter, "unknown");