Skip to content

Commit 300c61d

Browse files
authored
Add "request.id" to file audit logs (#35536)
This generates a synthesized "id" for each incoming request that is included in the audit logs (file only). This id can be used to correlate events for the same request (e.g. authentication success with access granted). This request.id is specific to the audit logs and is not used for any other purpose The request.id is consistent across nodes if a single request requires execution on multiple nodes (e.g. search across multiple shards).
1 parent f57e429 commit 300c61d

21 files changed

+921
-641
lines changed

x-pack/plugin/core/src/main/config/log4j2.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ appender.audit_rolling.layout.pattern = {\
2323
%varsNotEmpty{, "url.path":"%enc{%map{url.path}}{JSON}"}\
2424
%varsNotEmpty{, "url.query":"%enc{%map{url.query}}{JSON}"}\
2525
%varsNotEmpty{, "request.body":"%enc{%map{request.body}}{JSON}"}\
26+
%varsNotEmpty{, "request.id":"%enc{%map{request.id}}{JSON}"}\
2627
%varsNotEmpty{, "action":"%enc{%map{action}}{JSON}"}\
2728
%varsNotEmpty{, "request.name":"%enc{%map{request.name}}{JSON}"}\
2829
%varsNotEmpty{, "indices":%map{indices}}\
@@ -50,6 +51,7 @@ appender.audit_rolling.layout.pattern = {\
5051
# "url.path" the URI component between the port and the query string; it is percent (URL) encoded
5152
# "url.query" the URI component after the path and before the fragment; it is percent (URL) encoded
5253
# "request.body" the content of the request body entity, JSON escaped
54+
# "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
5355
# "action" an action is the most granular operation that is authorized and this identifies it in a namespaced way (internal)
5456
# "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)
5557
# "indices" the array of indices that the "action" is acting upon

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/interceptor/IndicesAliasesRequestInterceptor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.xpack.core.security.authz.permission.Role;
2020
import org.elasticsearch.xpack.core.security.support.Exceptions;
2121
import org.elasticsearch.xpack.security.audit.AuditTrailService;
22+
import org.elasticsearch.xpack.security.audit.AuditUtil;
2223

2324
import java.util.HashMap;
2425
import java.util.Map;
@@ -70,7 +71,8 @@ public void intercept(IndicesAliasesRequest request, Authentication authenticati
7071
permissionsMap.computeIfAbsent(alias, userPermissions.indices()::allowedActionsMatcher);
7172
if (Operations.subsetOf(aliasPermissions, indexPermissions) == false) {
7273
// TODO we've already audited a access granted event so this is going to look ugly
73-
auditTrailService.accessDenied(authentication, action, request, userPermissions.names());
74+
auditTrailService.accessDenied(AuditUtil.extractRequestId(threadContext), authentication, action, request,
75+
userPermissions.names());
7476
throw Exceptions.authorizationError("Adding an alias is not allowed when the alias " +
7577
"has more permissions than any of the indices");
7678
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/interceptor/ResizeRequestInterceptor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.xpack.core.security.authz.permission.Role;
2222
import org.elasticsearch.xpack.core.security.support.Exceptions;
2323
import org.elasticsearch.xpack.security.audit.AuditTrailService;
24+
import static org.elasticsearch.xpack.security.audit.AuditUtil.extractRequestId;
2425

2526
public final class ResizeRequestInterceptor extends AbstractComponent implements RequestInterceptor<ResizeRequest> {
2627

@@ -60,7 +61,7 @@ public void intercept(ResizeRequest request, Authentication authentication, Role
6061
userPermissions.indices().allowedActionsMatcher(request.getTargetIndexRequest().index());
6162
if (Operations.subsetOf(targetIndexPermissions, sourceIndexPermissions) == false) {
6263
// TODO we've already audited a access granted event so this is going to look ugly
63-
auditTrailService.accessDenied(authentication, action, request, userPermissions.names());
64+
auditTrailService.accessDenied(extractRequestId(threadContext), authentication, action, request, userPermissions.names());
6465
throw Exceptions.authorizationError("Resizing an index is not allowed when the target index " +
6566
"has more permissions than the source index");
6667
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrail.java

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,50 @@ public interface AuditTrail {
1818

1919
String name();
2020

21-
void authenticationSuccess(String realm, User user, RestRequest request);
21+
void authenticationSuccess(String requestId, String realm, User user, RestRequest request);
2222

23-
void authenticationSuccess(String realm, User user, String action, TransportMessage message);
23+
void authenticationSuccess(String requestId, String realm, User user, String action, TransportMessage message);
2424

25-
void anonymousAccessDenied(String action, TransportMessage message);
25+
void anonymousAccessDenied(String requestId, String action, TransportMessage message);
2626

27-
void anonymousAccessDenied(RestRequest request);
27+
void anonymousAccessDenied(String requestId, RestRequest request);
2828

29-
void authenticationFailed(RestRequest request);
29+
void authenticationFailed(String requestId, RestRequest request);
3030

31-
void authenticationFailed(String action, TransportMessage message);
31+
void authenticationFailed(String requestId, String action, TransportMessage message);
3232

33-
void authenticationFailed(AuthenticationToken token, String action, TransportMessage message);
33+
void authenticationFailed(String requestId, AuthenticationToken token, String action, TransportMessage message);
3434

35-
void authenticationFailed(AuthenticationToken token, RestRequest request);
35+
void authenticationFailed(String requestId, AuthenticationToken token, RestRequest request);
3636

37-
void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message);
37+
void authenticationFailed(String requestId, String realm, AuthenticationToken token, String action, TransportMessage message);
3838

39-
void authenticationFailed(String realm, AuthenticationToken token, RestRequest request);
39+
void authenticationFailed(String requestId, String realm, AuthenticationToken token, RestRequest request);
4040

41-
void accessGranted(Authentication authentication, String action, TransportMessage message, String[] roleNames);
41+
void accessGranted(String requestId, Authentication authentication, String action, TransportMessage message, String[] roleNames);
4242

43-
void accessDenied(Authentication authentication, String action, TransportMessage message, String[] roleNames);
43+
void accessDenied(String requestId, Authentication authentication, String action, TransportMessage message, String[] roleNames);
4444

45-
void tamperedRequest(RestRequest request);
45+
void tamperedRequest(String requestId, RestRequest request);
4646

47-
void tamperedRequest(String action, TransportMessage message);
47+
void tamperedRequest(String requestId, String action, TransportMessage message);
4848

49-
void tamperedRequest(User user, String action, TransportMessage request);
49+
void tamperedRequest(String requestId, User user, String action, TransportMessage request);
5050

51+
/**
52+
* The {@link #connectionGranted(InetAddress, String, SecurityIpFilterRule)} and
53+
* {@link #connectionDenied(InetAddress, String, SecurityIpFilterRule)} methods do not have a requestId because they related to a
54+
* potentially long-lived TCP connection, not a single request. For both Transport and Rest connections, a single connection
55+
* granted/denied event is generated even if that connection is used for multiple Elasticsearch actions (potentially as different users)
56+
*/
5157
void connectionGranted(InetAddress inetAddress, String profile, SecurityIpFilterRule rule);
5258

5359
void connectionDenied(InetAddress inetAddress, String profile, SecurityIpFilterRule rule);
5460

55-
void runAsGranted(Authentication authentication, String action, TransportMessage message, String[] roleNames);
61+
void runAsGranted(String requestId, Authentication authentication, String action, TransportMessage message, String[] roleNames);
5662

57-
void runAsDenied(Authentication authentication, String action, TransportMessage message, String[] roleNames);
63+
void runAsDenied(String requestId, Authentication authentication, String action, TransportMessage message, String[] roleNames);
64+
65+
void runAsDenied(String requestId, Authentication authentication, RestRequest request, String[] roleNames);
5866

59-
void runAsDenied(Authentication authentication, RestRequest request, String[] roleNames);
6067
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,136 +39,136 @@ public List<AuditTrail> getAuditTrails() {
3939
}
4040

4141
@Override
42-
public void authenticationSuccess(String realm, User user, RestRequest request) {
42+
public void authenticationSuccess(String requestId, String realm, User user, RestRequest request) {
4343
if (licenseState.isAuditingAllowed()) {
4444
for (AuditTrail auditTrail : auditTrails) {
45-
auditTrail.authenticationSuccess(realm, user, request);
45+
auditTrail.authenticationSuccess(requestId, realm, user, request);
4646
}
4747
}
4848
}
4949

5050
@Override
51-
public void authenticationSuccess(String realm, User user, String action, TransportMessage message) {
51+
public void authenticationSuccess(String requestId, String realm, User user, String action, TransportMessage message) {
5252
if (licenseState.isAuditingAllowed()) {
5353
for (AuditTrail auditTrail : auditTrails) {
54-
auditTrail.authenticationSuccess(realm, user, action, message);
54+
auditTrail.authenticationSuccess(requestId, realm, user, action, message);
5555
}
5656
}
5757
}
5858

5959
@Override
60-
public void anonymousAccessDenied(String action, TransportMessage message) {
60+
public void anonymousAccessDenied(String requestId, String action, TransportMessage message) {
6161
if (licenseState.isAuditingAllowed()) {
6262
for (AuditTrail auditTrail : auditTrails) {
63-
auditTrail.anonymousAccessDenied(action, message);
63+
auditTrail.anonymousAccessDenied(requestId, action, message);
6464
}
6565
}
6666
}
6767

6868
@Override
69-
public void anonymousAccessDenied(RestRequest request) {
69+
public void anonymousAccessDenied(String requestId, RestRequest request) {
7070
if (licenseState.isAuditingAllowed()) {
7171
for (AuditTrail auditTrail : auditTrails) {
72-
auditTrail.anonymousAccessDenied(request);
72+
auditTrail.anonymousAccessDenied(requestId, request);
7373
}
7474
}
7575
}
7676

7777
@Override
78-
public void authenticationFailed(RestRequest request) {
78+
public void authenticationFailed(String requestId, RestRequest request) {
7979
if (licenseState.isAuditingAllowed()) {
8080
for (AuditTrail auditTrail : auditTrails) {
81-
auditTrail.authenticationFailed(request);
81+
auditTrail.authenticationFailed(requestId, request);
8282
}
8383
}
8484
}
8585

8686
@Override
87-
public void authenticationFailed(String action, TransportMessage message) {
87+
public void authenticationFailed(String requestId, String action, TransportMessage message) {
8888
if (licenseState.isAuditingAllowed()) {
8989
for (AuditTrail auditTrail : auditTrails) {
90-
auditTrail.authenticationFailed(action, message);
90+
auditTrail.authenticationFailed(requestId, action, message);
9191
}
9292
}
9393
}
9494

9595
@Override
96-
public void authenticationFailed(AuthenticationToken token, String action, TransportMessage message) {
96+
public void authenticationFailed(String requestId, AuthenticationToken token, String action, TransportMessage message) {
9797
if (licenseState.isAuditingAllowed()) {
9898
for (AuditTrail auditTrail : auditTrails) {
99-
auditTrail.authenticationFailed(token, action, message);
99+
auditTrail.authenticationFailed(requestId, token, action, message);
100100
}
101101
}
102102
}
103103

104104
@Override
105-
public void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message) {
105+
public void authenticationFailed(String requestId, String realm, AuthenticationToken token, String action, TransportMessage message) {
106106
if (licenseState.isAuditingAllowed()) {
107107
for (AuditTrail auditTrail : auditTrails) {
108-
auditTrail.authenticationFailed(realm, token, action, message);
108+
auditTrail.authenticationFailed(requestId, realm, token, action, message);
109109
}
110110
}
111111
}
112112

113113
@Override
114-
public void authenticationFailed(AuthenticationToken token, RestRequest request) {
114+
public void authenticationFailed(String requestId, AuthenticationToken token, RestRequest request) {
115115
if (licenseState.isAuditingAllowed()) {
116116
for (AuditTrail auditTrail : auditTrails) {
117-
auditTrail.authenticationFailed(token, request);
117+
auditTrail.authenticationFailed(requestId, token, request);
118118
}
119119
}
120120
}
121121

122122
@Override
123-
public void authenticationFailed(String realm, AuthenticationToken token, RestRequest request) {
123+
public void authenticationFailed(String requestId, String realm, AuthenticationToken token, RestRequest request) {
124124
if (licenseState.isAuditingAllowed()) {
125125
for (AuditTrail auditTrail : auditTrails) {
126-
auditTrail.authenticationFailed(realm, token, request);
126+
auditTrail.authenticationFailed(requestId, realm, token, request);
127127
}
128128
}
129129
}
130130

131131
@Override
132-
public void accessGranted(Authentication authentication, String action, TransportMessage message, String[] roleNames) {
132+
public void accessGranted(String requestId, Authentication authentication, String action, TransportMessage msg, String[] roleNames) {
133133
if (licenseState.isAuditingAllowed()) {
134134
for (AuditTrail auditTrail : auditTrails) {
135-
auditTrail.accessGranted(authentication, action, message, roleNames);
135+
auditTrail.accessGranted(requestId, authentication, action, msg, roleNames);
136136
}
137137
}
138138
}
139139

140140
@Override
141-
public void accessDenied(Authentication authentication, String action, TransportMessage message, String[] roleNames) {
141+
public void accessDenied(String requestId, Authentication authentication, String action, TransportMessage message, String[] roleNames) {
142142
if (licenseState.isAuditingAllowed()) {
143143
for (AuditTrail auditTrail : auditTrails) {
144-
auditTrail.accessDenied(authentication, action, message, roleNames);
144+
auditTrail.accessDenied(requestId, authentication, action, message, roleNames);
145145
}
146146
}
147147
}
148148

149149
@Override
150-
public void tamperedRequest(RestRequest request) {
150+
public void tamperedRequest(String requestId, RestRequest request) {
151151
if (licenseState.isAuditingAllowed()) {
152152
for (AuditTrail auditTrail : auditTrails) {
153-
auditTrail.tamperedRequest(request);
153+
auditTrail.tamperedRequest(requestId, request);
154154
}
155155
}
156156
}
157157

158158
@Override
159-
public void tamperedRequest(String action, TransportMessage message) {
159+
public void tamperedRequest(String requestId, String action, TransportMessage message) {
160160
if (licenseState.isAuditingAllowed()) {
161161
for (AuditTrail auditTrail : auditTrails) {
162-
auditTrail.tamperedRequest(action, message);
162+
auditTrail.tamperedRequest(requestId, action, message);
163163
}
164164
}
165165
}
166166

167167
@Override
168-
public void tamperedRequest(User user, String action, TransportMessage request) {
168+
public void tamperedRequest(String requestId, User user, String action, TransportMessage request) {
169169
if (licenseState.isAuditingAllowed()) {
170170
for (AuditTrail auditTrail : auditTrails) {
171-
auditTrail.tamperedRequest(user, action, request);
171+
auditTrail.tamperedRequest(requestId, user, action, request);
172172
}
173173
}
174174
}
@@ -192,28 +192,28 @@ public void connectionDenied(InetAddress inetAddress, String profile, SecurityIp
192192
}
193193

194194
@Override
195-
public void runAsGranted(Authentication authentication, String action, TransportMessage message, String[] roleNames) {
195+
public void runAsGranted(String requestId, Authentication authentication, String action, TransportMessage message, String[] roleNames) {
196196
if (licenseState.isAuditingAllowed()) {
197197
for (AuditTrail auditTrail : auditTrails) {
198-
auditTrail.runAsGranted(authentication, action, message, roleNames);
198+
auditTrail.runAsGranted(requestId, authentication, action, message, roleNames);
199199
}
200200
}
201201
}
202202

203203
@Override
204-
public void runAsDenied(Authentication authentication, String action, TransportMessage message, String[] roleNames) {
204+
public void runAsDenied(String requestId, Authentication authentication, String action, TransportMessage message, String[] roleNames) {
205205
if (licenseState.isAuditingAllowed()) {
206206
for (AuditTrail auditTrail : auditTrails) {
207-
auditTrail.runAsDenied(authentication, action, message, roleNames);
207+
auditTrail.runAsDenied(requestId, authentication, action, message, roleNames);
208208
}
209209
}
210210
}
211211

212212
@Override
213-
public void runAsDenied(Authentication authentication, RestRequest request, String[] roleNames) {
213+
public void runAsDenied(String requestId, Authentication authentication, RestRequest request, String[] roleNames) {
214214
if (licenseState.isAuditingAllowed()) {
215215
for (AuditTrail auditTrail : auditTrails) {
216-
auditTrail.runAsDenied(authentication, request, roleNames);
216+
auditTrail.runAsDenied(requestId, authentication, request, roleNames);
217217
}
218218
}
219219
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditUtil.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
package org.elasticsearch.xpack.security.audit;
77

88
import org.elasticsearch.action.IndicesRequest;
9+
import org.elasticsearch.common.Strings;
10+
import org.elasticsearch.common.UUIDs;
11+
import org.elasticsearch.common.util.concurrent.ThreadContext;
912
import org.elasticsearch.common.xcontent.XContentHelper;
1013
import org.elasticsearch.rest.RestRequest;
1114
import org.elasticsearch.transport.TransportMessage;
@@ -17,6 +20,8 @@
1720

1821
public class AuditUtil {
1922

23+
private static final String AUDIT_REQUEST_ID = "_xpack_audit_request_id";
24+
2025
public static String restRequestContent(RestRequest request) {
2126
if (request.hasContent()) {
2227
try {
@@ -38,4 +43,34 @@ public static Set<String> indices(TransportMessage message) {
3843
private static Set<String> arrayToSetOrNull(String[] indices) {
3944
return indices == null ? null : new HashSet<>(Arrays.asList(indices));
4045
}
46+
47+
public static String generateRequestId(ThreadContext threadContext) {
48+
return generateRequestId(threadContext, true);
49+
}
50+
51+
public static String getOrGenerateRequestId(ThreadContext threadContext) {
52+
final String requestId = extractRequestId(threadContext);
53+
if (Strings.isEmpty(requestId)) {
54+
return generateRequestId(threadContext, false);
55+
}
56+
return requestId;
57+
}
58+
59+
private static String generateRequestId(ThreadContext threadContext, boolean checkExisting) {
60+
if (checkExisting) {
61+
final String existing = extractRequestId(threadContext);
62+
if (existing != null) {
63+
throw new IllegalStateException("Cannot generate a new audit request id - existing id ["
64+
+ existing + "] already registered");
65+
}
66+
}
67+
final String requestId = UUIDs.randomBase64UUID();
68+
// Store as a header (not transient) so that it is passed over the network if this request requires execution on other nodes
69+
threadContext.putHeader(AUDIT_REQUEST_ID, requestId);
70+
return requestId;
71+
}
72+
73+
public static String extractRequestId(ThreadContext threadContext) {
74+
return threadContext.getHeader(AUDIT_REQUEST_ID);
75+
}
4176
}

0 commit comments

Comments
 (0)