Add X-Forwarded-For to the logfile audit (#36427)

Extracts the value of the X-Forwarded-For HTTP request header and
places it in the audit entries from the logfile output.
This commit is contained in:
Albert Zaharovits 2018-12-19 14:56:40 +02:00 committed by GitHub
parent 3cc0cf03c6
commit 63aa8756b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 0 deletions

View File

@ -94,6 +94,16 @@ descriptions for the format that you are using.
the request associated with this event. This header can
be used freely by the client to mark API calls, as it has
no semantics in Elasticsearch.
`x_forwarded_for` :: The verbatim value of the `X-Forwarded-For` HTTP request
header (if present) of the request associated with the
audit event. This header is commonly added by proxies
when they forward requests and the value is the address
of the proxied client. When a request crosses multiple
proxies the header is a comma delimited list with the
last value being the address of the second to last
proxy server (the address of the last proxy server is
designated by the `origin.address` field).
==== Audit event attributes of the REST event type

View File

@ -28,6 +28,7 @@ appender.audit_rolling.layout.pattern = {\
%varsNotEmpty{, "request.name":"%enc{%map{request.name}}{JSON}"}\
%varsNotEmpty{, "indices":%map{indices}}\
%varsNotEmpty{, "opaque_id":"%enc{%map{opaque_id}}{JSON}"}\
%varsNotEmpty{, "x_forwarded_for":"%enc{%map{x_forwarded_for}}{JSON}"}\
%varsNotEmpty{, "transport.profile":"%enc{%map{transport.profile}}{JSON}"}\
%varsNotEmpty{, "rule":"%enc{%map{rule}}{JSON}"}\
%varsNotEmpty{, "event.category":"%enc{%map{event.category}}{JSON}"}\
@ -56,6 +57,7 @@ appender.audit_rolling.layout.pattern = {\
# "request.name" if the event is in connection to a transport message this is the name of the request class, similar to how rest requests are identified by the url path (internal)
# "indices" the array of indices that the "action" is acting upon
# "opaque_id" opaque value conveyed by the "X-Opaque-Id" request header
# "x_forwarded_for" the addresses from the "X-Forwarded-For" request header, as a verbatim string value (not an array)
# "transport.profile" name of the transport profile in case this is a "connection_granted" or "connection_denied" event
# "rule" name of the applied rulee if the "origin.type" is "ip_filter"
# "event.category" fixed value "elasticsearch-audit"

View File

@ -634,6 +634,9 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
}
Set<String> headers = new HashSet<>();
headers.add(UsernamePasswordToken.BASIC_AUTH_HEADER);
if (XPackSettings.AUDIT_ENABLED.get(settings)) {
headers.add(AuditTrail.X_FORWARDED_FOR_HEADER);
}
if (AuthenticationServiceField.RUN_AS_ENABLED.get(settings)) {
headers.add(AuthenticationServiceField.RUN_AS_USER_HEADER);
}

View File

@ -16,6 +16,8 @@ import java.net.InetAddress;
public interface AuditTrail {
String X_FORWARDED_FOR_HEADER = "X-Forwarded-For";
String name();
void authenticationSuccess(String requestId, String realm, User user, RestRequest request);

View File

@ -108,6 +108,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
public static final String TRANSPORT_PROFILE_FIELD_NAME = "transport.profile";
public static final String RULE_FIELD_NAME = "rule";
public static final String OPAQUE_ID_FIELD_NAME = "opaque_id";
public static final String X_FORWARDED_FOR_FIELD_NAME = "x_forwarded_for";
public static final String NAME = "logfile";
public static final Setting<Boolean> EMIT_HOST_ADDRESS_SETTING = Setting.boolSetting(setting("audit.logfile.emit_node_host_address"),
@ -216,6 +217,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRestOrigin(request)
.withRequestBody(request)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -238,6 +240,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRestOrTransportOrigin(message, threadContext)
.with(INDICES_FIELD_NAME, indices.orElse(null))
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -259,6 +262,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRestOrTransportOrigin(message, threadContext)
.with(INDICES_FIELD_NAME, indices.orElse(null))
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -277,6 +281,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRequestBody(request)
.withRequestId(requestId)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -298,6 +303,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRestOrTransportOrigin(message, threadContext)
.with(INDICES_FIELD_NAME, indices.orElse(null))
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -315,6 +321,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRequestBody(request)
.withRequestId(requestId)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -335,6 +342,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRestOrTransportOrigin(message, threadContext)
.with(INDICES_FIELD_NAME, indices.orElse(null))
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -354,6 +362,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRequestBody(request)
.withRequestId(requestId)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -376,6 +385,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRestOrTransportOrigin(message, threadContext)
.with(INDICES_FIELD_NAME, indices.orElse(null))
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -396,6 +406,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRequestBody(request)
.withRequestId(requestId)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -420,6 +431,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(INDICES_FIELD_NAME, indices.orElse(null))
.with(PRINCIPAL_ROLES_FIELD_NAME, roleNames)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -443,6 +455,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(INDICES_FIELD_NAME, indices.orElse(null))
.with(PRINCIPAL_ROLES_FIELD_NAME, roleNames)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -460,6 +473,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRequestBody(request)
.withRequestId(requestId)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -480,6 +494,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRestOrTransportOrigin(message, threadContext)
.with(INDICES_FIELD_NAME, indices.orElse(null))
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -502,6 +517,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withPrincipal(user)
.with(INDICES_FIELD_NAME, indices.orElse(null))
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -520,6 +536,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(TRANSPORT_PROFILE_FIELD_NAME, profile)
.with(RULE_FIELD_NAME, rule.toString())
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -537,6 +554,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(TRANSPORT_PROFILE_FIELD_NAME, profile)
.with(RULE_FIELD_NAME, rule.toString())
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -559,6 +577,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(INDICES_FIELD_NAME, indices.orElse(null))
.with(PRINCIPAL_ROLES_FIELD_NAME, roleNames)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -582,6 +601,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(INDICES_FIELD_NAME, indices.orElse(null))
.with(PRINCIPAL_ROLES_FIELD_NAME, roleNames)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -603,6 +623,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.withRequestBody(request)
.withRequestId(requestId)
.withOpaqueId(threadContext)
.withXForwardedFor(threadContext)
.build();
logger.info(logEntry);
}
@ -696,6 +717,14 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
return this;
}
LogEntryBuilder withXForwardedFor(ThreadContext threadContext) {
final String xForwardedFor = threadContext.getHeader(AuditTrail.X_FORWARDED_FOR_HEADER);
if (xForwardedFor != null) {
logEntry.with(X_FORWARDED_FOR_FIELD_NAME, xForwardedFor);
}
return this;
}
LogEntryBuilder withPrincipal(User user) {
logEntry.with(PRINCIPAL_FIELD_NAME, user.principal());
if (user.isRunAs()) {

View File

@ -38,6 +38,7 @@ import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.user.SystemUser;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.audit.AuditUtil;
import org.elasticsearch.xpack.security.rest.RemoteHostHeader;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@ -189,6 +190,10 @@ public class LoggingAuditTrailTests extends ESTestCase {
if (randomBoolean()) {
threadContext.putHeader(Task.X_OPAQUE_ID, randomAlphaOfLengthBetween(1, 4));
}
if (randomBoolean()) {
threadContext.putHeader(AuditTrail.X_FORWARDED_FOR_HEADER,
randomFrom("2001:db8:85a3:8d3:1319:8a2e:370:7348", "203.0.113.195", "203.0.113.195, 70.41.3.18, 150.172.238.178"));
}
logger = CapturingLogger.newCapturingLogger(Level.INFO, patternLayout);
auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
}
@ -212,6 +217,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
indicesRequest(message, checkedFields, checkedArrayFields);
restOrTransportOrigin(message, threadContext, checkedFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -245,6 +251,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.URL_PATH_FIELD_NAME, "_uri")
.put(LoggingAuditTrail.URL_QUERY_FIELD_NAME, null);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
// test disabled
@ -275,6 +282,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -303,6 +311,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -343,6 +352,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.URL_PATH_FIELD_NAME, "_uri")
.put(LoggingAuditTrail.URL_QUERY_FIELD_NAME, params.isEmpty() ? null : "foo=bar");
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
// test disabled
@ -382,6 +392,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.URL_PATH_FIELD_NAME, "_uri")
.put(LoggingAuditTrail.URL_QUERY_FIELD_NAME, params.isEmpty() ? null : "bar=baz");
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
// test disabled
@ -422,6 +433,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
}
@ -462,6 +474,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.URL_PATH_FIELD_NAME, "_uri")
.put(LoggingAuditTrail.URL_QUERY_FIELD_NAME, params.isEmpty() ? null : "_param=baz");
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
}
@ -484,6 +497,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -525,6 +539,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
}
@ -547,6 +562,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -579,6 +595,8 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -615,6 +633,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.URL_PATH_FIELD_NAME, "_uri")
.put(LoggingAuditTrail.URL_QUERY_FIELD_NAME, params.isEmpty() ? null : "_param=baz");
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
// test disabled
@ -643,6 +662,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -684,6 +704,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -713,6 +734,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.TRANSPORT_PROFILE_FIELD_NAME, profile)
.put(LoggingAuditTrail.RULE_FIELD_NAME, "deny _all");
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
// test disabled
@ -751,6 +773,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.TRANSPORT_PROFILE_FIELD_NAME, profile)
.put(LoggingAuditTrail.RULE_FIELD_NAME, "allow default:accept_all");
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
}
@ -779,6 +802,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -817,6 +841,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
// test disabled
@ -878,6 +903,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
checkedFields.put(LoggingAuditTrail.PRINCIPAL_FIELD_NAME, "_username");
}
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap());
}
@ -919,6 +945,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
restOrTransportOrigin(message, threadContext, checkedFields);
indicesRequest(message, checkedFields, checkedArrayFields);
opaqueId(threadContext, checkedFields);
forwardedFor(threadContext, checkedFields);
assertMsg(logger, checkedFields.immutableMap(), checkedArrayFields.immutableMap());
}
@ -1177,6 +1204,15 @@ public class LoggingAuditTrailTests extends ESTestCase {
}
}
private static void forwardedFor(ThreadContext threadContext ,MapBuilder<String, String> checkedFields) {
final String forwardedFor = threadContext.getHeader(AuditTrail.X_FORWARDED_FOR_HEADER);
if (forwardedFor != null) {
checkedFields.put(LoggingAuditTrail.X_FORWARDED_FOR_FIELD_NAME, forwardedFor);
} else {
checkedFields.put(LoggingAuditTrail.X_FORWARDED_FOR_FIELD_NAME, null);
}
}
private static void indicesRequest(TransportMessage message, MapBuilder<String, String> checkedFields,
MapBuilder<String, String[]> checkedArrayFields) {
if (message instanceof IndicesRequest) {