Security Audit includes HTTP method for requests (#37322)

Adds another field, named "request.method", to the structured logfile audit.
This field is present for all events associated with a REST request (not a
transport request) and the value is one of GET, POST, PUT, DELETE, OPTIONS,
HEAD, PATCH, TRACE and CONNECT.
This commit is contained in:
Albert Zaharovits 2019-01-13 15:26:23 +02:00 committed by GitHub
parent 96cfa000a5
commit 6fd57d90da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 23 additions and 10 deletions

View File

@ -118,6 +118,9 @@ common ones):
This is URL encoded.
`url.query` :: The query part of the URL (after "?", if present) of the
REST request associated with this event. This is URL encoded.
`request.method` :: The HTTP method of the REST request associated with this
event. It is one of GET, POST, PUT, DELETE, OPTIONS,
HEAD, PATCH, TRACE and CONNECT.
`request.body` :: The full content of the REST request associated with this
event, if enabled. This contains the query body. The body
is escaped according to the JSON RFC 4627.

View File

@ -18,8 +18,7 @@ For more information, see {ref}/logging.html#configuring-logging-levels[configur
[[audit-log-entry-format]]
=== Log entry format
The log entries in the `<clustername>_audit.log` file
have the following format:
The log entries in the `<clustername>_audit.log` file have the following format:
- Each log entry is a one line JSON document and each one is printed on a separate line.
- The fields of a log entry are ordered. However, if a field does not have a value it

View File

@ -22,6 +22,7 @@ appender.audit_rolling.layout.pattern = {\
%varsNotEmpty{, "realm":"%enc{%map{realm}}{JSON}"}\
%varsNotEmpty{, "url.path":"%enc{%map{url.path}}{JSON}"}\
%varsNotEmpty{, "url.query":"%enc{%map{url.query}}{JSON}"}\
%varsNotEmpty{, "request.method":"%enc{%map{request.method}}{JSON}"}\
%varsNotEmpty{, "request.body":"%enc{%map{request.body}}{JSON}"}\
%varsNotEmpty{, "request.id":"%enc{%map{request.id}}{JSON}"}\
%varsNotEmpty{, "action":"%enc{%map{action}}{JSON}"}\
@ -51,6 +52,7 @@ appender.audit_rolling.layout.pattern = {\
# "realm" name of a realm that has generated an "authentication_failed" or an "authentication_successful"; the subject is not yet authenticated
# "url.path" the URI component between the port and the query string; it is percent (URL) encoded
# "url.query" the URI component after the path and before the fragment; it is percent (URL) encoded
# "request.method" the method of the HTTP request, i.e. one of GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH, TRACE, CONNECT
# "request.body" the content of the request body entity, JSON escaped
# "request.id" a synthentic identifier for the incoming request, this is unique per incoming request, and consistent across all audit events generated by that request
# "action" an action is the most granular operation that is authorized and this identifies it in a namespaced way (internal)

View File

@ -100,6 +100,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
public static final String REALM_FIELD_NAME = "realm";
public static final String URL_PATH_FIELD_NAME = "url.path";
public static final String URL_QUERY_FIELD_NAME = "url.query";
public static final String REQUEST_METHOD_FIELD_NAME = "request.method";
public static final String REQUEST_BODY_FIELD_NAME = "request.body";
public static final String REQUEST_ID_FIELD_NAME = "request.id";
public static final String ACTION_FIELD_NAME = "action";
@ -211,7 +212,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(EVENT_TYPE_FIELD_NAME, REST_ORIGIN_FIELD_VALUE)
.with(EVENT_ACTION_FIELD_NAME, "authentication_success")
.with(REALM_FIELD_NAME, realm)
.withRestUri(request)
.withRestUriAndMethod(request)
.withRequestId(requestId)
.withPrincipal(user)
.withRestOrigin(request)
@ -276,7 +277,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
final StringMapMessage logEntry = new LogEntryBuilder()
.with(EVENT_TYPE_FIELD_NAME, REST_ORIGIN_FIELD_VALUE)
.with(EVENT_ACTION_FIELD_NAME, "anonymous_access_denied")
.withRestUri(request)
.withRestUriAndMethod(request)
.withRestOrigin(request)
.withRequestBody(request)
.withRequestId(requestId)
@ -316,7 +317,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
final StringMapMessage logEntry = new LogEntryBuilder()
.with(EVENT_TYPE_FIELD_NAME, REST_ORIGIN_FIELD_VALUE)
.with(EVENT_ACTION_FIELD_NAME, "authentication_failed")
.withRestUri(request)
.withRestUriAndMethod(request)
.withRestOrigin(request)
.withRequestBody(request)
.withRequestId(requestId)
@ -357,7 +358,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(EVENT_TYPE_FIELD_NAME, REST_ORIGIN_FIELD_VALUE)
.with(EVENT_ACTION_FIELD_NAME, "authentication_failed")
.with(PRINCIPAL_FIELD_NAME, token.principal())
.withRestUri(request)
.withRestUriAndMethod(request)
.withRestOrigin(request)
.withRequestBody(request)
.withRequestId(requestId)
@ -401,7 +402,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(EVENT_ACTION_FIELD_NAME, "realm_authentication_failed")
.with(REALM_FIELD_NAME, realm)
.with(PRINCIPAL_FIELD_NAME, token.principal())
.withRestUri(request)
.withRestUriAndMethod(request)
.withRestOrigin(request)
.withRequestBody(request)
.withRequestId(requestId)
@ -468,7 +469,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
final StringMapMessage logEntry = new LogEntryBuilder()
.with(EVENT_TYPE_FIELD_NAME, REST_ORIGIN_FIELD_VALUE)
.with(EVENT_ACTION_FIELD_NAME, "tampered_request")
.withRestUri(request)
.withRestUriAndMethod(request)
.withRestOrigin(request)
.withRequestBody(request)
.withRequestId(requestId)
@ -617,7 +618,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
.with(EVENT_TYPE_FIELD_NAME, REST_ORIGIN_FIELD_VALUE)
.with(EVENT_ACTION_FIELD_NAME, "run_as_denied")
.with(PRINCIPAL_ROLES_FIELD_NAME, roleNames)
.withRestUri(request)
.withRestUriAndMethod(request)
.withRunAsSubject(authentication)
.withRestOrigin(request)
.withRequestBody(request)
@ -637,7 +638,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
logEntry = new StringMapMessage(LoggingAuditTrail.this.entryCommonFields.commonFields);
}
LogEntryBuilder withRestUri(RestRequest request) {
LogEntryBuilder withRestUriAndMethod(RestRequest request) {
final int queryStringIndex = request.uri().indexOf('?');
int queryStringLength = request.uri().indexOf('#');
if (queryStringLength < 0) {
@ -651,6 +652,7 @@ public class LoggingAuditTrail implements AuditTrail, ClusterStateListener {
if (queryStringIndex > -1) {
logEntry.with(URL_QUERY_FIELD_NAME, request.uri().substring(queryStringIndex + 1, queryStringLength));
}
logEntry.with(REQUEST_METHOD_FIELD_NAME, request.method().toString());
return this;
}

View File

@ -245,6 +245,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.EVENT_ACTION_FIELD_NAME, "anonymous_access_denied")
.put(LoggingAuditTrail.ORIGIN_TYPE_FIELD_NAME, LoggingAuditTrail.REST_ORIGIN_FIELD_VALUE)
.put(LoggingAuditTrail.ORIGIN_ADDRESS_FIELD_NAME, NetworkAddress.format(address))
.put(LoggingAuditTrail.REQUEST_METHOD_FIELD_NAME, request.method().toString())
.put(LoggingAuditTrail.REQUEST_BODY_FIELD_NAME,
includeRequestBody && Strings.hasLength(expectedMessage) ? expectedMessage : null)
.put(LoggingAuditTrail.REQUEST_ID_FIELD_NAME, requestId)
@ -346,6 +347,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.PRINCIPAL_FIELD_NAME, mockToken.principal())
.put(LoggingAuditTrail.ORIGIN_TYPE_FIELD_NAME, LoggingAuditTrail.REST_ORIGIN_FIELD_VALUE)
.put(LoggingAuditTrail.ORIGIN_ADDRESS_FIELD_NAME, NetworkAddress.format(address))
.put(LoggingAuditTrail.REQUEST_METHOD_FIELD_NAME, request.method().toString())
.put(LoggingAuditTrail.REQUEST_BODY_FIELD_NAME,
includeRequestBody && Strings.hasLength(expectedMessage) ? expectedMessage : null)
.put(LoggingAuditTrail.REQUEST_ID_FIELD_NAME, requestId)
@ -386,6 +388,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.PRINCIPAL_FIELD_NAME, null)
.put(LoggingAuditTrail.ORIGIN_TYPE_FIELD_NAME, LoggingAuditTrail.REST_ORIGIN_FIELD_VALUE)
.put(LoggingAuditTrail.ORIGIN_ADDRESS_FIELD_NAME, NetworkAddress.format(address))
.put(LoggingAuditTrail.REQUEST_METHOD_FIELD_NAME, request.method().toString())
.put(LoggingAuditTrail.REQUEST_BODY_FIELD_NAME,
includeRequestBody && Strings.hasLength(expectedMessage) ? expectedMessage : null)
.put(LoggingAuditTrail.REQUEST_ID_FIELD_NAME, requestId)
@ -468,6 +471,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.ORIGIN_ADDRESS_FIELD_NAME, NetworkAddress.format(address))
.put(LoggingAuditTrail.PRINCIPAL_FIELD_NAME, mockToken.principal())
.put(LoggingAuditTrail.ACTION_FIELD_NAME, null)
.put(LoggingAuditTrail.REQUEST_METHOD_FIELD_NAME, request.method().toString())
.put(LoggingAuditTrail.REQUEST_BODY_FIELD_NAME,
includeRequestBody && Strings.hasLength(expectedMessage) ? expectedMessage : null)
.put(LoggingAuditTrail.REQUEST_ID_FIELD_NAME, requestId)
@ -627,6 +631,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.EVENT_ACTION_FIELD_NAME, "tampered_request")
.put(LoggingAuditTrail.ORIGIN_TYPE_FIELD_NAME, LoggingAuditTrail.REST_ORIGIN_FIELD_VALUE)
.put(LoggingAuditTrail.ORIGIN_ADDRESS_FIELD_NAME, NetworkAddress.format(address))
.put(LoggingAuditTrail.REQUEST_METHOD_FIELD_NAME, request.method().toString())
.put(LoggingAuditTrail.REQUEST_BODY_FIELD_NAME,
includeRequestBody && Strings.hasLength(expectedMessage) ? expectedMessage : null)
.put(LoggingAuditTrail.REQUEST_ID_FIELD_NAME, requestId)
@ -891,6 +896,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
.put(LoggingAuditTrail.REALM_FIELD_NAME, realm)
.put(LoggingAuditTrail.ORIGIN_TYPE_FIELD_NAME, LoggingAuditTrail.REST_ORIGIN_FIELD_VALUE)
.put(LoggingAuditTrail.ORIGIN_ADDRESS_FIELD_NAME, NetworkAddress.format(address))
.put(LoggingAuditTrail.REQUEST_METHOD_FIELD_NAME, request.method().toString())
.put(LoggingAuditTrail.REQUEST_BODY_FIELD_NAME,
includeRequestBody && Strings.hasLength(expectedMessage) ? expectedMessage : null)
.put(LoggingAuditTrail.REQUEST_ID_FIELD_NAME, requestId)
@ -1080,6 +1086,7 @@ public class LoggingAuditTrailTests extends ESTestCase {
}
builder.withRemoteAddress(remoteAddress);
builder.withParams(params);
builder.withMethod(randomFrom(RestRequest.Method.values()));
return new Tuple<>(content, builder.build());
}