Explicitly fail if a realm only exists in keystore (#45091)

There are no realms that can be configured exclusively with secure
settings. Every realm that supports secure settings also requires one
or more non-secure settings.
However, sometimes a node will be configured with entries in the
keystore for which there is nothing in elasticsearch.yml - this may be
because the realm we removed from the yml, but not deleted from the
keystore, or it could be because there was a typo in the realm name
which has accidentially orphaned the keystore entry.

In these cases the realm building would fail, but the error would not
always be clear or point to the root cause (orphaned keystore
entries). RealmSettings would act as though the realm existed, but
then fail because an incorrect combination of settings was provided.

This change causes realm building to fail early, with an explicit
message about incorrect keystore entries.

Backport of: #44471
This commit is contained in:
Tim Vernum 2019-08-02 12:28:59 +10:00 committed by GitHub
parent ae5c01e2d2
commit 590777150f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 7 deletions

View File

@ -10,6 +10,7 @@ import org.elasticsearch.common.settings.SecureSetting;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsException;
import java.util.Arrays;
import java.util.List;
@ -91,12 +92,31 @@ public class RealmSettings {
return settingsByName.names().stream().map(name -> {
final RealmConfig.RealmIdentifier id = new RealmConfig.RealmIdentifier(type, name);
final Settings realmSettings = settingsByName.getAsSettings(name);
verifyRealmSettings(id, realmSettings);
return new Tuple<>(id, realmSettings);
});
})
.collect(Collectors.toMap(Tuple::v1, Tuple::v2));
}
/**
* Performs any necessary verifications on a realms settings that are not automatically applied by Settings validation infrastructure.
*/
private static void verifyRealmSettings(RealmConfig.RealmIdentifier identifier, Settings realmSettings) {
final Settings nonSecureSettings = Settings.builder().put(realmSettings, false).build();
if (nonSecureSettings.isEmpty()) {
final String prefix = realmSettingPrefix(identifier);
throw new SettingsException(
"found settings for the realm [{}] (with type [{}]) in the secure settings (elasticsearch.keystore)," +
" but this realm does not have any settings in elasticsearch.yml." +
" Please remove these settings from the keystore, or update their names to match one of the realms that are" +
" defined in elasticsearch.yml - [{}]",
identifier.getName(), identifier.getType(),
realmSettings.keySet().stream().map(k -> prefix + k).collect(Collectors.joining(","))
);
}
}
public static String getFullSettingKey(String realmName, Setting.AffixSetting<?> setting) {
return setting.getConcreteSettingForNamespace(realmName).getKey();
}

View File

@ -10,12 +10,16 @@ import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.SecureSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsException;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.authc.InternalRealmsSettings;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
import org.hamcrest.Matchers;
import java.util.Collections;
import java.util.HashSet;
@ -23,7 +27,6 @@ import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.notNullValue;
public class RealmSettingsTests extends ESTestCase {
private static final List<String> CACHE_HASHING_ALGOS = Hasher.getAvailableAlgoCacheHash();
@ -87,6 +90,21 @@ public class RealmSettingsTests extends ESTestCase {
assertSuccess(settings);
}
public void testSettingsWithKeystoreOnlyRealmDoesNotValidate() throws Exception {
final String securePasswordKey = RealmSettings.getFullSettingKey(
new RealmConfig.RealmIdentifier("ldap", "ldap_1"), PoolingSessionFactorySettings.SECURE_BIND_PASSWORD);
final Settings.Builder builder = Settings.builder()
.put(ldapRealm("ldap-1", randomBoolean(), randomBoolean()).build());
SecuritySettingsSource.addSecureSettings(builder, secureSettings -> {
secureSettings.setString(securePasswordKey, "secret-password");
});
final Settings settings = builder.build();
final SettingsException exception = expectThrows(SettingsException.class, () -> RealmSettings.getRealmSettings(settings));
assertThat(exception.getMessage(), containsString("elasticsearch.keystore"));
assertThat(exception.getMessage(), containsString("elasticsearch.yml"));
assertThat(exception.getMessage(), containsString(securePasswordKey));
}
private Settings.Builder nativeRealm(String name) {
return realm("native", name, nativeSettings());
}
@ -277,12 +295,7 @@ public class RealmSettingsTests extends ESTestCase {
} catch (RuntimeException e) {
fail("Settings do not validate: " + e);
}
}
private void assertErrorWithCause(String realmType, String realmName, String message, Settings settings) {
final IllegalArgumentException exception = assertError(realmType, realmName, settings);
assertThat(exception.getCause(), notNullValue());
assertThat(exception.getCause().getMessage(), containsString(message));
assertThat(RealmSettings.getRealmSettings(settings), Matchers.not(Matchers.anEmptyMap()));
}
private void assertErrorWithMessage(String realmType, String realmName, String message, Settings settings) {