From aa0148735e3384b23d504d44ed4f2e20f64f1bca Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 24 Feb 2020 12:30:44 +0200 Subject: [PATCH] Logfile audit settings validation (#52537) Add validation for the following logfile audit settings: xpack.security.audit.logfile.events.include xpack.security.audit.logfile.events.exclude xpack.security.audit.logfile.events.ignore_filters.*.users xpack.security.audit.logfile.events.ignore_filters.*.realms xpack.security.audit.logfile.events.ignore_filters.*.roles xpack.security.audit.logfile.events.ignore_filters.*.indices Closes #52357 Relates #47711 #47038 Follows the example from #47246 --- .../audit/logfile/LoggingAuditTrail.java | 34 +++++++------ .../AuditTrailSettingsUpdateTests.java | 2 +- .../audit/logfile/LoggingAuditTrailTests.java | 50 +++++++++++++++++++ 3 files changed, 71 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java index b171f58a4ade7..40cb6f5969d44 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java @@ -136,27 +136,33 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener { ANONYMOUS_ACCESS_DENIED.toString(), AUTHENTICATION_FAILED.toString(), CONNECTION_DENIED.toString(), TAMPERED_REQUEST.toString(), RUN_AS_DENIED.toString(), RUN_AS_GRANTED.toString()); public static final Setting> INCLUDE_EVENT_SETTINGS = Setting.listSetting(setting("audit.logfile.events.include"), - DEFAULT_EVENT_INCLUDES, Function.identity(), Property.NodeScope, Property.Dynamic); + DEFAULT_EVENT_INCLUDES, Function.identity(), value -> AuditLevel.parse(value, List.of()), + Property.NodeScope, Property.Dynamic); public static final Setting> EXCLUDE_EVENT_SETTINGS = Setting.listSetting(setting("audit.logfile.events.exclude"), - Collections.emptyList(), Function.identity(), Property.NodeScope, Property.Dynamic); + Collections.emptyList(), Function.identity(), value -> AuditLevel.parse(List.of(), value), + Property.NodeScope, Property.Dynamic); public static final Setting INCLUDE_REQUEST_BODY = Setting.boolSetting(setting("audit.logfile.events.emit_request_body"), false, Property.NodeScope, Property.Dynamic); private static final String FILTER_POLICY_PREFIX = setting("audit.logfile.events.ignore_filters."); // because of the default wildcard value (*) for the field filter, a policy with // an unspecified filter field will match events that have any value for that // particular field, as well as events with that particular field missing - private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_PRINCIPALS = Setting.affixKeySetting(FILTER_POLICY_PREFIX, - "users", - (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), Property.NodeScope, Property.Dynamic)); - private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_REALMS = Setting.affixKeySetting(FILTER_POLICY_PREFIX, - "realms", - (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), Property.NodeScope, Property.Dynamic)); - private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_ROLES = Setting.affixKeySetting(FILTER_POLICY_PREFIX, - "roles", - (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), Property.NodeScope, Property.Dynamic)); - private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_INDICES = Setting.affixKeySetting(FILTER_POLICY_PREFIX, - "indices", - (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), Property.NodeScope, Property.Dynamic)); + protected static final Setting.AffixSetting> FILTER_POLICY_IGNORE_PRINCIPALS = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "users", + (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), + value -> EventFilterPolicy.parsePredicate(value), Property.NodeScope, Property.Dynamic)); + protected static final Setting.AffixSetting> FILTER_POLICY_IGNORE_REALMS = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "realms", + (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), + value -> EventFilterPolicy.parsePredicate(value), Property.NodeScope, Property.Dynamic)); + protected static final Setting.AffixSetting> FILTER_POLICY_IGNORE_ROLES = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "roles", + (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), + value -> EventFilterPolicy.parsePredicate(value), Property.NodeScope, Property.Dynamic)); + protected static final Setting.AffixSetting> FILTER_POLICY_IGNORE_INDICES = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "indices", + (key) -> Setting.listSetting(key, Collections.singletonList("*"), Function.identity(), + value -> EventFilterPolicy.parsePredicate(value), Property.NodeScope, Property.Dynamic)); private static final Marker AUDIT_MARKER = MarkerManager.getMarker("org.elasticsearch.xpack.security.audit"); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/AuditTrailSettingsUpdateTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/AuditTrailSettingsUpdateTests.java index 23408f5668ec9..b469d32c082cc 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/AuditTrailSettingsUpdateTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/AuditTrailSettingsUpdateTests.java @@ -99,7 +99,7 @@ public void testInvalidFilterSettings() throws Exception { settingsBuilder.put(randomFrom(allSettingsKeys), invalidLuceneRegex); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> client().admin().cluster().prepareUpdateSettings().setTransientSettings(settingsBuilder.build()).get()); - assertThat(e.getMessage(), containsString("illegal value can't update")); + assertThat(e.getMessage(), containsString("invalid pattern [/invalid]")); } public void testDynamicHostSettings() { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java index 230cb4c4950d2..b4f4022bbb654 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java @@ -67,7 +67,9 @@ import java.util.regex.Pattern; import static org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.PRINCIPAL_ROLES_FIELD_NAME; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; @@ -208,6 +210,54 @@ public void clearLog() throws Exception { CapturingLogger.output(logger.getName(), Level.INFO).clear(); } + public void testEventsSettingValidation() { + final String prefix = "xpack.security.audit.logfile.events."; + Settings settings = Settings.builder().putList(prefix + "include", Arrays.asList("access_granted", "bogus")).build(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> LoggingAuditTrail.INCLUDE_EVENT_SETTINGS.get(settings)); + assertThat(e, hasToString(containsString("invalid event name specified [bogus]"))); + + Settings settings2 = Settings.builder().putList(prefix + "exclude", Arrays.asList("access_denied", "foo")).build(); + e = expectThrows(IllegalArgumentException.class, () -> LoggingAuditTrail.EXCLUDE_EVENT_SETTINGS.get(settings2)); + assertThat(e, hasToString(containsString("invalid event name specified [foo]"))); + } + + public void testAuditFilterSettingValidation() { + final String prefix = "xpack.security.audit.logfile.events."; + Settings settings = + Settings.builder().putList(prefix + "ignore_filters.filter1.users", Arrays.asList("mickey", "/bogus")).build(); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> LoggingAuditTrail.FILTER_POLICY_IGNORE_PRINCIPALS.getConcreteSettingForNamespace("filter1").get(settings)); + assertThat(e, hasToString(containsString("invalid pattern [/bogus]"))); + + Settings settings2 = Settings.builder() + .putList(prefix + "ignore_filters.filter2.users", Arrays.asList("tom", "cruise")) + .putList(prefix + "ignore_filters.filter2.realms", Arrays.asList("native", "/foo")).build(); + assertThat(LoggingAuditTrail.FILTER_POLICY_IGNORE_PRINCIPALS.getConcreteSettingForNamespace("filter2").get(settings2), + containsInAnyOrder("tom", "cruise")); + e = expectThrows(IllegalArgumentException.class, + () -> LoggingAuditTrail.FILTER_POLICY_IGNORE_REALMS.getConcreteSettingForNamespace("filter2").get(settings2)); + assertThat(e, hasToString(containsString("invalid pattern [/foo]"))); + + Settings settings3 = Settings.builder() + .putList(prefix + "ignore_filters.filter3.realms", Arrays.asList("native", "oidc1")) + .putList(prefix + "ignore_filters.filter3.roles", Arrays.asList("kibana", "/wrong")).build(); + assertThat(LoggingAuditTrail.FILTER_POLICY_IGNORE_REALMS.getConcreteSettingForNamespace("filter3").get(settings3), + containsInAnyOrder("native", "oidc1")); + e = expectThrows(IllegalArgumentException.class, + () -> LoggingAuditTrail.FILTER_POLICY_IGNORE_ROLES.getConcreteSettingForNamespace("filter3").get(settings3)); + assertThat(e, hasToString(containsString("invalid pattern [/wrong]"))); + + Settings settings4 = Settings.builder() + .putList(prefix + "ignore_filters.filter4.roles", Arrays.asList("kibana", "elastic")) + .putList(prefix + "ignore_filters.filter4.indices", Arrays.asList("index-1", "/no-inspiration")).build(); + assertThat(LoggingAuditTrail.FILTER_POLICY_IGNORE_ROLES.getConcreteSettingForNamespace("filter4").get(settings4), + containsInAnyOrder("kibana", "elastic")); + e = expectThrows(IllegalArgumentException.class, + () -> LoggingAuditTrail.FILTER_POLICY_IGNORE_INDICES.getConcreteSettingForNamespace("filter4").get(settings4)); + assertThat(e, hasToString(containsString("invalid pattern [/no-inspiration]"))); + } + public void testAnonymousAccessDeniedTransport() throws Exception { final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);