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[] // tag::xpack-sa-lf-events-ignore-privileges-tag[]
`xpack.security.audit.logfile.events.ignore_filters.<policy_name>.privileges`:: `xpack.security.audit.logfile.events.ignore_filters.<policy_name>.privileges`::
(<<dynamic-cluster-setting,Dynamic>>) (<<dynamic-cluster-setting,Dynamic>>)
A list of index privileges. The specified policy will A list of privileges. The specified policy will not print audit events for actions
not print audit events for actions with these minimal required privileges. requiring any of these privileges.
// end::xpack-sa-lf-events-ignore-privileges-tag[] // end::xpack-sa-lf-events-ignore-privileges-tag[]
[[xpack-sa-lf-events-ignore-roles]] [[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.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; 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.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.support.Automatons; 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.AuditLevel;
import org.elasticsearch.xpack.security.audit.AuditTrail; import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.ApiKeyService; 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.rest.RemoteHostHeader;
import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; 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()); 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 * 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 * of an audit event are multi-valued and all values should match the filter
* predicate of the corresponding field. * predicate of the corresponding field.
*/ */
Predicate<AuditEventMetaInfo> ignorePredicate() { Predicate<AuditEventMetaInfo> ignorePredicate() {
return eventInfo -> { return eventInfo -> eventInfo.principal != null && ignorePrincipalsPredicate.test(eventInfo.principal)
final Collection<String> privileges = IndexPrivilege.findPrivilegesThatGrant(eventInfo.action);
return eventInfo.principal != null && ignorePrincipalsPredicate.test(eventInfo.principal)
&& eventInfo.realm != null && ignoreRealmsPredicate.test(eventInfo.realm) && 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.roles.get().allMatch(role -> role != null && ignoreRolesPredicate.test(role))
&& eventInfo.indices.get().allMatch(index -> index != null && ignoreIndicesPredicate.test(index)); && eventInfo.indices.get().allMatch(index -> index != null && ignoreIndicesPredicate.test(index));
};
} }
@Override @Override

View file

@ -572,7 +572,7 @@ public class AuthorizationService {
return new IllegalArgumentException(message); return new IllegalArgumentException(message);
} }
private static boolean isIndexAction(String action) { public static boolean isIndexAction(String action) {
return IndexPrivilege.ACTION_MATCHER.test(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.Authentication.RealmRef;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo; 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.SystemUser;
import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.AuditEventMetaInfo; 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.MockRequest;
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrailTests.RestContent; import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrailTests.RestContent;
import org.elasticsearch.xpack.security.authc.ApiKeyService; 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.rest.RemoteHostHeader;
import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry;
import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.elasticsearch.xpack.security.support.SecurityIndexManager;
@ -57,7 +60,6 @@ import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.stream.Collectors; 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.audit.logfile.LoggingAuditTrail.PRINCIPAL_ROLES_FIELD_NAME;
import static org.elasticsearch.xpack.security.authc.ApiKeyServiceTests.Utils.createApiKeyAuthentication; import static org.elasticsearch.xpack.security.authc.ApiKeyServiceTests.Utils.createApiKeyAuthentication;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
@ -126,7 +128,7 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
final List<String> filteredActions = randomNonEmptyListOfFilteredActions(); final List<String> filteredActions = randomNonEmptyListOfFilteredActions();
final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions); final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions);
settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.privilegesPolicy.privileges", 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); 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> filteredActions = randomNonEmptyListOfFilteredActions();
final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions); final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions);
settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.privileges", 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); 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> filteredActions = randomNonEmptyListOfFilteredActions();
final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions); final List<String> filteredPrivileges = randomNonEmptyListOfFilteredPrivileges(filteredActions);
settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.privileges", 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); final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext);
@ -1937,7 +1939,37 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
"indices:admin/refresh*", "indices:admin/refresh*",
"indices:admin/flush*", "indices:admin/flush*",
"indices:admin/synced_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(); Random random = random();
for (int i = 0; i < randomIntBetween(1, 4); i++) { for (int i = 0; i < randomIntBetween(1, 4); i++) {
Object name = actionPatterns[random.nextInt(actionPatterns.length)]; Object name = actionPatterns[random.nextInt(actionPatterns.length)];
@ -1947,11 +1979,13 @@ public class LoggingAuditTrailFilterTests extends ESTestCase {
} }
private List<String> randomNonEmptyListOfFilteredPrivileges(List<String> listOfActions) { 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++) { 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); assertNotNull(privileges);
filtered.add(privileges.iterator().next()); filtered.addAll(privileges);
} }
return filtered; return filtered;
} }