All logging audit settings updateable (elastic/x-pack-elasticsearch#4227)
All logging audit settings are update-able via cluster settings update API (prefix.emit_node_host_address, prefix.emit_node_host_name, prefix.emit_node_name, events.include, events.exclude). Original commit: elastic/x-pack-elasticsearch@96adbd0ae2
This commit is contained in:
parent
00c391602d
commit
53436450c4
|
@ -21,6 +21,7 @@ import org.elasticsearch.common.settings.Setting.Property;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
|
@ -72,11 +73,11 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
|
|||
|
||||
public static final String NAME = "logfile";
|
||||
public static final Setting<Boolean> HOST_ADDRESS_SETTING =
|
||||
Setting.boolSetting(setting("audit.logfile.prefix.emit_node_host_address"), false, Property.NodeScope);
|
||||
Setting.boolSetting(setting("audit.logfile.prefix.emit_node_host_address"), false, Property.NodeScope, Property.Dynamic);
|
||||
public static final Setting<Boolean> HOST_NAME_SETTING =
|
||||
Setting.boolSetting(setting("audit.logfile.prefix.emit_node_host_name"), false, Property.NodeScope);
|
||||
Setting.boolSetting(setting("audit.logfile.prefix.emit_node_host_name"), false, Property.NodeScope, Property.Dynamic);
|
||||
public static final Setting<Boolean> NODE_NAME_SETTING =
|
||||
Setting.boolSetting(setting("audit.logfile.prefix.emit_node_name"), true, Property.NodeScope);
|
||||
Setting.boolSetting(setting("audit.logfile.prefix.emit_node_name"), true, Property.NodeScope, Property.Dynamic);
|
||||
private static final List<String> DEFAULT_EVENT_INCLUDES = Arrays.asList(
|
||||
ACCESS_DENIED.toString(),
|
||||
ACCESS_GRANTED.toString(),
|
||||
|
@ -87,35 +88,37 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
|
|||
RUN_AS_DENIED.toString(),
|
||||
RUN_AS_GRANTED.toString()
|
||||
);
|
||||
private static final Setting<List<String>> INCLUDE_EVENT_SETTINGS =
|
||||
Setting.listSetting(setting("audit.logfile.events.include"), DEFAULT_EVENT_INCLUDES, Function.identity(), Property.NodeScope);
|
||||
private static final Setting<List<String>> EXCLUDE_EVENT_SETTINGS =
|
||||
Setting.listSetting(setting("audit.logfile.events.exclude"), Collections.emptyList(), Function.identity(), Property.NodeScope);
|
||||
private static final Setting<Boolean> INCLUDE_REQUEST_BODY =
|
||||
Setting.boolSetting(setting("audit.logfile.events.emit_request_body"), false, Property.NodeScope);
|
||||
public static final Setting<List<String>> INCLUDE_EVENT_SETTINGS =
|
||||
Setting.listSetting(setting("audit.logfile.events.include"), DEFAULT_EVENT_INCLUDES, Function.identity(), Property.NodeScope,
|
||||
Property.Dynamic);
|
||||
public static final Setting<List<String>> EXCLUDE_EVENT_SETTINGS =
|
||||
Setting.listSetting(setting("audit.logfile.events.exclude"), Collections.emptyList(), Function.identity(), Property.NodeScope,
|
||||
Property.Dynamic);
|
||||
public static final Setting<Boolean> 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<List<String>> FILTER_POLICY_IGNORE_PRINCIPALS =
|
||||
Setting.affixKeySetting(FILTER_POLICY_PREFIX, "users", (key) -> Setting.listSetting(key, Collections.singletonList("*"),
|
||||
Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic));
|
||||
Function.identity(), Property.NodeScope, Property.Dynamic));
|
||||
private static final Setting.AffixSetting<List<String>> FILTER_POLICY_IGNORE_REALMS =
|
||||
Setting.affixKeySetting(FILTER_POLICY_PREFIX, "realms", (key) -> Setting.listSetting(key, Collections.singletonList("*"),
|
||||
Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic));
|
||||
Function.identity(), Property.NodeScope, Property.Dynamic));
|
||||
private static final Setting.AffixSetting<List<String>> FILTER_POLICY_IGNORE_ROLES =
|
||||
Setting.affixKeySetting(FILTER_POLICY_PREFIX, "roles", (key) -> Setting.listSetting(key, Collections.singletonList("*"),
|
||||
Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic));
|
||||
Function.identity(), Property.NodeScope, Property.Dynamic));
|
||||
private static final Setting.AffixSetting<List<String>> FILTER_POLICY_IGNORE_INDICES =
|
||||
Setting.affixKeySetting(FILTER_POLICY_PREFIX, "indices", (key) -> Setting.listSetting(key, Collections.singletonList("*"),
|
||||
Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic));
|
||||
Function.identity(), Property.NodeScope, Property.Dynamic));
|
||||
|
||||
private final Logger logger;
|
||||
private final EnumSet<AuditLevel> events;
|
||||
private final boolean includeRequestBody;
|
||||
// protected for testing
|
||||
final EventFilterPolicyRegistry eventFilterPolicyRegistry;
|
||||
private final ThreadContext threadContext;
|
||||
// protected for testing
|
||||
volatile EnumSet<AuditLevel> events;
|
||||
volatile boolean includeRequestBody;
|
||||
volatile LocalNodeInfo localNodeInfo;
|
||||
|
||||
@Override
|
||||
|
@ -136,6 +139,17 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
|
|||
this.localNodeInfo = new LocalNodeInfo(settings, null);
|
||||
this.eventFilterPolicyRegistry = new EventFilterPolicyRegistry(settings);
|
||||
clusterService.addListener(this);
|
||||
clusterService.getClusterSettings().addSettingsUpdateConsumer(newSettings -> {
|
||||
this.includeRequestBody = INCLUDE_REQUEST_BODY.get(newSettings);
|
||||
}, Arrays.asList(INCLUDE_REQUEST_BODY));
|
||||
clusterService.getClusterSettings().addSettingsUpdateConsumer(newSettings -> {
|
||||
this.events = parse(INCLUDE_EVENT_SETTINGS.get(newSettings), EXCLUDE_EVENT_SETTINGS.get(newSettings));
|
||||
}, Arrays.asList(INCLUDE_EVENT_SETTINGS, EXCLUDE_EVENT_SETTINGS));
|
||||
clusterService.getClusterSettings().addSettingsUpdateConsumer(newSettings -> {
|
||||
final LocalNodeInfo localNodeInfo = this.localNodeInfo;
|
||||
final Settings.Builder builder = Settings.builder().put(localNodeInfo.settings).put(newSettings, false);
|
||||
this.localNodeInfo = new LocalNodeInfo(builder.build(), localNodeInfo.localNode);
|
||||
}, Arrays.asList(HOST_ADDRESS_SETTING, HOST_NAME_SETTING, NODE_NAME_SETTING));
|
||||
clusterService.getClusterSettings().addAffixUpdateConsumer(FILTER_POLICY_IGNORE_PRINCIPALS, (policyName, filtersList) -> {
|
||||
final Optional<EventFilterPolicy> policy = eventFilterPolicyRegistry.get(policyName);
|
||||
final EventFilterPolicy newPolicy = policy.orElse(new EventFilterPolicy(policyName, settings))
|
||||
|
@ -788,19 +802,21 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
|
|||
|
||||
void updateLocalNodeInfo(DiscoveryNode newLocalNode) {
|
||||
// check if local node changed
|
||||
final DiscoveryNode localNode = localNodeInfo.localNode;
|
||||
if ((localNode == null) || (localNode.equals(newLocalNode) == false)) {
|
||||
final LocalNodeInfo localNodeInfo = this.localNodeInfo;
|
||||
if ((localNodeInfo.localNode == null) || (localNodeInfo.localNode.equals(newLocalNode) == false)) {
|
||||
// no need to synchronize, called only from the cluster state applier thread
|
||||
localNodeInfo = new LocalNodeInfo(settings, newLocalNode);
|
||||
this.localNodeInfo = new LocalNodeInfo(localNodeInfo.settings, newLocalNode);
|
||||
}
|
||||
}
|
||||
|
||||
static class LocalNodeInfo {
|
||||
private final Settings settings;
|
||||
private final DiscoveryNode localNode;
|
||||
private final String prefix;
|
||||
final String prefix;
|
||||
private final String localOriginTag;
|
||||
|
||||
LocalNodeInfo(Settings settings, @Nullable DiscoveryNode newLocalNode) {
|
||||
this.settings = settings;
|
||||
this.localNode = newLocalNode;
|
||||
this.prefix = resolvePrefix(settings, newLocalNode);
|
||||
this.localOriginTag = localOriginTag(newLocalNode);
|
||||
|
@ -821,7 +837,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
|
|||
}
|
||||
}
|
||||
if (NODE_NAME_SETTING.get(settings)) {
|
||||
final String name = settings.get("name");
|
||||
final String name = Node.NODE_NAME_SETTING.get(settings);
|
||||
if (name != null) {
|
||||
builder.append("[").append(name).append("] ");
|
||||
}
|
||||
|
|
|
@ -12,13 +12,17 @@ import org.elasticsearch.common.settings.Setting;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.xpack.security.audit.AuditLevel;
|
||||
import org.elasticsearch.xpack.security.audit.AuditTrailService;
|
||||
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
@ -26,7 +30,7 @@ import static org.mockito.Mockito.mock;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 1)
|
||||
public class AuditTrailFilteringUpdateTests extends SecurityIntegTestCase {
|
||||
public class AuditTrailSettingsUpdateTests extends SecurityIntegTestCase {
|
||||
|
||||
private static Settings startupFilterSettings;
|
||||
private static Settings updateFilterSettings;
|
||||
|
@ -64,7 +68,7 @@ public class AuditTrailFilteringUpdateTests extends SecurityIntegTestCase {
|
|||
return settingsBuilder.build();
|
||||
}
|
||||
|
||||
public void testDynamicSettings() throws Exception {
|
||||
public void testDynamicFilterSettings() throws Exception {
|
||||
final ClusterService clusterService = mock(ClusterService.class);
|
||||
final ClusterSettings clusterSettings = mockClusterSettings();
|
||||
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
|
||||
|
@ -76,7 +80,7 @@ public class AuditTrailFilteringUpdateTests extends SecurityIntegTestCase {
|
|||
final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext);
|
||||
final String expected = auditTrail.eventFilterPolicyRegistry.toString();
|
||||
// update settings on internal cluster
|
||||
updateSettings(updateFilterSettings);
|
||||
updateSettings(updateFilterSettings, randomBoolean());
|
||||
final String actual = ((LoggingAuditTrail) internalCluster().getInstances(AuditTrailService.class)
|
||||
.iterator()
|
||||
.next()
|
||||
|
@ -86,7 +90,7 @@ public class AuditTrailFilteringUpdateTests extends SecurityIntegTestCase {
|
|||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testInvalidSettings() throws Exception {
|
||||
public void testInvalidFilterSettings() throws Exception {
|
||||
final String invalidLuceneRegex = "/invalid";
|
||||
final Settings.Builder settingsBuilder = Settings.builder();
|
||||
final String[] allSettingsKeys = new String[] { "xpack.security.audit.logfile.events.ignore_filters.invalid.users",
|
||||
|
@ -99,8 +103,72 @@ public class AuditTrailFilteringUpdateTests extends SecurityIntegTestCase {
|
|||
assertThat(e.getMessage(), containsString("illegal value can't update"));
|
||||
}
|
||||
|
||||
private void updateSettings(Settings settings) {
|
||||
if (randomBoolean()) {
|
||||
public void testDynamicHostSettings() {
|
||||
final boolean persistent = randomBoolean();
|
||||
final Settings.Builder settingsBuilder = Settings.builder();
|
||||
settingsBuilder.put(LoggingAuditTrail.HOST_ADDRESS_SETTING.getKey(), true);
|
||||
settingsBuilder.put(LoggingAuditTrail.HOST_NAME_SETTING.getKey(), true);
|
||||
settingsBuilder.put(LoggingAuditTrail.NODE_NAME_SETTING.getKey(), true);
|
||||
updateSettings(settingsBuilder.build(), persistent);
|
||||
final LoggingAuditTrail loggingAuditTrail = ((LoggingAuditTrail) internalCluster().getInstances(AuditTrailService.class)
|
||||
.iterator()
|
||||
.next()
|
||||
.getAuditTrails()
|
||||
.iterator()
|
||||
.next());
|
||||
assertTrue(Pattern.matches("\\[127\\.0\\.0\\.1\\] \\[127\\.0\\.0\\.1\\] \\[node_.*\\] ", loggingAuditTrail.localNodeInfo.prefix));
|
||||
settingsBuilder.put(LoggingAuditTrail.HOST_ADDRESS_SETTING.getKey(), false);
|
||||
updateSettings(settingsBuilder.build(), persistent);
|
||||
assertTrue(Pattern.matches("\\[127\\.0\\.0\\.1\\] \\[node_.*\\] ", loggingAuditTrail.localNodeInfo.prefix));
|
||||
settingsBuilder.put(LoggingAuditTrail.HOST_ADDRESS_SETTING.getKey(), true);
|
||||
settingsBuilder.put(LoggingAuditTrail.HOST_NAME_SETTING.getKey(), false);
|
||||
updateSettings(settingsBuilder.build(), persistent);
|
||||
assertTrue(Pattern.matches("\\[127\\.0\\.0\\.1\\] \\[node_.*\\] ", loggingAuditTrail.localNodeInfo.prefix));
|
||||
settingsBuilder.put(LoggingAuditTrail.HOST_NAME_SETTING.getKey(), true);
|
||||
settingsBuilder.put(LoggingAuditTrail.NODE_NAME_SETTING.getKey(), false);
|
||||
updateSettings(settingsBuilder.build(), persistent);
|
||||
assertTrue(Pattern.matches("\\[127\\.0\\.0\\.1\\] \\[127\\.0\\.0\\.1\\] ", loggingAuditTrail.localNodeInfo.prefix));
|
||||
}
|
||||
|
||||
public void testDynamicRequestBodySettings() {
|
||||
final boolean persistent = randomBoolean();
|
||||
final boolean enableRequestBody = randomBoolean();
|
||||
final Settings.Builder settingsBuilder = Settings.builder();
|
||||
settingsBuilder.put(LoggingAuditTrail.INCLUDE_REQUEST_BODY.getKey(), enableRequestBody);
|
||||
updateSettings(settingsBuilder.build(), persistent);
|
||||
final LoggingAuditTrail loggingAuditTrail = ((LoggingAuditTrail) internalCluster().getInstances(AuditTrailService.class)
|
||||
.iterator()
|
||||
.next()
|
||||
.getAuditTrails()
|
||||
.iterator()
|
||||
.next());
|
||||
assertEquals(enableRequestBody, loggingAuditTrail.includeRequestBody);
|
||||
settingsBuilder.put(LoggingAuditTrail.INCLUDE_REQUEST_BODY.getKey(), !enableRequestBody);
|
||||
updateSettings(settingsBuilder.build(), persistent);
|
||||
assertEquals(!enableRequestBody, loggingAuditTrail.includeRequestBody);
|
||||
}
|
||||
|
||||
public void testDynamicEventsSettings() {
|
||||
final List<String> allEventTypes = Arrays.asList("anonymous_access_denied", "authentication_failed", "realm_authentication_failed",
|
||||
"access_granted", "access_denied", "tampered_request", "connection_granted", "connection_denied", "system_access_granted",
|
||||
"authentication_success", "run_as_granted", "run_as_denied");
|
||||
final List<String> includedEvents = randomSubsetOf(allEventTypes);
|
||||
final List<String> excludedEvents = randomSubsetOf(allEventTypes);
|
||||
final Settings.Builder settingsBuilder = Settings.builder();
|
||||
settingsBuilder.putList(LoggingAuditTrail.INCLUDE_EVENT_SETTINGS.getKey(), includedEvents);
|
||||
settingsBuilder.putList(LoggingAuditTrail.EXCLUDE_EVENT_SETTINGS.getKey(), excludedEvents);
|
||||
updateSettings(settingsBuilder.build(), randomBoolean());
|
||||
final LoggingAuditTrail loggingAuditTrail = ((LoggingAuditTrail) internalCluster().getInstances(AuditTrailService.class)
|
||||
.iterator()
|
||||
.next()
|
||||
.getAuditTrails()
|
||||
.iterator()
|
||||
.next());
|
||||
assertEquals(AuditLevel.parse(includedEvents, excludedEvents), loggingAuditTrail.events);
|
||||
}
|
||||
|
||||
private void updateSettings(Settings settings, boolean persistent) {
|
||||
if (persistent) {
|
||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(settings));
|
||||
} else {
|
||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
|
|
@ -552,7 +552,7 @@ public abstract class SqlSecurityTestCase extends ESRestTestCase {
|
|||
List<Map<String, Object>> logs = new ArrayList<>();
|
||||
String line;
|
||||
Pattern logPattern = Pattern.compile(
|
||||
("PART PART PART origin_type=PART, origin_address=PART, principal=PART, realm=PART, "
|
||||
("PART PART PART PART origin_type=PART, origin_address=PART, principal=PART, realm=PART, "
|
||||
+ "(?:run_as_principal=IGN, )?(?:run_as_realm=IGN, )?(?:run_by_principal=PART, )?(?:run_by_realm=PART, )?"
|
||||
+ "roles=PART, action=\\[(.*?)\\], (?:indices=PART, )?request=PART")
|
||||
.replace(" ", "\\s+").replace("PART", "\\[([^\\]]*)\\]").replace("IGN", "\\[[^\\]]*\\]"));
|
||||
|
@ -567,6 +567,7 @@ public abstract class SqlSecurityTestCase extends ESRestTestCase {
|
|||
/* We *could* parse the date but leaving it in the original format makes it
|
||||
* easier to find the lines in the file that this log comes from. */
|
||||
log.put("time", m.group(i++));
|
||||
log.put("node", m.group(i++));
|
||||
log.put("origin", m.group(i++));
|
||||
String eventType = m.group(i++);
|
||||
if (false == ("access_denied".equals(eventType) || "access_granted".equals(eventType))) {
|
||||
|
|
Loading…
Reference in New Issue