Support audit ignore policy by index privileges

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: #60877
Related: #10836
Related: #37148
This commit is contained in:
BigPandaToo 2021-01-19 21:28:56 +01:00
parent 665749c6e9
commit a918da10ff
4 changed files with 60 additions and 16 deletions

View file

@ -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]]

View file

@ -70,6 +70,7 @@ import org.elasticsearch.xpack.core.security.authc.Authentication;
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;
@ -78,6 +79,7 @@ import org.elasticsearch.xpack.security.Security;
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;
@ -1428,20 +1430,28 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
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

View file

@ -572,7 +572,7 @@ public class AuthorizationService {
return new IllegalArgumentException(message);
}
private static boolean isIndexAction(String action) {
public static boolean isIndexAction(String action) {
return IndexPrivilege.ACTION_MATCHER.test(action);
}

View file

@ -31,12 +31,15 @@ import org.elasticsearch.xpack.core.security.authc.Authentication;
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;
@ -57,7 +60,6 @@ import java.util.Optional;
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;
@ -126,7 +128,7 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
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);
@ -217,7 +219,7 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
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);
@ -343,7 +345,7 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
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);
@ -1937,7 +1939,37 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
"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)];
@ -1947,11 +1979,13 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
}
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;
}