From edc9580f66ff6ccfd86145dab49fe7e12f05c691 Mon Sep 17 00:00:00 2001 From: jaymode Date: Tue, 15 Mar 2016 10:06:54 -0400 Subject: [PATCH] security: validate that security and audit indices can be auto created Adds a check to the settings at startup to ensure that the security and audit indices are allowed to be auto created if a user has disabled auto create explicitly. Additionally fixes a small issue with the error message for watcher passing the incorrect value. Closes elastic/elasticsearch#1453 Original commit: elastic/x-pack-elasticsearch@2b0698ff19f84633329f4aab05769bbff7007788 --- .../java/org/elasticsearch/shield/Shield.java | 77 +++++++++++++++++++ .../shield/ShieldPluginSettingsTests.java | 57 ++++++++++++++ .../org/elasticsearch/watcher/Watcher.java | 2 +- .../watcher/WatcherPluginTests.java | 6 ++ 4 files changed, 141 insertions(+), 1 deletion(-) diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Shield.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Shield.java index 654b02d4dc4..57cc08d3be4 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Shield.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Shield.java @@ -6,11 +6,15 @@ package org.elasticsearch.shield; import org.elasticsearch.action.ActionModule; +import org.elasticsearch.common.Booleans; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkModule; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsModule; @@ -35,6 +39,8 @@ import org.elasticsearch.shield.action.user.TransportPutUserAction; import org.elasticsearch.shield.action.user.TransportDeleteUserAction; import org.elasticsearch.shield.action.user.TransportGetUsersAction; import org.elasticsearch.shield.audit.AuditTrailModule; +import org.elasticsearch.shield.audit.index.IndexAuditTrail; +import org.elasticsearch.shield.audit.index.IndexNameResolver; import org.elasticsearch.shield.audit.logfile.LoggingAuditTrail; import org.elasticsearch.shield.authc.AuthenticationModule; import org.elasticsearch.shield.authc.Realms; @@ -71,6 +77,8 @@ import org.elasticsearch.shield.transport.filter.IPFilter; import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport; import org.elasticsearch.shield.transport.netty.ShieldNettyTransport; import org.elasticsearch.xpack.XPackPlugin; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import java.util.ArrayList; import java.util.Arrays; @@ -102,6 +110,7 @@ public class Shield { this.enabled = XPackPlugin.featureEnabled(settings, NAME, true); if (enabled && !transportClientMode) { failIfShieldQueryCacheIsNotActive(settings, true); + validateAutoCreateIndex(settings); } } @@ -410,4 +419,72 @@ public class Shield { .INDEX_QUERY_CACHE_TYPE_SETTING.getKey() + "] with value [" + queryCacheImplementation + "]"); } } + + static void validateAutoCreateIndex(Settings settings) { + String value = settings.get("action.auto_create_index"); + if (value == null) { + return; + } + + final boolean indexAuditingEnabled = AuditTrailModule.indexAuditLoggingEnabled(settings); + final String auditIndex = indexAuditingEnabled ? "," + IndexAuditTrail.INDEX_NAME_PREFIX + "*" : ""; + String errorMessage = LoggerMessageFormat.format("the [action.auto_create_index] setting value [{}] is too" + + " restrictive. disable [action.auto_create_index] or set it to " + + "[{}{}]", (Object) value, ShieldTemplateService.SECURITY_INDEX_NAME, auditIndex); + if (Booleans.isExplicitFalse(value)) { + throw new IllegalArgumentException(errorMessage); + } + + if (Booleans.isExplicitTrue(value)) { + return; + } + + String[] matches = Strings.commaDelimitedListToStringArray(value); + List indices = new ArrayList<>(); + indices.add(ShieldTemplateService.SECURITY_INDEX_NAME); + if (indexAuditingEnabled) { + DateTime now = new DateTime(DateTimeZone.UTC); + // just use daily rollover + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now, IndexNameResolver.Rollover.DAILY)); + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusDays(1), IndexNameResolver.Rollover.DAILY)); + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(1), IndexNameResolver.Rollover.DAILY)); + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(2), IndexNameResolver.Rollover.DAILY)); + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(3), IndexNameResolver.Rollover.DAILY)); + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(4), IndexNameResolver.Rollover.DAILY)); + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(5), IndexNameResolver.Rollover.DAILY)); + indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(6), IndexNameResolver.Rollover.DAILY)); + } + + for (String index : indices) { + boolean matched = false; + for (String match : matches) { + char c = match.charAt(0); + if (c == '-') { + if (Regex.simpleMatch(match.substring(1), index)) { + throw new IllegalArgumentException(errorMessage); + } + } else if (c == '+') { + if (Regex.simpleMatch(match.substring(1), index)) { + matched = true; + break; + } + } else { + if (Regex.simpleMatch(match, index)) { + matched = true; + break; + } + } + } + if (!matched) { + throw new IllegalArgumentException(errorMessage); + } + } + + if (indexAuditingEnabled) { + logger.warn("the [action.auto_create_index] setting is configured to be restrictive [{}]. " + + " for the next 6 months audit indices are allowed to be created, but please make sure" + + " that any future history indices after 6 months with the pattern " + + "[.shield_audit_log*] are allowed to be created", value); + } + } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginSettingsTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginSettingsTests.java index f9cb0e28196..7689bf174bf 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginSettingsTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginSettingsTests.java @@ -7,6 +7,7 @@ package org.elasticsearch.shield; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.shield.audit.index.IndexAuditTrail; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.XPackPlugin; @@ -16,6 +17,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.not; public class ShieldPluginSettingsTests extends ESTestCase { @@ -132,4 +134,59 @@ public class ShieldPluginSettingsTests extends ESTestCase { assertThat(additionalSettings.get("tribe.t2.shield.bar"), is("foo")); assertThat(additionalSettings.getAsArray("tribe.t2.shield.something.else.here"), arrayContaining("foo", "bar")); } + + public void testValidAutoCreateIndex() { + Shield.validateAutoCreateIndex(Settings.EMPTY); + Shield.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", true).build()); + + try { + Shield.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", false).build()); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString(ShieldTemplateService.SECURITY_INDEX_NAME)); + assertThat(e.getMessage(), not(containsString(IndexAuditTrail.INDEX_NAME_PREFIX))); + } + + Shield.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".security").build()); + Shield.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", "*s*").build()); + Shield.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".s*").build()); + + try { + Shield.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", "foo").build()); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString(ShieldTemplateService.SECURITY_INDEX_NAME)); + assertThat(e.getMessage(), not(containsString(IndexAuditTrail.INDEX_NAME_PREFIX))); + } + + try { + Shield.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".shield_audit_log*").build()); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString(ShieldTemplateService.SECURITY_INDEX_NAME)); + } + + Shield.validateAutoCreateIndex(Settings.builder() + .put("action.auto_create_index", ".security") + .put("shield.audit.enabled", true) + .build()); + + try { + Shield.validateAutoCreateIndex(Settings.builder() + .put("action.auto_create_index", ".security") + .put("shield.audit.enabled", true) + .put("shield.audit.outputs", randomFrom("index", "logfile,index")) + .build()); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString(ShieldTemplateService.SECURITY_INDEX_NAME)); + assertThat(e.getMessage(), containsString(IndexAuditTrail.INDEX_NAME_PREFIX)); + } + + Shield.validateAutoCreateIndex(Settings.builder() + .put("action.auto_create_index", ".shield_audit_log*,.security") + .put("shield.audit.enabled", true) + .put("shield.audit.outputs", randomFrom("index", "logfile,index")) + .build()); + } } diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java index 4109f07ab09..1d9a203f54c 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java @@ -271,7 +271,7 @@ public class Watcher { String errorMessage = LoggerMessageFormat.format("the [action.auto_create_index] setting value [{}] is too" + " restrictive. disable [action.auto_create_index] or set it to " + - "[.watches,.triggered_watches,.watcher-history*]", (Object) settings); + "[.watches,.triggered_watches,.watcher-history*]", (Object) value); if (Booleans.isExplicitFalse(value)) { throw new IllegalArgumentException(errorMessage); } diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/watcher/WatcherPluginTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/watcher/WatcherPluginTests.java index cbddc934503..a1c9e59aeea 100644 --- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/watcher/WatcherPluginTests.java +++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/watcher/WatcherPluginTests.java @@ -8,6 +8,8 @@ package org.elasticsearch.watcher; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; +import static org.hamcrest.Matchers.containsString; + public class WatcherPluginTests extends ESTestCase { public void testValidAutoCreateIndex() { @@ -17,6 +19,7 @@ public class WatcherPluginTests extends ESTestCase { Watcher.validAutoCreateIndex(Settings.builder().put("action.auto_create_index", false).build()); fail("IllegalArgumentException expected"); } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("[.watches,.triggered_watches,.watcher-history*]")); } Watcher.validAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".watches,.triggered_watches,.watcher-history*").build()); @@ -26,16 +29,19 @@ public class WatcherPluginTests extends ESTestCase { Watcher.validAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".watches").build()); fail("IllegalArgumentException expected"); } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("[.watches,.triggered_watches,.watcher-history*]")); } try { Watcher.validAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".triggered_watch").build()); fail("IllegalArgumentException expected"); } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("[.watches,.triggered_watches,.watcher-history*]")); } try { Watcher.validAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".watcher-history*").build()); fail("IllegalArgumentException expected"); } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("[.watches,.triggered_watches,.watcher-history*]")); } }