diff --git a/docs/reference/migration/index.asciidoc b/docs/reference/migration/index.asciidoc index 0e85abf93b10f..422e22ae7eeb8 100644 --- a/docs/reference/migration/index.asciidoc +++ b/docs/reference/migration/index.asciidoc @@ -28,6 +28,7 @@ For more information about {minor-version}, see the <> and <>. For information about how to upgrade your cluster, see <>. +* <> * <> * <> * <> @@ -44,6 +45,7 @@ For information about how to upgrade your cluster, see <>. -- +include::migrate_7_13.asciidoc[] include::migrate_7_12.asciidoc[] include::migrate_7_11.asciidoc[] include::migrate_7_10.asciidoc[] diff --git a/docs/reference/migration/migrate_7_13.asciidoc b/docs/reference/migration/migrate_7_13.asciidoc new file mode 100644 index 0000000000000..c3258fd7d8886 --- /dev/null +++ b/docs/reference/migration/migrate_7_13.asciidoc @@ -0,0 +1,67 @@ +[[migrating-7.13]] +== Migrating to 7.13 +++++ +7.13 +++++ + +This section discusses the changes that you need to be aware of when migrating +your application to {es} 7.13. + +See also <> and <>. + +// * <> +// * <> + +//NOTE: The notable-breaking-changes tagged regions are re-used in the +//Installation and Upgrade Guide + +//tag::notable-breaking-changes[] + +[discrete] +[[breaking-changes-7.13]] +=== Breaking changes + +The following changes in {es} 7.13 might affect your applications +and prevent them from operating normally. +Before upgrading to 7.13, review these changes and take the described steps +to mitigate the impact. + +NOTE: Breaking changes introduced in minor versions are +normally limited to security and bug fixes. +Significant changes in behavior are deprecated in a minor release and +the old behavior is supported until the next major release. +To find out if you are using any deprecated functionality, +enable <>. + + +[discrete] +[[deprecated-7.13]] +=== Deprecations + +The following functionality has been deprecated in {es} 7.13 +and will be removed in 8.0 +While this won't have an immediate impact on your applications, +we strongly encourage you take the described steps to update your code +after upgrading to 7.13. + +NOTE: Significant changes in behavior are deprecated in a minor release and +the old behavior is supported until the next major release. +To find out if you are using any deprecated functionality, +enable <>. + +[discrete] +[[breaking_713_security_changes]] +==== Security deprecations + +[[implicitly-disabled-basic-realms]] +Currently, the file and native realms have following implicit behaviours: + +* If file and native realms are not configured, they are implicitly disabled +if there are other explicitly configured realms. +* If no realm is available due to either unconfigured, explicitly disabled +or disallowed by the license, the file and native realms are always enabled +even when they are explicitly disabled. + +Both of the above behaviours are deprecated. In version 8.0.0, the file and +native realms will always be enabled unless explicitly disabled. If they are +explicitly disabled, they remain disabled at all times. diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java index 874797ec9372a..17bd3fbae5140 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/RealmSettings.java @@ -30,9 +30,10 @@ public class RealmSettings { public static final String PREFIX = "xpack.security.authc.realms."; + public static final String ENABLED_SETTING_KEY = "enabled"; public static final String ORDER_SETTING_KEY = "order"; - public static final Function> ENABLED_SETTING = affixSetting("enabled", + public static final Function> ENABLED_SETTING = affixSetting(ENABLED_SETTING_KEY, key -> Setting.boolSetting(key, true, Setting.Property.NodeScope)); public static final Function> ORDER_SETTING = affixSetting(ORDER_SETTING_KEY, key -> Setting.intSetting(key, Integer.MAX_VALUE, Setting.Property.NodeScope)); diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java index 830015742e65c..3573fa5c0e3cc 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java @@ -47,6 +47,7 @@ private DeprecationChecks() { NodeDeprecationChecks::checkProcessors, NodeDeprecationChecks::checkMissingRealmOrders, NodeDeprecationChecks::checkUniqueRealmOrders, + NodeDeprecationChecks::checkImplicitlyDisabledBasicRealms, (settings, pluginsAndModules) -> NodeDeprecationChecks.checkThreadPoolListenerQueueSize(settings), (settings, pluginsAndModules) -> NodeDeprecationChecks.checkThreadPoolListenerSize(settings), NodeDeprecationChecks::checkClusterRemoteConnectSetting, diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecks.java index ee8403721eab3..d9b67209946f5 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecks.java @@ -9,18 +9,24 @@ import org.elasticsearch.action.admin.cluster.node.info.PluginsAndModules; import org.elasticsearch.bootstrap.JavaVersion; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.env.Environment; import org.elasticsearch.node.Node; import org.elasticsearch.script.ScriptService; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.transport.RemoteClusterService; import org.elasticsearch.xpack.core.deprecation.DeprecationIssue; +import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; +import org.elasticsearch.xpack.core.security.authc.esnative.NativeRealmSettings; +import org.elasticsearch.xpack.core.security.authc.file.FileRealmSettings; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -52,6 +58,7 @@ static DeprecationIssue checkMissingRealmOrders(final Settings settings, final P final Set orderNotConfiguredRealms = RealmSettings.getRealmSettings(settings).entrySet() .stream() .filter(e -> false == e.getValue().hasValue(RealmSettings.ORDER_SETTING_KEY)) + .filter(e -> e.getValue().getAsBoolean(RealmSettings.ENABLED_SETTING_KEY, true)) .map(e -> RealmSettings.realmSettingPrefix(e.getKey()) + RealmSettings.ORDER_SETTING_KEY) .collect(Collectors.toSet()); @@ -104,6 +111,57 @@ static DeprecationIssue checkUniqueRealmOrders(final Settings settings, final Pl ); } + static DeprecationIssue checkImplicitlyDisabledBasicRealms(final Settings settings, final PluginsAndModules pluginsAndModules) { + final Map realmSettings = RealmSettings.getRealmSettings(settings); + if (realmSettings.isEmpty()) { + return null; + } + + boolean anyRealmEnabled = false; + final Set unconfiguredBasicRealms = + new HashSet<>(org.elasticsearch.common.collect.Set.of(FileRealmSettings.TYPE, NativeRealmSettings.TYPE)); + for (Map.Entry realmSetting: realmSettings.entrySet()) { + anyRealmEnabled = anyRealmEnabled || realmSetting.getValue().getAsBoolean(RealmSettings.ENABLED_SETTING_KEY, true); + unconfiguredBasicRealms.remove(realmSetting.getKey().getType()); + } + + final String details; + if (false == anyRealmEnabled) { + final List explicitlyDisabledBasicRealms = + Sets.difference(org.elasticsearch.common.collect.Set.of(FileRealmSettings.TYPE, NativeRealmSettings.TYPE), + unconfiguredBasicRealms).stream().sorted().collect(Collectors.toList()); + if (explicitlyDisabledBasicRealms.isEmpty()) { + return null; + } + details = String.format( + Locale.ROOT, + "Found explicitly disabled basic %s: [%s]. But %s will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled.", + explicitlyDisabledBasicRealms.size() == 1 ? "realm" : "realms", + Strings.collectionToDelimitedString(explicitlyDisabledBasicRealms, ","), + explicitlyDisabledBasicRealms.size() == 1 ? "it" : "they" + ); + } else { + if (unconfiguredBasicRealms.isEmpty()) { + return null; + } + details = String.format( + Locale.ROOT, + "Found implicitly disabled basic %s: [%s]. %s disabled because there are other explicitly configured realms." + + "In next major release, basic realms will always be enabled unless explicitly disabled.", + unconfiguredBasicRealms.size() == 1 ? "realm" : "realms", + Strings.collectionToDelimitedString(unconfiguredBasicRealms, ","), + unconfiguredBasicRealms.size() == 1 ? "It is" : "They are"); + } + return new DeprecationIssue( + DeprecationIssue.Level.WARNING, + "File and/or native realms are enabled by default in next major release.", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.13/deprecated-7.13.html#implicitly-disabled-basic-realms", + details + ); + + } + static DeprecationIssue checkThreadPoolListenerQueueSize(final Settings settings) { return checkThreadPoolListenerSetting("thread_pool.listener.queue_size", settings); } diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecksTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecksTests.java index 1510bab295b01..f447111eaa6a3 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecksTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/NodeDeprecationChecksTests.java @@ -98,11 +98,13 @@ public void testCheckProcessors() { public void testCheckMissingRealmOrders() { final RealmConfig.RealmIdentifier invalidRealm = - new RealmConfig.RealmIdentifier(randomAlphaOfLengthBetween(4, 12), randomAlphaOfLengthBetween(4, 12)); + new RealmConfig.RealmIdentifier(randomRealmTypeOtherThanFileOrNative(), randomAlphaOfLengthBetween(4, 12)); final RealmConfig.RealmIdentifier validRealm = - new RealmConfig.RealmIdentifier(randomAlphaOfLengthBetween(4, 12), randomAlphaOfLengthBetween(4, 12)); + new RealmConfig.RealmIdentifier(randomRealmTypeOtherThanFileOrNative(), randomAlphaOfLengthBetween(4, 12)); final Settings settings = Settings.builder() + .put("xpack.security.authc.realms.file.default_file.enabled", false) + .put("xpack.security.authc.realms.native.default_native.enabled", false) .put("xpack.security.authc.realms." + invalidRealm.getType() + "." + invalidRealm.getName() + ".enabled", "true") .put("xpack.security.authc.realms." + validRealm.getType() + "." + validRealm.getName() + ".order", randomInt()) .build(); @@ -123,16 +125,30 @@ public void testCheckMissingRealmOrders() { ), deprecationIssues.get(0)); } + public void testRealmOrderIsNotRequiredIfRealmIsDisabled() { + final RealmConfig.RealmIdentifier realmIdentifier = + new RealmConfig.RealmIdentifier(randomAlphaOfLengthBetween(4, 12), randomAlphaOfLengthBetween(4, 12)); + final Settings settings = + Settings.builder() + .put("xpack.security.authc.realms." + realmIdentifier.getType() + "." + realmIdentifier.getName() + ".enabled", "false") + .build(); + final PluginsAndModules pluginsAndModules = new PluginsAndModules(Collections.emptyList(), Collections.emptyList()); + final List deprecationIssues = getDeprecationIssues(settings, pluginsAndModules); + assertTrue(deprecationIssues.isEmpty()); + } + public void testCheckUniqueRealmOrders() { final int order = randomInt(9999); final RealmConfig.RealmIdentifier invalidRealm1 = - new RealmConfig.RealmIdentifier(randomAlphaOfLengthBetween(4, 12), randomAlphaOfLengthBetween(4, 12)); + new RealmConfig.RealmIdentifier(randomRealmTypeOtherThanFileOrNative(), randomAlphaOfLengthBetween(4, 12)); final RealmConfig.RealmIdentifier invalidRealm2 = - new RealmConfig.RealmIdentifier(randomAlphaOfLengthBetween(4, 12), randomAlphaOfLengthBetween(4, 12)); + new RealmConfig.RealmIdentifier(randomRealmTypeOtherThanFileOrNative(), randomAlphaOfLengthBetween(4, 12)); final RealmConfig.RealmIdentifier validRealm = - new RealmConfig.RealmIdentifier(randomAlphaOfLengthBetween(4, 12), randomAlphaOfLengthBetween(4, 12)); + new RealmConfig.RealmIdentifier(randomRealmTypeOtherThanFileOrNative(), randomAlphaOfLengthBetween(4, 12)); final Settings settings = Settings.builder() + .put("xpack.security.authc.realms.file.default_file.enabled", false) + .put("xpack.security.authc.realms.native.default_native.enabled", false) .put("xpack.security.authc.realms." + invalidRealm1.getType() + "." + invalidRealm1.getName() + ".order", order) .put("xpack.security.authc.realms." @@ -159,16 +175,121 @@ public void testCheckUniqueRealmOrders() { public void testCorrectRealmOrders() { final int order = randomInt(9999); final Settings settings = Settings.builder() + .put("xpack.security.authc.realms.file.default_file.enabled", false) + .put("xpack.security.authc.realms.native.default_native.enabled", false) .put("xpack.security.authc.realms." - + randomAlphaOfLengthBetween(4, 12) + "." + randomAlphaOfLengthBetween(4, 12) + ".order", order) + + randomRealmTypeOtherThanFileOrNative() + "." + randomAlphaOfLengthBetween(4, 12) + ".order", order) .put("xpack.security.authc.realms." - + randomAlphaOfLengthBetween(4, 12) + "." + randomAlphaOfLengthBetween(4, 12) + ".order", order + 1) + + randomRealmTypeOtherThanFileOrNative() + "." + randomAlphaOfLengthBetween(4, 12) + ".order", order + 1) .build(); final PluginsAndModules pluginsAndModules = new PluginsAndModules(Collections.emptyList(), Collections.emptyList()); final List deprecationIssues = getDeprecationIssues(settings, pluginsAndModules); - assertEquals(0, deprecationIssues.size()); + assertTrue(deprecationIssues.isEmpty()); + } + + public void testCheckImplicitlyDisabledBasicRealms() { + final Settings.Builder builder = Settings.builder(); + + final boolean otherRealmConfigured = randomBoolean(); + final boolean otherRealmEnabled = randomBoolean(); + if (otherRealmConfigured) { + final int otherRealmId = randomIntBetween(0, 9); + final String otherRealmName = randomAlphaOfLengthBetween(4, 12); + if (otherRealmEnabled) { + builder.put("xpack.security.authc.realms.type_" + otherRealmId + ".realm_" + otherRealmName + ".order", 1); + } else { + builder.put("xpack.security.authc.realms.type_" + otherRealmId + ".realm_" + otherRealmName + ".enabled", false); + } + } + final boolean fileRealmConfigured = randomBoolean(); + final boolean fileRealmEnabled = randomBoolean(); + if (fileRealmConfigured) { + final String fileRealmName = randomAlphaOfLengthBetween(4, 12); + // Configure file realm or explicitly disable it + if (fileRealmEnabled) { + builder.put("xpack.security.authc.realms.file." + fileRealmName + ".order", 10); + } else { + builder.put("xpack.security.authc.realms.file." + fileRealmName + ".enabled", false); + } + } + final boolean nativeRealmConfigured = randomBoolean(); + final boolean nativeRealmEnabled = randomBoolean(); + if (nativeRealmConfigured) { + final String nativeRealmName = randomAlphaOfLengthBetween(4, 12); + // Configure native realm or explicitly disable it + if (nativeRealmEnabled) { + builder.put("xpack.security.authc.realms.native." + nativeRealmName + ".order", 20); + } else { + builder.put("xpack.security.authc.realms.native." + nativeRealmName + ".enabled", false); + } + } + final Settings settings = builder.build(); + final PluginsAndModules pluginsAndModules = new PluginsAndModules(Collections.emptyList(), Collections.emptyList()); + final List deprecationIssues = getDeprecationIssues(settings, pluginsAndModules); + + if (otherRealmConfigured && otherRealmEnabled) { + if (false == fileRealmConfigured && false == nativeRealmConfigured) { + assertCommonImplicitDisabledRealms(deprecationIssues); + assertEquals("Found implicitly disabled basic realms: [file,native]. " + + "They are disabled because there are other explicitly configured realms." + + "In next major release, basic realms will always be enabled unless explicitly disabled.", + deprecationIssues.get(0).getDetails()); + } else if (false == fileRealmConfigured) { + assertCommonImplicitDisabledRealms(deprecationIssues); + assertEquals("Found implicitly disabled basic realm: [file]. " + + "It is disabled because there are other explicitly configured realms." + + "In next major release, basic realms will always be enabled unless explicitly disabled.", + deprecationIssues.get(0).getDetails()); + } else if (false == nativeRealmConfigured) { + assertCommonImplicitDisabledRealms(deprecationIssues); + assertEquals("Found implicitly disabled basic realm: [native]. " + + "It is disabled because there are other explicitly configured realms." + + "In next major release, basic realms will always be enabled unless explicitly disabled.", + deprecationIssues.get(0).getDetails()); + } else { + assertTrue(deprecationIssues.isEmpty()); + } + } else { + if (false == fileRealmConfigured && false == nativeRealmConfigured) { + assertTrue(deprecationIssues.isEmpty()); + } else if (false == fileRealmConfigured) { + assertCommonImplicitDisabledRealms(deprecationIssues); + if (nativeRealmEnabled) { + assertEquals("Found implicitly disabled basic realm: [file]. " + + "It is disabled because there are other explicitly configured realms." + + "In next major release, basic realms will always be enabled unless explicitly disabled.", + deprecationIssues.get(0).getDetails()); + } else { + assertEquals("Found explicitly disabled basic realm: [native]. " + + "But it will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled.", + deprecationIssues.get(0).getDetails()); + } + } else if (false == nativeRealmConfigured) { + assertCommonImplicitDisabledRealms(deprecationIssues); + if (fileRealmEnabled) { + assertEquals("Found implicitly disabled basic realm: [native]. " + + "It is disabled because there are other explicitly configured realms." + + "In next major release, basic realms will always be enabled unless explicitly disabled.", + deprecationIssues.get(0).getDetails()); + } else { + assertEquals("Found explicitly disabled basic realm: [file]. " + + "But it will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled.", + deprecationIssues.get(0).getDetails()); + } + } else { + if (false == fileRealmEnabled && false == nativeRealmEnabled) { + assertCommonImplicitDisabledRealms(deprecationIssues); + assertEquals("Found explicitly disabled basic realms: [file,native]. " + + "But they will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled.", + deprecationIssues.get(0).getDetails()); + } + } + } } public void testThreadPoolListenerQueueSize() { @@ -349,4 +470,18 @@ private List getDeprecationIssues(Settings settings, PluginsAn return issues; } + + private void assertCommonImplicitDisabledRealms(List deprecationIssues) { + assertEquals(1, deprecationIssues.size()); + assertEquals("File and/or native realms are enabled by default in next major release.", + deprecationIssues.get(0).getMessage()); + assertEquals("https://www.elastic.co/guide/en/elasticsearch/reference" + + "/7.13/deprecated-7.13.html#implicitly-disabled-basic-realms", + deprecationIssues.get(0).getUrl()); + } + + private String randomRealmTypeOtherThanFileOrNative() { + return randomValueOtherThanMany(t -> org.elasticsearch.common.collect.Set.of("file", "native").contains(t), + () -> randomAlphaOfLengthBetween(4, 12)); + } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java index e86aed51edee3..88a66ddecec09 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.env.Environment; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.license.XPackLicenseState.Feature; @@ -176,10 +177,16 @@ protected List initRealms() throws Exception { Map> nameToRealmIdentifier = new HashMap<>(); Set missingOrderRealmSettingKeys = new TreeSet<>(); Map> orderToRealmOrderSettingKeys = new HashMap<>(); + Set unconfiguredBasicRealms = new HashSet<>( + org.elasticsearch.common.collect.Set.of(FileRealmSettings.TYPE, NativeRealmSettings.TYPE)); for (final Map.Entry entry: realmsSettings.entrySet()) { final RealmConfig.RealmIdentifier identifier = entry.getKey(); if (false == entry.getValue().hasValue(RealmSettings.ORDER_SETTING_KEY)) { - missingOrderRealmSettingKeys.add(RealmSettings.getFullSettingKey(identifier, RealmSettings.ORDER_SETTING)); + // If the realm is disabled, it is ok to have no order setting. This is only really useful for file/native realm. + // Because settings of other realms can just be entirely removed. + if (entry.getValue().getAsBoolean(RealmSettings.ENABLED_SETTING_KEY, true)) { + missingOrderRealmSettingKeys.add(RealmSettings.getFullSettingKey(identifier, RealmSettings.ORDER_SETTING)); + } } else { orderToRealmOrderSettingKeys.computeIfAbsent(entry.getValue().get(RealmSettings.ORDER_SETTING_KEY), k -> new TreeSet<>()) .add(RealmSettings.getFullSettingKey(identifier, RealmSettings.ORDER_SETTING)); @@ -189,6 +196,7 @@ protected List initRealms() throws Exception { throw new IllegalArgumentException("unknown realm type [" + identifier.getType() + "] for realm [" + identifier + "]"); } RealmConfig config = new RealmConfig(identifier, settings, env, threadContext); + unconfiguredBasicRealms.remove(identifier.getType()); if (config.enabled() == false) { if (logger.isDebugEnabled()) { logger.debug("realm [{}] is disabled", identifier); @@ -218,6 +226,7 @@ protected List initRealms() throws Exception { realms.add(realm); } + logDeprecationForImplicitlyDisabledBasicRealms(realms, unconfiguredBasicRealms); if (realms.isEmpty() == false) { Collections.sort(realms); } else { @@ -369,4 +378,31 @@ private void logDeprecationIfFound(Set missingOrderRealmSettingKeys, Map } } + private void logDeprecationForImplicitlyDisabledBasicRealms(List realms, Set unconfiguredBasicRealms) { + if (realms.isEmpty()) { // No available realm + final List explicitlyDisabledBasicRealms = + Sets.difference(org.elasticsearch.common.collect.Set.of(FileRealmSettings.TYPE, NativeRealmSettings.TYPE), + unconfiguredBasicRealms).stream().sorted().collect(Collectors.toList()); + if (explicitlyDisabledBasicRealms.isEmpty()) { + return; + } + deprecationLogger.deprecate(DeprecationCategory.SECURITY, "implicitly_disabled_basic_realms", + "Found explicitly disabled basic {}: [{}]. But {} will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled.", + explicitlyDisabledBasicRealms.size() == 1 ? "realm" : "realms", + Strings.collectionToDelimitedString(explicitlyDisabledBasicRealms, ","), + explicitlyDisabledBasicRealms.size() == 1 ? "it" : "they"); + } else { // There are configured and enabled realms + if (unconfiguredBasicRealms.isEmpty()) { + return; + } + deprecationLogger.deprecate(DeprecationCategory.SECURITY, "implicitly_disabled_basic_realms", + "Found implicitly disabled basic {}: [{}]. {} disabled because there are other explicitly configured realms. " + + "In next major release, basic realms will always be enabled unless explicitly disabled.", + unconfiguredBasicRealms.size() == 1 ? "realm" : "realms", + Strings.collectionToDelimitedString(unconfiguredBasicRealms, ","), + unconfiguredBasicRealms.size() == 1 ? "It is" : "They are" + ); + } + } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java index 1f7593a50cfd4..09b82bca60943 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java @@ -110,6 +110,7 @@ public void testWithSettings() throws Exception { builder.put("xpack.security.authc.realms.type_" + i + ".realm_" + i + ".order", orders.get(i)); orderToIndex.put(orders.get(i), i); } + disableFileAndNativeRealms(builder); Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -150,6 +151,7 @@ public void testWithSettingsWhereDifferentRealmsHaveSameOrder() throws Exception // set same order for all realms builder.put("xpack.security.authc.realms.type_" + randomizedRealmId + ".realm_" + randomizedRealmName + ".order", 1); } + disableFileAndNativeRealms(builder); Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -244,6 +246,7 @@ public void testUnlicensedWithOnlyCustomRealms() throws Exception { builder.put("xpack.security.authc.realms.type_" + i + ".realm_" + i + ".order", orders.get(i)); orderToIndex.put(orders.get(i), i); } + disableFileAndNativeRealms(builder); Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -330,6 +333,7 @@ public void testUnlicensedWithInternalRealms() throws Exception { .put("path.home", createTempDir()) .put("xpack.security.authc.realms.ldap.foo.order", "0") .put("xpack.security.authc.realms.type_0.custom.order", "1"); + disableFileAndNativeRealms(builder); Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -399,6 +403,7 @@ public void testUnlicensedWithNativeRealmSettings() throws Exception { .put("path.home", createTempDir()) .put("xpack.security.authc.realms.ldap.foo.order", "0") .put("xpack.security.authc.realms." + type + ".native.order", "1"); + disableFileAndNativeRealms(builder); Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -437,6 +442,7 @@ public void testUnlicensedWithNonStandardRealms() throws Exception { Settings.Builder builder = Settings.builder() .put("path.home", createTempDir()) .put("xpack.security.authc.realms." + selectedRealmType + ".foo.order", "0"); + disableFileAndNativeRealms(builder); Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -490,6 +496,7 @@ public void testUnlicensedWithNonStandardRealms() throws Exception { public void testDisabledRealmsAreNotAdded() throws Exception { Settings.Builder builder = Settings.builder() .put("path.home", createTempDir()); + disableFileAndNativeRealms(builder); List orders = new ArrayList<>(randomRealmTypesCount); for (int i = 0; i < randomRealmTypesCount; i++) { orders.add(i); @@ -543,10 +550,11 @@ public void testDisabledRealmsAreNotAdded() throws Exception { } public void testAuthcAuthzDisabled() throws Exception { - Settings settings = Settings.builder() + Settings.Builder builder = Settings.builder() .put("path.home", createTempDir()) - .put("xpack.security.authc.realms." + FileRealmSettings.TYPE + ".realm_1.order", 0) - .build(); + .put("xpack.security.authc.realms." + FileRealmSettings.TYPE + ".realm_1.order", 0); + disableFileAndNativeRealms(builder); + final Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -562,6 +570,7 @@ public void testUsageStats() throws Exception { .put("path.home", createTempDir()) .put("xpack.security.authc.realms.type_0.foo.order", "0") .put("xpack.security.authc.realms.type_0.bar.order", "1"); + disableFileAndNativeRealms(builder); Settings settings = builder.build(); Environment env = TestEnvironment.newEnvironment(settings); Realms realms = new Realms(settings, env, factories, licenseState, threadContext, reservedRealm); @@ -628,10 +637,11 @@ public void testInitRealmsFailsForMultipleKerberosRealms() throws IOException { public void testWarningForMissingRealmOrder() throws Exception { final int realmTypeId = randomIntBetween(0, randomRealmTypesCount - 1); final String realmName = randomAlphaOfLengthBetween(4, 12); - final Settings settings = Settings.builder() + final Settings.Builder builder = Settings.builder() .put("path.home", createTempDir()) - .put("xpack.security.authc.realms.type_" + realmTypeId + ".realm_" + realmName + ".enabled", true) - .build(); + .put("xpack.security.authc.realms.type_" + realmTypeId + ".realm_" + realmName + ".enabled", true); + disableFileAndNativeRealms(builder); + final Settings settings = builder.build(); new Realms(settings, TestEnvironment.newEnvironment(settings), factories, licenseState, threadContext, reservedRealm); assertWarnings("Found realms without order config: [xpack.security.authc.realms.type_" @@ -639,6 +649,106 @@ public void testWarningForMissingRealmOrder() throws Exception { + "In next major release, node will fail to start with missing realm order."); } + public void testWarningsForImplicitlyDisabledBasicRealms() throws Exception { + final Settings.Builder builder = Settings.builder() + .put("path.home", createTempDir()); + final boolean otherRealmConfigured = randomBoolean(); + final boolean otherRealmEnabled = randomBoolean(); + if (otherRealmConfigured) { + final int otherRealmId = randomIntBetween(0, randomRealmTypesCount - 1); + final String otherRealmName = randomAlphaOfLengthBetween(4, 12); + if (otherRealmEnabled) { + builder.put("xpack.security.authc.realms.type_" + otherRealmId + ".realm_" + otherRealmName + ".order", 1); + } else { + builder.put("xpack.security.authc.realms.type_" + otherRealmId + ".realm_" + otherRealmName + ".enabled", false); + } + } + final boolean fileRealmConfigured = randomBoolean(); + final boolean fileRealmEnabled = randomBoolean(); + if (fileRealmConfigured) { + final String fileRealmName = randomAlphaOfLengthBetween(4, 12); + // Configure file realm or explicitly disable it + if (fileRealmEnabled) { + builder.put("xpack.security.authc.realms.file." + fileRealmName + ".order", 10); + } else { + builder.put("xpack.security.authc.realms.file." + fileRealmName + ".enabled", false); + } + } + final boolean nativeRealmConfigured = randomBoolean(); + final boolean nativeRealmEnabled = randomBoolean(); + if (nativeRealmConfigured) { + final String nativeRealmName = randomAlphaOfLengthBetween(4, 12); + // Configure native realm or explicitly disable it + if (nativeRealmEnabled) { + builder.put("xpack.security.authc.realms.native." + nativeRealmName + ".order", 20); + } else { + builder.put("xpack.security.authc.realms.native." + nativeRealmName + ".enabled", false); + } + } + final Settings settings = builder.build(); + final Realms realms = + new Realms(settings, TestEnvironment.newEnvironment(settings), factories, licenseState, threadContext, reservedRealm); + + if (otherRealmConfigured && otherRealmEnabled) { + if (false == fileRealmConfigured && false == nativeRealmConfigured) { + assertWarnings("Found implicitly disabled basic realms: [file,native]. " + + "They are disabled because there are other explicitly configured realms. " + + "In next major release, basic realms will always be enabled unless explicitly disabled."); + } else if (false == fileRealmConfigured) { + assertWarnings("Found implicitly disabled basic realm: [file]. " + + "It is disabled because there are other explicitly configured realms. " + + "In next major release, basic realms will always be enabled unless explicitly disabled."); + } else if (false == nativeRealmConfigured) { + assertWarnings("Found implicitly disabled basic realm: [native]. " + + "It is disabled because there are other explicitly configured realms. " + + "In next major release, basic realms will always be enabled unless explicitly disabled."); + } + } else { + if (false == fileRealmConfigured && false == nativeRealmConfigured) { + // Default behaviour of implicitly enabling file and native realms + assertNotNull(realms.realm(FileRealmSettings.DEFAULT_NAME)); + assertNotNull(realms.realm(NativeRealmSettings.DEFAULT_NAME)); + } else if (false == fileRealmConfigured) { + if (nativeRealmEnabled) { + assertWarnings("Found implicitly disabled basic realm: [file]. " + + "It is disabled because there are other explicitly configured realms. " + + "In next major release, basic realms will always be enabled unless explicitly disabled."); + } else { + assertWarnings("Found explicitly disabled basic realm: [native]. " + + "But it will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled."); + assertNotNull(realms.realm(FileRealmSettings.DEFAULT_NAME)); + assertNotNull(realms.realm(NativeRealmSettings.DEFAULT_NAME)); + } + } else if (false == nativeRealmConfigured) { + if (fileRealmEnabled) { + assertWarnings("Found implicitly disabled basic realm: [native]. " + + "It is disabled because there are other explicitly configured realms. " + + "In next major release, basic realms will always be enabled unless explicitly disabled."); + } else { + assertWarnings("Found explicitly disabled basic realm: [file]. " + + "But it will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled."); + assertNotNull(realms.realm(FileRealmSettings.DEFAULT_NAME)); + assertNotNull(realms.realm(NativeRealmSettings.DEFAULT_NAME)); + } + } else { + if (false == fileRealmEnabled && false == nativeRealmEnabled) { + assertWarnings("Found explicitly disabled basic realms: [file,native]. " + + "But they will be enabled because no other realms are configured or enabled. " + + "In next major release, explicitly disabled basic realms will remain disabled."); + assertNotNull(realms.realm(FileRealmSettings.DEFAULT_NAME)); + assertNotNull(realms.realm(NativeRealmSettings.DEFAULT_NAME)); + } + } + } + } + + private void disableFileAndNativeRealms(Settings.Builder builder) { + builder.put("xpack.security.authc.realms.file.default_file.enabled", false) + .put("xpack.security.authc.realms.native.default_native.enabled", false); + } + static class DummyRealm extends Realm { DummyRealm(String type, RealmConfig config) {