This commit introduces audit event filtering policies for the logging audit.
Until now the only way to temper the gush of audit events was to specifically
pick some event types that were reported.
This superposes a way (named policies) to filter events using lucene regexp filters on 
the following event fields: users, realms, roles and indices. The policies are ignore
policies, ie when an event matches it is ignored (not reported).

Original commit: elastic/x-pack-elasticsearch@233f685121
This commit is contained in:
Albert Zaharovits 2018-01-11 11:07:46 +02:00 committed by GitHub
parent e74f90eba0
commit 3fc17ab918
3 changed files with 2065 additions and 255 deletions

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.security.audit.logfile; package org.elasticsearch.xpack.security.audit.logfile;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
@ -26,6 +27,7 @@ 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.AuthenticationToken; import org.elasticsearch.xpack.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.security.rest.RemoteHostHeader; import org.elasticsearch.xpack.security.rest.RemoteHostHeader;
import org.elasticsearch.xpack.security.support.Automatons;
import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule;
import org.elasticsearch.xpack.security.user.SystemUser; import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User; import org.elasticsearch.xpack.security.user.User;
@ -37,12 +39,16 @@ import java.net.SocketAddress;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.elasticsearch.common.Strings.collectionToCommaDelimitedString;
import static org.elasticsearch.common.Strings.arrayToCommaDelimitedString; import static org.elasticsearch.common.Strings.arrayToCommaDelimitedString;
import static org.elasticsearch.xpack.security.SecurityField.setting; import static org.elasticsearch.xpack.security.SecurityField.setting;
import static org.elasticsearch.xpack.security.audit.AuditLevel.ACCESS_DENIED; import static org.elasticsearch.xpack.security.audit.AuditLevel.ACCESS_DENIED;
@ -58,7 +64,6 @@ import static org.elasticsearch.xpack.security.audit.AuditLevel.SYSTEM_ACCESS_GR
import static org.elasticsearch.xpack.security.audit.AuditLevel.TAMPERED_REQUEST; import static org.elasticsearch.xpack.security.audit.AuditLevel.TAMPERED_REQUEST;
import static org.elasticsearch.xpack.security.audit.AuditLevel.AUTHENTICATION_SUCCESS; import static org.elasticsearch.xpack.security.audit.AuditLevel.AUTHENTICATION_SUCCESS;
import static org.elasticsearch.xpack.security.audit.AuditLevel.parse; import static org.elasticsearch.xpack.security.audit.AuditLevel.parse;
import static org.elasticsearch.xpack.security.audit.AuditUtil.indices;
import static org.elasticsearch.xpack.security.audit.AuditUtil.restRequestContent; import static org.elasticsearch.xpack.security.audit.AuditUtil.restRequestContent;
public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, ClusterStateListener { public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, ClusterStateListener {
@ -86,10 +91,28 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
Setting.listSetting(setting("audit.logfile.events.exclude"), Collections.emptyList(), Function.identity(), Property.NodeScope); Setting.listSetting(setting("audit.logfile.events.exclude"), Collections.emptyList(), Function.identity(), Property.NodeScope);
private static final Setting<Boolean> INCLUDE_REQUEST_BODY = private static final Setting<Boolean> INCLUDE_REQUEST_BODY =
Setting.boolSetting(setting("audit.logfile.events.emit_request_body"), false, Property.NodeScope); Setting.boolSetting(setting("audit.logfile.events.emit_request_body"), false, Property.NodeScope);
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));
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));
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));
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));
private final Logger logger; private final Logger logger;
private final EnumSet<AuditLevel> events; private final EnumSet<AuditLevel> events;
private final boolean includeRequestBody; private final boolean includeRequestBody;
// protected for testing
protected final Predicate<AuditEventMetaInfo> filterPolicyPredicate;
private final ThreadContext threadContext; private final ThreadContext threadContext;
volatile LocalNodeInfo localNodeInfo; volatile LocalNodeInfo localNodeInfo;
@ -109,12 +132,14 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
this.includeRequestBody = INCLUDE_REQUEST_BODY.get(settings); this.includeRequestBody = INCLUDE_REQUEST_BODY.get(settings);
this.threadContext = threadContext; this.threadContext = threadContext;
this.localNodeInfo = new LocalNodeInfo(settings, null); this.localNodeInfo = new LocalNodeInfo(settings, null);
this.filterPolicyPredicate = new EventFilterPolicyRegistry(settings).ignorePredicate();
clusterService.addListener(this); clusterService.addListener(this);
} }
@Override @Override
public void authenticationSuccess(String realm, User user, RestRequest request) { public void authenticationSuccess(String realm, User user, RestRequest request) {
if (events.contains(AUTHENTICATION_SUCCESS)) { if (events.contains(AUTHENTICATION_SUCCESS) && filterPolicyPredicate
.test(new AuditEventMetaInfo(Optional.of(user), Optional.of(realm), Optional.empty(), Optional.empty())) == false) {
if (includeRequestBody) { if (includeRequestBody) {
logger.info("{}[rest] [authentication_success]\t{}, realm=[{}], uri=[{}], params=[{}], request_body=[{}]", logger.info("{}[rest] [authentication_success]\t{}, realm=[{}], uri=[{}], params=[{}], request_body=[{}]",
localNodeInfo.prefix, principal(user), realm, request.uri(), request.params(), restRequestContent(request)); localNodeInfo.prefix, principal(user), realm, request.uri(), request.params(), restRequestContent(request));
@ -128,32 +153,44 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void authenticationSuccess(String realm, User user, String action, TransportMessage message) { public void authenticationSuccess(String realm, User user, String action, TransportMessage message) {
if (events.contains(AUTHENTICATION_SUCCESS)) { if (events.contains(AUTHENTICATION_SUCCESS)) {
final LocalNodeInfo localNodeInfo = this.localNodeInfo; final Optional<String[]> indices = indices(message);
logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], request=[{}]", if (filterPolicyPredicate
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), realm, action, .test(new AuditEventMetaInfo(Optional.of(user), Optional.of(realm), Optional.empty(), indices)) == false) {
message.getClass().getSimpleName()); final LocalNodeInfo localNodeInfo = this.localNodeInfo;
if (indices.isPresent()) {
logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], indices=[{}], request=[{}]",
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), realm, action,
arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName());
} else {
logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], request=[{}]",
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), realm, action,
message.getClass().getSimpleName());
}
}
} }
} }
@Override @Override
public void anonymousAccessDenied(String action, TransportMessage message) { public void anonymousAccessDenied(String action, TransportMessage message) {
if (events.contains(ANONYMOUS_ACCESS_DENIED)) { if (events.contains(ANONYMOUS_ACCESS_DENIED)) {
String indices = indicesString(message); final Optional<String[]> indices = indices(message);
final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) {
if (indices != null) { final LocalNodeInfo localNodeInfo = this.localNodeInfo;
logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], indices=[{}], request=[{}]", if (indices.isPresent()) {
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), action, indices, logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], indices=[{}], request=[{}]",
message.getClass().getSimpleName()); localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), action,
} else { arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName());
logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, } else {
originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix,
originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName());
}
} }
} }
} }
@Override @Override
public void anonymousAccessDenied(RestRequest request) { public void anonymousAccessDenied(RestRequest request) {
if (events.contains(ANONYMOUS_ACCESS_DENIED)) { if (events.contains(ANONYMOUS_ACCESS_DENIED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) {
if (includeRequestBody) { if (includeRequestBody) {
logger.info("{}[rest] [anonymous_access_denied]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, logger.info("{}[rest] [anonymous_access_denied]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix,
hostAttributes(request), request.uri(), restRequestContent(request)); hostAttributes(request), request.uri(), restRequestContent(request));
@ -167,30 +204,30 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void authenticationFailed(AuthenticationToken token, String action, TransportMessage message) { public void authenticationFailed(AuthenticationToken token, String action, TransportMessage message) {
if (events.contains(AUTHENTICATION_FAILED)) { if (events.contains(AUTHENTICATION_FAILED)) {
String indices = indicesString(message); final Optional<String[]> indices = indices(message);
final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.empty(), indices)) == false) {
if (indices != null) { final LocalNodeInfo localNodeInfo = this.localNodeInfo;
logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], indices=[{}], request=[{}]", if (indices.isPresent()) {
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, indices, logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], indices=[{}], request=[{}]",
message.getClass().getSimpleName()); localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action,
} else { arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName());
logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], request=[{}]", } else {
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], request=[{}]",
message.getClass().getSimpleName()); localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action,
message.getClass().getSimpleName());
}
} }
} }
} }
@Override @Override
public void authenticationFailed(RestRequest request) { public void authenticationFailed(RestRequest request) {
if (events.contains(AUTHENTICATION_FAILED)) { if (events.contains(AUTHENTICATION_FAILED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) {
if (includeRequestBody) { if (includeRequestBody) {
logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix,
hostAttributes(request), request.uri(), restRequestContent(request)); hostAttributes(request), request.uri(), restRequestContent(request));
} else { } else {
logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}]", localNodeInfo.prefix, hostAttributes(request), logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri());
request.uri());
} }
} }
} }
@ -198,27 +235,31 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void authenticationFailed(String action, TransportMessage message) { public void authenticationFailed(String action, TransportMessage message) {
if (events.contains(AUTHENTICATION_FAILED)) { if (events.contains(AUTHENTICATION_FAILED)) {
String indices = indicesString(message); final Optional<String[]> indices = indices(message);
final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) {
if (indices != null) { final LocalNodeInfo localNodeInfo = this.localNodeInfo;
logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, if (indices.isPresent()) {
originAttributes(threadContext, message, localNodeInfo), action, indices, message.getClass().getSimpleName()); logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix,
} else { originAttributes(threadContext, message, localNodeInfo), action, arrayToCommaDelimitedString(indices.get()),
logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, message.getClass().getSimpleName());
originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); } else {
logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix,
originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName());
}
} }
} }
} }
@Override @Override
public void authenticationFailed(AuthenticationToken token, RestRequest request) { public void authenticationFailed(AuthenticationToken token, RestRequest request) {
if (events.contains(AUTHENTICATION_FAILED)) { if (events.contains(AUTHENTICATION_FAILED)
&& filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.empty(), Optional.empty())) == false) {
if (includeRequestBody) { if (includeRequestBody) {
logger.info("{}[rest] [authentication_failed]\t{}, principal=[{}], uri=[{}], request_body=[{}]", localNodeInfo.prefix, logger.info("{}[rest] [authentication_failed]\t{}, principal=[{}], uri=[{}], request_body=[{}]", localNodeInfo.prefix,
hostAttributes(request), token.principal(), request.uri(), restRequestContent(request)); hostAttributes(request), token.principal(), request.uri(), restRequestContent(request));
} else { } else {
logger.info("{}[rest] [authentication_failed]\t{}, principal=[{}], uri=[{}]", localNodeInfo.prefix, logger.info("{}[rest] [authentication_failed]\t{}, principal=[{}], uri=[{}]", localNodeInfo.prefix, hostAttributes(request),
hostAttributes(request), token.principal(), request.uri()); token.principal(), request.uri());
} }
} }
} }
@ -226,30 +267,35 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message) { public void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message) {
if (events.contains(REALM_AUTHENTICATION_FAILED)) { if (events.contains(REALM_AUTHENTICATION_FAILED)) {
String indices = indicesString(message); final Optional<String[]> indices = indices(message);
final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.of(realm), indices)) == false) {
if (indices != null) { final LocalNodeInfo localNodeInfo = this.localNodeInfo;
logger.info("{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], indices=[{}], " + if (indices.isPresent()) {
"request=[{}]", localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), logger.info(
token.principal(), action, indices, message.getClass().getSimpleName()); "{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], indices=[{}], "
} else { + "request=[{}]",
logger.info("{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], request=[{}]", localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), token.principal(), action,
localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName());
message.getClass().getSimpleName()); } else {
logger.info("{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], request=[{}]",
localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), token.principal(), action,
message.getClass().getSimpleName());
}
} }
} }
} }
@Override @Override
public void authenticationFailed(String realm, AuthenticationToken token, RestRequest request) { public void authenticationFailed(String realm, AuthenticationToken token, RestRequest request) {
if (events.contains(REALM_AUTHENTICATION_FAILED)) { if (events.contains(REALM_AUTHENTICATION_FAILED)
&& filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.of(realm), Optional.empty())) == false) {
if (includeRequestBody) { if (includeRequestBody) {
logger.info("{}[rest] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], uri=[{}], request_body=[{}]", logger.info("{}[rest] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], uri=[{}], request_body=[{}]",
localNodeInfo.prefix, realm, hostAttributes(request), token.principal(), request.uri(), localNodeInfo.prefix, realm, hostAttributes(request), token.principal(), request.uri(),
restRequestContent(request)); restRequestContent(request));
} else { } else {
logger.info("{}[rest] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], uri=[{}]", localNodeInfo.prefix, logger.info("{}[rest] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], uri=[{}]", localNodeInfo.prefix, realm,
realm, hostAttributes(request), token.principal(), request.uri()); hostAttributes(request), token.principal(), request.uri());
} }
} }
} }
@ -257,19 +303,21 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void accessGranted(User user, String action, TransportMessage message, String[] roleNames) { public void accessGranted(User user, String action, TransportMessage message, String[] roleNames) {
final boolean isSystem = SystemUser.is(user) || XPackUser.is(user); final boolean isSystem = SystemUser.is(user) || XPackUser.is(user);
final boolean logSystemAccessGranted = isSystem && events.contains(SYSTEM_ACCESS_GRANTED); if ((isSystem && events.contains(SYSTEM_ACCESS_GRANTED)) || (isSystem == false && events.contains(ACCESS_GRANTED))) {
final boolean shouldLog = logSystemAccessGranted || (isSystem == false && events.contains(ACCESS_GRANTED)); final Optional<String[]> indices = indices(message);
if (shouldLog) { if (filterPolicyPredicate
String indices = indicesString(message); .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) {
final LocalNodeInfo localNodeInfo = this.localNodeInfo; final LocalNodeInfo localNodeInfo = this.localNodeInfo;
if (indices != null) { if (indices.isPresent()) {
logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]", logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]",
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user),
arrayToCommaDelimitedString(roleNames), action, indices, message.getClass().getSimpleName()); arrayToCommaDelimitedString(roleNames), action, arrayToCommaDelimitedString(indices.get()),
} else { message.getClass().getSimpleName());
logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix, } else {
originAttributes(threadContext, message, localNodeInfo), principal(user), arrayToCommaDelimitedString(roleNames), logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix,
action, message.getClass().getSimpleName()); originAttributes(threadContext, message, localNodeInfo), principal(user),
arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName());
}
} }
} }
} }
@ -277,26 +325,30 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void accessDenied(User user, String action, TransportMessage message, String[] roleNames) { public void accessDenied(User user, String action, TransportMessage message, String[] roleNames) {
if (events.contains(ACCESS_DENIED)) { if (events.contains(ACCESS_DENIED)) {
String indices = indicesString(message); final Optional<String[]> indices = indices(message);
final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (filterPolicyPredicate
if (indices != null) { .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) {
logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]", final LocalNodeInfo localNodeInfo = this.localNodeInfo;
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), if (indices.isPresent()) {
arrayToCommaDelimitedString(roleNames), action, indices, message.getClass().getSimpleName()); logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]",
} else { localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user),
logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix, arrayToCommaDelimitedString(roleNames), action, arrayToCommaDelimitedString(indices.get()),
originAttributes(threadContext, message, localNodeInfo), principal(user), arrayToCommaDelimitedString(roleNames), message.getClass().getSimpleName());
action, message.getClass().getSimpleName()); } else {
logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix,
originAttributes(threadContext, message, localNodeInfo), principal(user),
arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName());
}
} }
} }
} }
@Override @Override
public void tamperedRequest(RestRequest request) { public void tamperedRequest(RestRequest request) {
if (events.contains(TAMPERED_REQUEST)) { if (events.contains(TAMPERED_REQUEST) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) {
if (includeRequestBody) { if (includeRequestBody) {
logger.info("{}[rest] [tampered_request]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, logger.info("{}[rest] [tampered_request]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request),
hostAttributes(request), request.uri(), restRequestContent(request)); request.uri(), restRequestContent(request));
} else { } else {
logger.info("{}[rest] [tampered_request]\t{}, uri=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri()); logger.info("{}[rest] [tampered_request]\t{}, uri=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri());
} }
@ -306,14 +358,17 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void tamperedRequest(String action, TransportMessage message) { public void tamperedRequest(String action, TransportMessage message) {
if (events.contains(TAMPERED_REQUEST)) { if (events.contains(TAMPERED_REQUEST)) {
String indices = indicesString(message); final Optional<String[]> indices = indices(message);
final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) {
if (indices != null) { final LocalNodeInfo localNodeInfo = this.localNodeInfo;
logger.info("{}[transport] [tampered_request]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, if (indices.isPresent()) {
originAttributes(threadContext, message, localNodeInfo), action, indices, message.getClass().getSimpleName()); logger.info("{}[transport] [tampered_request]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix,
} else { originAttributes(threadContext, message, localNodeInfo), action, arrayToCommaDelimitedString(indices.get()),
logger.info("{}[transport] [tampered_request]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, message.getClass().getSimpleName());
originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); } else {
logger.info("{}[transport] [tampered_request]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix,
originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName());
}
} }
} }
} }
@ -321,59 +376,95 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
@Override @Override
public void tamperedRequest(User user, String action, TransportMessage request) { public void tamperedRequest(User user, String action, TransportMessage request) {
if (events.contains(TAMPERED_REQUEST)) { if (events.contains(TAMPERED_REQUEST)) {
String indices = indicesString(request); final Optional<String[]> indices = indices(request);
final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (filterPolicyPredicate
if (indices != null) { .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.empty(), indices)) == false) {
logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, final LocalNodeInfo localNodeInfo = this.localNodeInfo;
originAttributes(threadContext, request, localNodeInfo), principal(user), action, indices, if (indices.isPresent()) {
request.getClass().getSimpleName()); logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix,
} else { originAttributes(threadContext, request, localNodeInfo), principal(user), action,
logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], request=[{}]", localNodeInfo.prefix, arrayToCommaDelimitedString(indices.get()), request.getClass().getSimpleName());
originAttributes(threadContext, request, localNodeInfo), principal(user), action, } else {
request.getClass().getSimpleName()); logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], request=[{}]", localNodeInfo.prefix,
originAttributes(threadContext, request, localNodeInfo), principal(user), action,
request.getClass().getSimpleName());
}
} }
} }
} }
@Override @Override
public void connectionGranted(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) { public void connectionGranted(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) {
if (events.contains(CONNECTION_GRANTED)) { if (events.contains(CONNECTION_GRANTED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) {
logger.info("{}[ip_filter] [connection_granted]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", logger.info("{}[ip_filter] [connection_granted]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", localNodeInfo.prefix,
localNodeInfo.prefix, NetworkAddress.format(inetAddress), profile, rule); NetworkAddress.format(inetAddress), profile, rule);
} }
} }
@Override @Override
public void connectionDenied(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) { public void connectionDenied(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) {
if (events.contains(CONNECTION_DENIED)) { if (events.contains(CONNECTION_DENIED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) {
logger.info("{}[ip_filter] [connection_denied]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", logger.info("{}[ip_filter] [connection_denied]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", localNodeInfo.prefix,
localNodeInfo.prefix, NetworkAddress.format(inetAddress), profile, rule); NetworkAddress.format(inetAddress), profile, rule);
} }
} }
@Override @Override
public void runAsGranted(User user, String action, TransportMessage message, String[] roleNames) { public void runAsGranted(User user, String action, TransportMessage message, String[] roleNames) {
if (events.contains(RUN_AS_GRANTED)) { if (events.contains(RUN_AS_GRANTED)) {
final LocalNodeInfo localNodeInfo = this.localNodeInfo; final Optional<String[]> indices = indices(message);
logger.info("{}[transport] [run_as_granted]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}], action=[{}], request=[{}]", if (filterPolicyPredicate
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), user.authenticatedUser().principal(), .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) {
user.principal(), arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName()); final LocalNodeInfo localNodeInfo = this.localNodeInfo;
if (indices.isPresent()) {
logger.info(
"{}[transport] [run_as_granted]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}],"
+ " action=[{}], indices=[{}], request=[{}]",
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo),
user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action,
arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName());
} else {
logger.info(
"{}[transport] [run_as_granted]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}],"
+ " action=[{}], request=[{}]",
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo),
user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action,
message.getClass().getSimpleName());
}
}
} }
} }
@Override @Override
public void runAsDenied(User user, String action, TransportMessage message, String[] roleNames) { public void runAsDenied(User user, String action, TransportMessage message, String[] roleNames) {
if (events.contains(RUN_AS_DENIED)) { if (events.contains(RUN_AS_DENIED)) {
final LocalNodeInfo localNodeInfo = this.localNodeInfo; final Optional<String[]> indices = indices(message);
logger.info("{}[transport] [run_as_denied]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}], action=[{}], request=[{}]", if (filterPolicyPredicate
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), user.authenticatedUser().principal(), .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) {
user.principal(), arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName()); final LocalNodeInfo localNodeInfo = this.localNodeInfo;
if (indices.isPresent()) {
logger.info(
"{}[transport] [run_as_denied]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}],"
+ " action=[{}], indices=[{}], request=[{}]",
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo),
user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action,
arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName());
} else {
logger.info(
"{}[transport] [run_as_denied]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}],"
+ " action=[{}], request=[{}]",
localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo),
user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action,
message.getClass().getSimpleName());
}
}
} }
} }
@Override @Override
public void runAsDenied(User user, RestRequest request, String[] roleNames) { public void runAsDenied(User user, RestRequest request, String[] roleNames) {
if (events.contains(RUN_AS_DENIED)) { if (events.contains(RUN_AS_DENIED) && filterPolicyPredicate
.test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), Optional.empty())) == false) {
if (includeRequestBody) { if (includeRequestBody) {
logger.info("{}[rest] [run_as_denied]\t{}, principal=[{}], roles=[{}], uri=[{}], request_body=[{}]", localNodeInfo.prefix, logger.info("{}[rest] [run_as_denied]\t{}, principal=[{}], roles=[{}], uri=[{}], request_body=[{}]", localNodeInfo.prefix,
hostAttributes(request), user.principal(), arrayToCommaDelimitedString(roleNames), request.uri(), hostAttributes(request), user.principal(), arrayToCommaDelimitedString(roleNames), request.uri(),
@ -387,7 +478,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
private static String hostAttributes(RestRequest request) { private static String hostAttributes(RestRequest request) {
String formattedAddress; String formattedAddress;
SocketAddress socketAddress = request.getRemoteAddress(); final SocketAddress socketAddress = request.getRemoteAddress();
if (socketAddress instanceof InetSocketAddress) { if (socketAddress instanceof InetSocketAddress) {
formattedAddress = NetworkAddress.format(((InetSocketAddress) socketAddress).getAddress()); formattedAddress = NetworkAddress.format(((InetSocketAddress) socketAddress).getAddress());
} else { } else {
@ -401,7 +492,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
} }
private static Optional<String> restOriginTag(ThreadContext threadContext) { private static Optional<String> restOriginTag(ThreadContext threadContext) {
InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext); final InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext);
if (restAddress == null) { if (restAddress == null) {
return Optional.empty(); return Optional.empty();
} }
@ -411,7 +502,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
} }
private static Optional<String> transportOriginTag(TransportMessage message) { private static Optional<String> transportOriginTag(TransportMessage message) {
TransportAddress address = message.remoteAddress(); final TransportAddress address = message.remoteAddress();
if (address == null) { if (address == null) {
return Optional.empty(); return Optional.empty();
} }
@ -421,13 +512,15 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
.toString()); .toString());
} }
static String indicesString(TransportMessage message) { static Optional<String[]> indices(TransportMessage message) {
Set<String> indices = indices(message); if (message instanceof IndicesRequest) {
return indices == null ? null : collectionToCommaDelimitedString(indices); return Optional.of(((IndicesRequest) message).indices());
}
return Optional.empty();
} }
static String principal(User user) { static String principal(User user) {
StringBuilder builder = new StringBuilder("principal=["); final StringBuilder builder = new StringBuilder("principal=[");
builder.append(user.principal()); builder.append(user.principal());
if (user.isRunAs()) { if (user.isRunAs()) {
builder.append("], run_by_principal=[").append(user.authenticatedUser().principal()); builder.append("], run_by_principal=[").append(user.authenticatedUser().principal());
@ -442,6 +535,132 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
settings.add(INCLUDE_EVENT_SETTINGS); settings.add(INCLUDE_EVENT_SETTINGS);
settings.add(EXCLUDE_EVENT_SETTINGS); settings.add(EXCLUDE_EVENT_SETTINGS);
settings.add(INCLUDE_REQUEST_BODY); settings.add(INCLUDE_REQUEST_BODY);
settings.add(FILTER_POLICY_IGNORE_PRINCIPALS);
settings.add(FILTER_POLICY_IGNORE_INDICES);
settings.add(FILTER_POLICY_IGNORE_ROLES);
settings.add(FILTER_POLICY_IGNORE_REALMS);
}
/**
* Builds the predicate for a single policy filter. The predicate matches events
* that will be ignored, aka filtered out, aka not logged. The event can be
* filtered by the following fields : `user`, `realm`, `role` and `index`.
* Predicates on each field are ANDed together to form the filter predicate of
* the policy.
*/
private static final class EventFilterPolicy {
final String name;
private final Predicate<String> ignorePrincipalsPredicate;
private final Predicate<String> ignoreRealmsPredicate;
private final Predicate<String> ignoreRolesPredicate;
private final Predicate<String> ignoreIndicesPredicate;
/**
* An empty filter list for a field will match events with that field missing.
* An event with an undefined field has the field value the empty string ("") or
* a singleton list of the empty string ([""]).
*/
EventFilterPolicy(String name, Settings settings) {
this.name = name;
ignorePrincipalsPredicate = Automatons.predicate(
emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_PRINCIPALS.getConcreteSettingForNamespace(name).get(settings)));
ignoreRealmsPredicate = Automatons.predicate(
emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_REALMS.getConcreteSettingForNamespace(name).get(settings)));
ignoreRolesPredicate = Automatons.predicate(
emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_ROLES.getConcreteSettingForNamespace(name).get(settings)));
ignoreIndicesPredicate = Automatons.predicate(
emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_INDICES.getConcreteSettingForNamespace(name).get(settings)));
}
/**
* It is a requirement that empty string filters match empty string fields. In
* this case we require automatons from empty string to match the empty string.
* `Automatons.predicate("").test("") == false`
* `Automatons.predicate("//").test("") == true`
*/
private List<String> emptyStringBuildsEmptyAutomaton(List<String> l) {
if (l.isEmpty()) {
return Collections.singletonList("//");
}
return l.stream().map(f -> f.isEmpty() ? "//" : f).collect(Collectors.toList());
}
/**
* 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) -> {
return ignorePrincipalsPredicate.test(eventInfo.principal)
&& ignoreRealmsPredicate.test(eventInfo.realm)
&& eventInfo.roles.get().allMatch(ignoreRolesPredicate)
&& eventInfo.indices.get().allMatch(ignoreIndicesPredicate);
};
}
}
/**
* Builds the filter predicates for all the policies. Predicates of all policies
* are ORed together, so that an audit event matching any policy is ignored.
*/
private static final class EventFilterPolicyRegistry {
private final Map<String, Predicate<AuditEventMetaInfo>> policyMap;
EventFilterPolicyRegistry(Settings settings) {
final Map<String, Predicate<AuditEventMetaInfo>> map = new HashMap<>();
for (final String policyName : settings.getGroups(FILTER_POLICY_PREFIX, true).keySet()) {
map.put(policyName, new EventFilterPolicy(policyName, settings).ignorePredicate());
}
policyMap = Collections.unmodifiableMap(map);
}
Predicate<AuditEventMetaInfo> ignorePredicate() {
return policyMap.values().stream().reduce(x -> false, (x, y) -> x.or(y));
}
}
/**
* Abstraction for the fields of the audit event that are used for filtering. If
* an event has a missing field (one of `user`, `realm`, `roles` and `indices`)
* the value for the field will be the empty string or a singleton stream of the
* empty string.
*/
static final class AuditEventMetaInfo {
final String principal;
final String realm;
final Supplier<Stream<String>> roles;
final Supplier<Stream<String>> indices;
// empty is used for events can be filtered out only by the lack of a field
static final AuditEventMetaInfo EMPTY = new AuditEventMetaInfo(Optional.empty(), Optional.empty(), Optional.empty());
/**
* If a field is missing for an event, its value for filtering purposes is the
* empty string or a singleton stream of the empty string. This a allows a
* policy to filter by the missing value using the empty string, ie
* `ignore_filters.users: ["", "elastic"]` will filter events with a missing
* user field (such as `anonymous_access_denied`) as well as events from the
* "elastic" username.
*/
AuditEventMetaInfo(Optional<User> user, Optional<String> realm, Optional<String[]> roles, Optional<String[]> indices) {
this.principal = user.map(u -> u.principal()).orElse("");
this.realm = realm.orElse("");
// Supplier indirection and lazy generation of Streams serves 2 purposes:
// 1. streams might not get generated due to short circuiting logical
// conditions on the `principal` and `realm` fields
// 2. reusability of the AuditEventMetaInfo instance: in this case Streams have
// to be regenerated as they cannot be operated upon twice
this.roles = () -> roles.filter(r -> r.length != 0).map(Arrays::stream).orElse(Stream.of(""));
this.indices = () -> indices.filter(i -> i.length != 0).map(Arrays::stream).orElse(Stream.of(""));
}
AuditEventMetaInfo(Optional<AuthenticationToken> authenticationToken, Optional<String> realm, Optional<String[]> indices) {
this.principal = authenticationToken.map(u -> u.principal()).orElse("");
this.realm = realm.orElse("");
this.roles = () -> Stream.of("");
this.indices = () -> indices.filter(r -> r.length != 0).map(i -> Arrays.stream(i)).orElse(Stream.of(""));
}
} }
@Override @Override
@ -458,7 +677,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
} }
} }
protected static class LocalNodeInfo { static class LocalNodeInfo {
private final DiscoveryNode localNode; private final DiscoveryNode localNode;
private final String prefix; private final String prefix;
private final String localOriginTag; private final String localOriginTag;
@ -472,19 +691,19 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail,
static String resolvePrefix(Settings settings, @Nullable DiscoveryNode localNode) { static String resolvePrefix(Settings settings, @Nullable DiscoveryNode localNode) {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
if (HOST_ADDRESS_SETTING.get(settings)) { if (HOST_ADDRESS_SETTING.get(settings)) {
String address = localNode != null ? localNode.getHostAddress() : null; final String address = localNode != null ? localNode.getHostAddress() : null;
if (address != null) { if (address != null) {
builder.append("[").append(address).append("] "); builder.append("[").append(address).append("] ");
} }
} }
if (HOST_NAME_SETTING.get(settings)) { if (HOST_NAME_SETTING.get(settings)) {
String hostName = localNode != null ? localNode.getHostName() : null; final String hostName = localNode != null ? localNode.getHostName() : null;
if (hostName != null) { if (hostName != null) {
builder.append("[").append(hostName).append("] "); builder.append("[").append(hostName).append("] ");
} }
} }
if (NODE_NAME_SETTING.get(settings)) { if (NODE_NAME_SETTING.get(settings)) {
String name = settings.get("name"); final String name = settings.get("name");
if (name != null) { if (name != null) {
builder.append("[").append(name).append("] "); builder.append("[").append(name).append("] ");
} }

View File

@ -27,7 +27,6 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.rest.FakeRestRequest; import org.elasticsearch.test.rest.FakeRestRequest;
import org.elasticsearch.test.rest.FakeRestRequest.Builder; import org.elasticsearch.test.rest.FakeRestRequest.Builder;
import org.elasticsearch.transport.TransportMessage; import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.xpack.security.audit.AuditUtil;
import org.elasticsearch.xpack.security.authc.AuthenticationToken; import org.elasticsearch.xpack.security.authc.AuthenticationToken;
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;
@ -53,7 +52,7 @@ import static org.mockito.Mockito.when;
public class LoggingAuditTrailTests extends ESTestCase { public class LoggingAuditTrailTests extends ESTestCase {
private enum RestContent { enum RestContent {
VALID() { VALID() {
@Override @Override
protected boolean hasContent() { protected boolean hasContent() {
@ -62,12 +61,12 @@ public class LoggingAuditTrailTests extends ESTestCase {
@Override @Override
protected BytesReference content() { protected BytesReference content() {
return new BytesArray("{ \"key\": \"value\"}"); return new BytesArray("{ \"key\": \"value\" }");
} }
@Override @Override
protected String expectedMessage() { protected String expectedMessage() {
return "{ \"key\": \"value\"}"; return "{ \"key\": \"value\" }";
} }
}, },
INVALID() { INVALID() {
@ -78,12 +77,12 @@ public class LoggingAuditTrailTests extends ESTestCase {
@Override @Override
protected BytesReference content() { protected BytesReference content() {
return new BytesArray("{ \"key\": \"value\""); return new BytesArray("{ \"key\": \"value\" ");
} }
@Override @Override
protected String expectedMessage() { protected String expectedMessage() {
return "{ \"key\": \"value\""; return "{ \"key\": \"value\" ";
} }
}, },
EMPTY() { EMPTY() {
@ -129,7 +128,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
clusterService = mock(ClusterService.class); clusterService = mock(ClusterService.class);
when(clusterService.localNode()).thenReturn(localNode); when(clusterService.localNode()).thenReturn(localNode);
Mockito.doAnswer((Answer) invocation -> { Mockito.doAnswer((Answer) invocation -> {
LoggingAuditTrail arg0 = (LoggingAuditTrail) invocation.getArguments()[0]; final LoggingAuditTrail arg0 = (LoggingAuditTrail) invocation.getArguments()[0];
arg0.updateLocalNodeInfo(localNode); arg0.updateLocalNodeInfo(localNode);
return null; return null;
}).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class)); }).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class));
@ -138,10 +137,10 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAnonymousAccessDeniedTransport() throws Exception { public void testAnonymousAccessDeniedTransport() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
auditTrail.anonymousAccessDenied("_action", message); auditTrail.anonymousAccessDenied("_action", message);
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [anonymous_access_denied]\t" + origins + assertMsg(logger, Level.INFO, prefix + "[transport] [anonymous_access_denied]\t" + origins +
@ -160,11 +159,11 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAnonymousAccessDeniedRest() throws Exception { public void testAnonymousAccessDeniedRest() throws Exception {
InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1");
Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); final Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200));
String expectedMessage = tuple.v1().expectedMessage(); final String expectedMessage = tuple.v1().expectedMessage();
RestRequest request = tuple.v2(); final RestRequest request = tuple.v2();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.anonymousAccessDenied(request); auditTrail.anonymousAccessDenied(request);
if (includeRequestBody) { if (includeRequestBody) {
@ -184,10 +183,10 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAuthenticationFailed() throws Exception { public void testAuthenticationFailed() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
auditTrail.authenticationFailed(new MockToken(), "_action", message); auditTrail.authenticationFailed(new MockToken(), "_action", message);
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_failed]\t" + origins + assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_failed]\t" + origins +
@ -207,10 +206,10 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAuthenticationFailedNoToken() throws Exception { public void testAuthenticationFailedNoToken() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
auditTrail.authenticationFailed("_action", message); auditTrail.authenticationFailed("_action", message);
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_failed]\t" + origins + assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_failed]\t" + origins +
@ -229,11 +228,11 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAuthenticationFailedRest() throws Exception { public void testAuthenticationFailedRest() throws Exception {
InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1");
Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); final Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200));
String expectedMessage = tuple.v1().expectedMessage(); final String expectedMessage = tuple.v1().expectedMessage();
RestRequest request = tuple.v2(); final RestRequest request = tuple.v2();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.authenticationFailed(new MockToken(), request); auditTrail.authenticationFailed(new MockToken(), request);
if (includeRequestBody) { if (includeRequestBody) {
@ -254,11 +253,11 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAuthenticationFailedRestNoToken() throws Exception { public void testAuthenticationFailedRestNoToken() throws Exception {
InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1");
Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); final Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200));
String expectedMessage = tuple.v1().expectedMessage(); final String expectedMessage = tuple.v1().expectedMessage();
RestRequest request = tuple.v2(); final RestRequest request = tuple.v2();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.authenticationFailed(request); auditTrail.authenticationFailed(request);
if (includeRequestBody) { if (includeRequestBody) {
@ -278,9 +277,9 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAuthenticationFailedRealm() throws Exception { public void testAuthenticationFailedRealm() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
auditTrail.authenticationFailed("_realm", new MockToken(), "_action", message); auditTrail.authenticationFailed("_realm", new MockToken(), "_action", message);
assertEmptyLog(logger); assertEmptyLog(logger);
@ -288,7 +287,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
settings = settings =
Settings.builder().put(settings).put("xpack.security.audit.logfile.events.include", "realm_authentication_failed").build(); Settings.builder().put(settings).put("xpack.security.audit.logfile.events.include", "realm_authentication_failed").build();
auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
auditTrail.authenticationFailed("_realm", new MockToken(), "_action", message); auditTrail.authenticationFailed("_realm", new MockToken(), "_action", message);
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [realm_authentication_failed]\trealm=[_realm], " + origins + assertMsg(logger, Level.INFO, prefix + "[transport] [realm_authentication_failed]\trealm=[_realm], " + origins +
@ -301,11 +300,11 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAuthenticationFailedRealmRest() throws Exception { public void testAuthenticationFailedRealmRest() throws Exception {
InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1");
Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); final Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200));
String expectedMessage = tuple.v1().expectedMessage(); final String expectedMessage = tuple.v1().expectedMessage();
RestRequest request = tuple.v2(); final RestRequest request = tuple.v2();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.authenticationFailed("_realm", new MockToken(), request); auditTrail.authenticationFailed("_realm", new MockToken(), request);
assertEmptyLog(logger); assertEmptyLog(logger);
@ -326,20 +325,20 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAccessGranted() throws Exception { public void testAccessGranted() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
boolean runAs = randomBoolean(); final boolean runAs = randomBoolean();
User user; User user;
if (runAs) { if (runAs) {
user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"}));
} else { } else {
user = new User("_username", new String[]{"r1"}); user = new User("_username", new String[]{"r1"});
} }
String role = randomAlphaOfLengthBetween(1, 6); final String role = randomAlphaOfLengthBetween(1, 6);
auditTrail.accessGranted(user, "_action", message, new String[] { role }); auditTrail.accessGranted(user, "_action", message, new String[] { role });
String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + final String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") +
", roles=[" + role + "]"; ", roles=[" + role + "]";
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", " + userInfo + assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", " + userInfo +
@ -358,17 +357,17 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAccessGrantedInternalSystemAction() throws Exception { public void testAccessGrantedInternalSystemAction() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String role = randomAlphaOfLengthBetween(1, 6); final String role = randomAlphaOfLengthBetween(1, 6);
auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { role }); auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { role });
assertEmptyLog(logger); assertEmptyLog(logger);
// test enabled // test enabled
settings = Settings.builder().put(settings).put("xpack.security.audit.logfile.events.include", "system_access_granted").build(); settings = Settings.builder().put(settings).put("xpack.security.audit.logfile.events.include", "system_access_granted").build();
auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { role }); auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { role });
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", principal=[" + assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", principal=[" +
@ -382,20 +381,20 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAccessGrantedInternalSystemActionNonSystemUser() throws Exception { public void testAccessGrantedInternalSystemActionNonSystemUser() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
boolean runAs = randomBoolean(); final boolean runAs = randomBoolean();
User user; User user;
if (runAs) { if (runAs) {
user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"}));
} else { } else {
user = new User("_username", new String[]{"r1"}); user = new User("_username", new String[]{"r1"});
} }
String role = randomAlphaOfLengthBetween(1, 6); final String role = randomAlphaOfLengthBetween(1, 6);
auditTrail.accessGranted(user, "internal:_action", message, new String[] { role }); auditTrail.accessGranted(user, "internal:_action", message, new String[] { role });
String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + final String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") +
", roles=[" + role + "]"; ", roles=[" + role + "]";
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", " + userInfo + assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", " + userInfo +
@ -414,20 +413,20 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAccessDenied() throws Exception { public void testAccessDenied() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
boolean runAs = randomBoolean(); final boolean runAs = randomBoolean();
User user; User user;
if (runAs) { if (runAs) {
user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"}));
} else { } else {
user = new User("_username", new String[]{"r1"}); user = new User("_username", new String[]{"r1"});
} }
String role = randomAlphaOfLengthBetween(1, 6); final String role = randomAlphaOfLengthBetween(1, 6);
auditTrail.accessDenied(user, "_action", message, new String[] { role }); auditTrail.accessDenied(user, "_action", message, new String[] { role });
String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + final String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") +
", roles=[" + role + "]"; ", roles=[" + role + "]";
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [access_denied]\t" + origins + ", " + userInfo + assertMsg(logger, Level.INFO, prefix + "[transport] [access_denied]\t" + origins + ", " + userInfo +
@ -446,11 +445,11 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testTamperedRequestRest() throws Exception { public void testTamperedRequestRest() throws Exception {
InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1");
Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); final Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200));
String expectedMessage = tuple.v1().expectedMessage(); final String expectedMessage = tuple.v1().expectedMessage();
RestRequest request = tuple.v2(); final RestRequest request = tuple.v2();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.tamperedRequest(request); auditTrail.tamperedRequest(request);
if (includeRequestBody) { if (includeRequestBody) {
@ -470,11 +469,11 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testTamperedRequest() throws Exception { public void testTamperedRequest() throws Exception {
String action = "_action"; final String action = "_action";
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
auditTrail.tamperedRequest(action, message); auditTrail.tamperedRequest(action, message);
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [tampered_request]\t" + origins + assertMsg(logger, Level.INFO, prefix + "[transport] [tampered_request]\t" + origins +
@ -489,7 +488,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testTamperedRequestWithUser() throws Exception { public void testTamperedRequestWithUser() throws Exception {
String action = "_action"; final String action = "_action";
final boolean runAs = randomBoolean(); final boolean runAs = randomBoolean();
User user; User user;
if (runAs) { if (runAs) {
@ -497,11 +496,11 @@ public class LoggingAuditTrailTests extends ESTestCase {
} else { } else {
user = new User("_username", new String[]{"r1"}); user = new User("_username", new String[]{"r1"});
} }
String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; final String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]";
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
auditTrail.tamperedRequest(user, action, message); auditTrail.tamperedRequest(user, action, message);
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [tampered_request]\t" + origins + ", " + userInfo + assertMsg(logger, Level.INFO, prefix + "[transport] [tampered_request]\t" + origins + ", " + userInfo +
@ -520,10 +519,10 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testConnectionDenied() throws Exception { public void testConnectionDenied() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
InetAddress inetAddress = InetAddress.getLoopbackAddress(); final InetAddress inetAddress = InetAddress.getLoopbackAddress();
SecurityIpFilterRule rule = new SecurityIpFilterRule(false, "_all"); final SecurityIpFilterRule rule = new SecurityIpFilterRule(false, "_all");
auditTrail.connectionDenied(inetAddress, "default", rule); auditTrail.connectionDenied(inetAddress, "default", rule);
assertMsg(logger, Level.INFO, String.format(Locale.ROOT, prefix + assertMsg(logger, Level.INFO, String.format(Locale.ROOT, prefix +
"[ip_filter] [connection_denied]\torigin_address=[%s], transport_profile=[%s], rule=[deny %s]", "[ip_filter] [connection_denied]\torigin_address=[%s], transport_profile=[%s], rule=[deny %s]",
@ -538,10 +537,10 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testConnectionGranted() throws Exception { public void testConnectionGranted() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
InetAddress inetAddress = InetAddress.getLoopbackAddress(); final InetAddress inetAddress = InetAddress.getLoopbackAddress();
SecurityIpFilterRule rule = IPFilter.DEFAULT_PROFILE_ACCEPT_ALL; final SecurityIpFilterRule rule = IPFilter.DEFAULT_PROFILE_ACCEPT_ALL;
auditTrail.connectionGranted(inetAddress, "default", rule); auditTrail.connectionGranted(inetAddress, "default", rule);
assertEmptyLog(logger); assertEmptyLog(logger);
@ -555,15 +554,22 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testRunAsGranted() throws Exception { public void testRunAsGranted() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = new MockMessage(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); final User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"}));
String role = randomAlphaOfLengthBetween(1, 6); final String role = randomAlphaOfLengthBetween(1, 6);
auditTrail.runAsGranted(user, "_action", message, new String[] { role }); auditTrail.runAsGranted(user, "_action", message, new String[] { role });
assertMsg(logger, Level.INFO, prefix + "[transport] [run_as_granted]\t" + origins + if (message instanceof IndicesRequest) {
", principal=[_username], run_as_principal=[running as], roles=[" + role + "], action=[_action], request=[MockMessage]"); assertMsg(logger, Level.INFO,
prefix + "[transport] [run_as_granted]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=["
+ role + "], action=[_action], indices=[" + indices(message) + "], request=[MockIndicesRequest]");
} else {
assertMsg(logger, Level.INFO,
prefix + "[transport] [run_as_granted]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=["
+ role + "], action=[_action], request=[MockMessage]");
}
// test disabled // test disabled
CapturingLogger.output(logger.getName(), Level.INFO).clear(); CapturingLogger.output(logger.getName(), Level.INFO).clear();
@ -574,15 +580,22 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testRunAsDenied() throws Exception { public void testRunAsDenied() throws Exception {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = new MockMessage(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); final User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"}));
String role = randomAlphaOfLengthBetween(1, 6); final String role = randomAlphaOfLengthBetween(1, 6);
auditTrail.runAsDenied(user, "_action", message, new String[] { role }); auditTrail.runAsDenied(user, "_action", message, new String[] { role });
assertMsg(logger, Level.INFO, prefix + "[transport] [run_as_denied]\t" + origins + if (message instanceof IndicesRequest) {
", principal=[_username], run_as_principal=[running as], roles=[" + role + "], action=[_action], request=[MockMessage]"); assertMsg(logger, Level.INFO,
prefix + "[transport] [run_as_denied]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=["
+ role + "], action=[_action], indices=[" + indices(message) + "], request=[MockIndicesRequest]");
} else {
assertMsg(logger, Level.INFO,
prefix + "[transport] [run_as_denied]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=["
+ role + "], action=[_action], request=[MockMessage]");
}
// test disabled // test disabled
CapturingLogger.output(logger.getName(), Level.INFO).clear(); CapturingLogger.output(logger.getName(), Level.INFO).clear();
@ -593,17 +606,16 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testOriginAttributes() throws Exception { public void testOriginAttributes() throws Exception {
final MockMessage message = new MockMessage(threadContext);
MockMessage message = new MockMessage(threadContext); final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); final String text = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
String text = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext);
InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext);
if (restAddress != null) { if (restAddress != null) {
assertThat(text, equalTo("origin_type=[rest], origin_address=[" + assertThat(text, equalTo("origin_type=[rest], origin_address=[" +
NetworkAddress.format(restAddress.getAddress()) + "]")); NetworkAddress.format(restAddress.getAddress()) + "]"));
return; return;
} }
TransportAddress address = message.remoteAddress(); final TransportAddress address = message.remoteAddress();
if (address == null) { if (address == null) {
assertThat(text, equalTo("origin_type=[local_node], origin_address=[" + localNode.getHostAddress() + "]")); assertThat(text, equalTo("origin_type=[local_node], origin_address=[" + localNode.getHostAddress() + "]"));
return; return;
@ -614,26 +626,26 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
public void testAuthenticationSuccessRest() throws Exception { public void testAuthenticationSuccessRest() throws Exception {
Map<String, String> params = new HashMap<>(); final Map<String, String> params = new HashMap<>();
params.put("foo", "bar"); params.put("foo", "bar");
InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1");
Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200), params); final Tuple<RestContent, RestRequest> tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200), params);
String expectedMessage = tuple.v1().expectedMessage(); final String expectedMessage = tuple.v1().expectedMessage();
RestRequest request = tuple.v2(); final RestRequest request = tuple.v2();
boolean runAs = randomBoolean(); final boolean runAs = randomBoolean();
User user; User user;
if (runAs) { if (runAs) {
user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"}));
} else { } else {
user = new User("_username", new String[] { "r1" }); user = new User("_username", new String[] { "r1" });
} }
String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; final String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]";
String realm = "_realm"; final String realm = "_realm";
Settings settings = Settings.builder().put(this.settings) Settings settings = Settings.builder().put(this.settings)
.put("xpack.security.audit.logfile.events.include", "authentication_success") .put("xpack.security.audit.logfile.events.include", "authentication_success")
.build(); .build();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.authenticationSuccess(realm, user, request); auditTrail.authenticationSuccess(realm, user, request);
if (includeRequestBody) { if (includeRequestBody) {
@ -657,23 +669,23 @@ public class LoggingAuditTrailTests extends ESTestCase {
public void testAuthenticationSuccessTransport() throws Exception { public void testAuthenticationSuccessTransport() throws Exception {
Settings settings = Settings.builder().put(this.settings) Settings settings = Settings.builder().put(this.settings)
.put("xpack.security.audit.logfile.events.include", "authentication_success").build(); .put("xpack.security.audit.logfile.events.include", "authentication_success").build();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo);
boolean runAs = randomBoolean(); final boolean runAs = randomBoolean();
User user; User user;
if (runAs) { if (runAs) {
user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"}));
} else { } else {
user = new User("_username", new String[] { "r1" }); user = new User("_username", new String[] { "r1" });
} }
String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; final String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]";
String realm = "_realm"; final String realm = "_realm";
auditTrail.authenticationSuccess(realm, user, "_action", message); auditTrail.authenticationSuccess(realm, user, "_action", message);
if (message instanceof IndicesRequest) { if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo
+ ", realm=[_realm], action=[_action], request=[MockIndicesRequest]"); + ", realm=[_realm], action=[_action], indices=[" + indices(message) + "], request=[MockIndicesRequest]");
} else { } else {
assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo
+ ", realm=[_realm], action=[_action], request=[MockMessage]"); + ", realm=[_realm], action=[_action], request=[MockMessage]");
@ -691,7 +703,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
private void assertMsg(Logger logger, Level level, String message) { private void assertMsg(Logger logger, Level level, String message) {
List<String> output = CapturingLogger.output(logger.getName(), level); final List<String> output = CapturingLogger.output(logger.getName(), level);
assertThat(output.size(), is(1)); assertThat(output.size(), is(1));
assertThat(output.get(0), equalTo(message)); assertThat(output.get(0), equalTo(message));
} }
@ -700,13 +712,13 @@ public class LoggingAuditTrailTests extends ESTestCase {
assertThat(CapturingLogger.isEmpty(logger.getName()), is(true)); assertThat(CapturingLogger.isEmpty(logger.getName()), is(true));
} }
private Tuple<RestContent, RestRequest> prepareRestContent(String uri, InetSocketAddress remoteAddress) { protected Tuple<RestContent, RestRequest> prepareRestContent(String uri, InetSocketAddress remoteAddress) {
return prepareRestContent(uri, remoteAddress, Collections.emptyMap()); return prepareRestContent(uri, remoteAddress, Collections.emptyMap());
} }
private Tuple<RestContent, RestRequest> prepareRestContent(String uri, InetSocketAddress remoteAddress, Map<String, String> params) { private Tuple<RestContent, RestRequest> prepareRestContent(String uri, InetSocketAddress remoteAddress, Map<String, String> params) {
RestContent content = randomFrom(RestContent.values()); final RestContent content = randomFrom(RestContent.values());
FakeRestRequest.Builder builder = new Builder(NamedXContentRegistry.EMPTY); final FakeRestRequest.Builder builder = new Builder(NamedXContentRegistry.EMPTY);
if (content.hasContent()) { if (content.hasContent()) {
builder.withContent(content.content(), XContentType.JSON); builder.withContent(content.content(), XContentType.JSON);
} }
@ -717,18 +729,18 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
/** creates address without any lookups. hostname can be null, for missing */ /** creates address without any lookups. hostname can be null, for missing */
private static InetAddress forge(String hostname, String address) throws IOException { protected static InetAddress forge(String hostname, String address) throws IOException {
byte bytes[] = InetAddress.getByName(address).getAddress(); final byte bytes[] = InetAddress.getByName(address).getAddress();
return InetAddress.getByAddress(hostname, bytes); return InetAddress.getByAddress(hostname, bytes);
} }
private static String indices(TransportMessage message) { private static String indices(TransportMessage message) {
return Strings.collectionToCommaDelimitedString(AuditUtil.indices(message)); return Strings.arrayToCommaDelimitedString(((IndicesRequest) message).indices());
} }
private static class MockMessage extends TransportMessage { static class MockMessage extends TransportMessage {
private MockMessage(ThreadContext threadContext) throws IOException { MockMessage(ThreadContext threadContext) throws IOException {
if (randomBoolean()) { if (randomBoolean()) {
if (randomBoolean()) { if (randomBoolean()) {
remoteAddress(buildNewFakeTransportAddress()); remoteAddress(buildNewFakeTransportAddress());
@ -742,9 +754,9 @@ public class LoggingAuditTrailTests extends ESTestCase {
} }
} }
private static class MockIndicesRequest extends org.elasticsearch.action.MockIndicesRequest { static class MockIndicesRequest extends org.elasticsearch.action.MockIndicesRequest {
private MockIndicesRequest(ThreadContext threadContext) throws IOException { MockIndicesRequest(ThreadContext threadContext) throws IOException {
super(IndicesOptions.strictExpandOpenAndForbidClosed(), "idx1", "idx2"); super(IndicesOptions.strictExpandOpenAndForbidClosed(), "idx1", "idx2");
if (randomBoolean()) { if (randomBoolean()) {
remoteAddress(buildNewFakeTransportAddress()); remoteAddress(buildNewFakeTransportAddress());