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:
parent
ae5c01e2d2
commit
590777150f
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.settings.SecureSetting;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.settings.SettingsException;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -91,12 +92,31 @@ public class RealmSettings {
|
||||||
return settingsByName.names().stream().map(name -> {
|
return settingsByName.names().stream().map(name -> {
|
||||||
final RealmConfig.RealmIdentifier id = new RealmConfig.RealmIdentifier(type, name);
|
final RealmConfig.RealmIdentifier id = new RealmConfig.RealmIdentifier(type, name);
|
||||||
final Settings realmSettings = settingsByName.getAsSettings(name);
|
final Settings realmSettings = settingsByName.getAsSettings(name);
|
||||||
|
verifyRealmSettings(id, realmSettings);
|
||||||
return new Tuple<>(id, realmSettings);
|
return new Tuple<>(id, realmSettings);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.collect(Collectors.toMap(Tuple::v1, Tuple::v2));
|
.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) {
|
public static String getFullSettingKey(String realmName, Setting.AffixSetting<?> setting) {
|
||||||
return setting.getConcreteSettingForNamespace(realmName).getKey();
|
return setting.getConcreteSettingForNamespace(realmName).getKey();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,16 @@ import org.elasticsearch.common.settings.MockSecureSettings;
|
||||||
import org.elasticsearch.common.settings.SecureSettings;
|
import org.elasticsearch.common.settings.SecureSettings;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.settings.SettingsException;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.SecuritySettingsSource;
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
import org.elasticsearch.xpack.core.XPackSettings;
|
import org.elasticsearch.xpack.core.XPackSettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.InternalRealmsSettings;
|
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.RealmSettings;
|
||||||
|
import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings;
|
||||||
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
|
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -23,7 +27,6 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
|
||||||
|
|
||||||
public class RealmSettingsTests extends ESTestCase {
|
public class RealmSettingsTests extends ESTestCase {
|
||||||
private static final List<String> CACHE_HASHING_ALGOS = Hasher.getAvailableAlgoCacheHash();
|
private static final List<String> CACHE_HASHING_ALGOS = Hasher.getAvailableAlgoCacheHash();
|
||||||
|
@ -87,6 +90,21 @@ public class RealmSettingsTests extends ESTestCase {
|
||||||
assertSuccess(settings);
|
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) {
|
private Settings.Builder nativeRealm(String name) {
|
||||||
return realm("native", name, nativeSettings());
|
return realm("native", name, nativeSettings());
|
||||||
}
|
}
|
||||||
|
@ -277,12 +295,7 @@ public class RealmSettingsTests extends ESTestCase {
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
fail("Settings do not validate: " + e);
|
fail("Settings do not validate: " + e);
|
||||||
}
|
}
|
||||||
}
|
assertThat(RealmSettings.getRealmSettings(settings), Matchers.not(Matchers.anEmptyMap()));
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertErrorWithMessage(String realmType, String realmName, String message, Settings settings) {
|
private void assertErrorWithMessage(String realmType, String realmName, String message, Settings settings) {
|
||||||
|
|
Loading…
Reference in New Issue