Skip to content

Commit

Permalink
Support audit ignore policy by index privileges
Browse files Browse the repository at this point in the history
Adding new audit ignore policy - privileges
For example, following policy will filter out all events, which actions
required privilege is either "read" or "delete":

xpack.security.audit.logfile.events.ignore_filters:
  example:
    privileges: ["read", "delete"]

Resolve: elastic#60877
Related: elastic#10836
Related: elastic#37148
  • Loading branch information
BigPandaToo committed Jan 19, 2021
1 parent 665749c commit a918da1
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 16 deletions.
4 changes: 2 additions & 2 deletions docs/reference/settings/audit-settings.asciidoc
Expand Up @@ -153,8 +153,8 @@ not print audit events for users in these realms.
// tag::xpack-sa-lf-events-ignore-privileges-tag[]
`xpack.security.audit.logfile.events.ignore_filters.<policy_name>.privileges`::
(<<dynamic-cluster-setting,Dynamic>>)
A list of index privileges. The specified policy will
not print audit events for actions with these minimal required privileges.
A list of privileges. The specified policy will not print audit events for actions
requiring any of these privileges.
// end::xpack-sa-lf-events-ignore-privileges-tag[]

[[xpack-sa-lf-events-ignore-roles]]
Expand Down
Expand Up @@ -70,6 +70,7 @@
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.support.Automatons;
Expand All @@ -78,6 +79,7 @@
import org.elasticsearch.xpack.security.audit.AuditLevel;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.ApiKeyService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.rest.RemoteHostHeader;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule;
Expand Down Expand Up @@ -1428,20 +1430,28 @@ private static List<String> emptyStringBuildsEmptyAutomaton(List<String> l) {
return l.stream().map(f -> f.isEmpty() ? "//" : f).collect(Collectors.toList());
}

private boolean ignorePrivilegesPredicateTest(String action) {
Collection<String> privileges = null;
if (AuthorizationService.isIndexAction(action)) {
privileges = IndexPrivilege.findPrivilegesThatGrant(action);
} else if (ClusterPrivilegeResolver.isClusterAction(action)) {
privileges = ClusterPrivilegeResolver.findPrivilegesThatGrant(action, null, null);
}
return privileges != null ? privileges.stream().anyMatch((s) -> ignorePrivilegesPredicate.test(s)) : false;
}

/**
* ANDs the predicates of this filter policy. The `indices` and `roles` fields
* of an audit event are multi-valued and all values should match the filter
* predicate of the corresponding field.
*/
Predicate<AuditEventMetaInfo> ignorePredicate() {
return eventInfo -> {
final Collection<String> privileges = IndexPrivilege.findPrivilegesThatGrant(eventInfo.action);
return eventInfo.principal != null && ignorePrincipalsPredicate.test(eventInfo.principal)
return eventInfo -> eventInfo.principal != null && ignorePrincipalsPredicate.test(eventInfo.principal)
&& eventInfo.realm != null && ignoreRealmsPredicate.test(eventInfo.realm)
&& eventInfo.action != null && ignorePrivilegesPredicate.test(privileges.size() > 0 ? privileges.iterator().next() : "")
&& eventInfo.action != null && (ignorePrivilegesPredicate.test(eventInfo.action) ||
ignorePrivilegesPredicateTest(eventInfo.action))
&& eventInfo.roles.get().allMatch(role -> role != null && ignoreRolesPredicate.test(role))
&& eventInfo.indices.get().allMatch(index -> index != null && ignoreIndicesPredicate.test(index));
};
}

@Override
Expand Down
Expand Up @@ -572,7 +572,7 @@ private static IllegalArgumentException illegalArgument(String message) {
return new IllegalArgumentException(message);
}

private static boolean isIndexAction(String action) {
public static boolean isIndexAction(String action) {
return IndexPrivilege.ACTION_MATCHER.test(action);
}

Expand Down
Expand Up @@ -31,12 +31,15 @@
import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.user.SystemUser;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.AuditEventMetaInfo;
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrailTests.MockRequest;
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrailTests.RestContent;
import org.elasticsearch.xpack.security.authc.ApiKeyService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.rest.RemoteHostHeader;
import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
Expand All @@ -57,7 +60,6 @@
import java.util.Random;
import java.util.stream.Collectors;

import static org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege.findPrivilegesThatGrant;
import static org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.PRINCIPAL_ROLES_FIELD_NAME;
import static org.elasticsearch.xpack.security.authc.ApiKeyServiceTests.Utils.createApiKeyAuthentication;
import static org.hamcrest.Matchers.is;
Expand Down Expand Up @@ -126,7 +128,7 @@ public void testPolicyDoesNotMatchNullValuesInEvent() throws Exception {
final List<String> filteredActions = randomNonEmptyListOfFilteredActions();
final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions);
settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.privilegesPolicy.privileges",
filteredPrivileges);
randomBoolean() ? filteredPrivileges : filteredActions);

final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext);

Expand Down Expand Up @@ -217,7 +219,7 @@ public void testSingleCompletePolicyPredicate() throws Exception {
final List<String> filteredActions = randomNonEmptyListOfFilteredActions();
final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions);
settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.privileges",
filteredPrivileges);
randomBoolean() ? filteredPrivileges : filteredActions);

final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext);

Expand Down Expand Up @@ -343,7 +345,7 @@ public void testSingleCompleteWithEmptyFieldPolicyPredicate() throws Exception {
final List<String> filteredActions = randomNonEmptyListOfFilteredActions();
final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions);
settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.privileges",
filteredPrivileges);
randomBoolean() ? filteredPrivileges : filteredActions);

final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext);

Expand Down Expand Up @@ -1937,7 +1939,37 @@ private List<String> randomNonEmptyListOfFilteredActions() {
"indices:admin/refresh*",
"indices:admin/flush*",
"indices:admin/synced_flush",
"indices:admin/forcemerge*"};
"indices:admin/forcemerge*",
"cluster:admin/xpack/security/*",
"cluster:admin/xpack/security/saml/*",
"cluster:admin/xpack/security/oidc/*",
"cluster:admin/xpack/security/token/*",
"cluster:admin/xpack/security/api_key/*",
"cluster:monitor/*",
"cluster:monitor/xpack/ml/*",
"cluster:monitor/text_structure/*",
"cluster:monitor/data_frame/*",
"cluster:monitor/xpack/watcher/*",
"cluster:monitor/xpack/rollup/*",
"cluster:*",
"indices:admin/index_template/*",
"indices:admin/data_stream/*",
"cluster:admin/xpack/ml/*",
"cluster:admin/data_frame/*",
"cluster:monitor/data_frame/*",
"cluster:monitor/transform/*",
"cluster:admin/transform/*",
"cluster:admin/xpack/watcher/*",
"cluster:monitor/nodes/liveness",
"cluster:monitor/state",
"indices:admin/template/*",
"cluster:admin/component_template/*",
"cluster:admin/ingest/pipeline/*",
"cluster:admin/xpack/rollup/*",
"cluster:admin/xpack/ccr/*",
"cluster:admin/ilm/*",
"cluster:admin/slm/*",
"cluster:admin/xpack/enrich/*"};
Random random = random();
for (int i = 0; i < randomIntBetween(1, 4); i++) {
Object name = actionPatterns[random.nextInt(actionPatterns.length)];
Expand All @@ -1947,11 +1979,13 @@ private List<String> randomNonEmptyListOfFilteredActions() {
}

private List<String> randomNonEmptyListOfFilteredPrivileges(List<String> listOfActions) {
final List<String> filtered = new ArrayList<>(listOfActions.size());
final List<String> filtered = new ArrayList<>();
for (int i = 0; i < listOfActions.size(); i++) {
Collection<String> privileges = findPrivilegesThatGrant(listOfActions.get(i));
Collection<String> privileges = AuthorizationService.isIndexAction(listOfActions.get(i)) ?
IndexPrivilege.findPrivilegesThatGrant(listOfActions.get(i)) :
ClusterPrivilegeResolver.findPrivilegesThatGrant(listOfActions.get(i), null, null);
assertNotNull(privileges);
filtered.add(privileges.iterator().next());
filtered.addAll(privileges);
}
return filtered;
}
Expand Down

0 comments on commit a918da1

Please sign in to comment.