Skip to content

Commit d5a4582

Browse files
committed
Adding tracing support for the authenticate action by wrapping the authenticator.authenticate call in AuthenticatorChain with a startTrace/stopTrace action.
In synchronous contexts, spans are stored as ThreadLocals under the hood and the currently active span look up (needed to end a span, append metadata, or spawn a child span) can be implicit. However, to track a span across threads as ES requires, we need to make `Authenticator.Context` implement `Traceable` which defines a random, unique `spanId` for each Context object, enabling us to uniquely locate the associated span regardless of which thread is currently executing.
1 parent c591e99 commit d5a4582

File tree

7 files changed

+48
-9
lines changed

7 files changed

+48
-9
lines changed

modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,11 @@ private static Setting<String> concreteAgentSetting(String namespace, String qua
285285
NodeScope
286286
);
287287

288+
// not a setting to reduce the footprint of this change
289+
public static final List<String> TELEMETRY_TRACING_SANITIZE_FIELD_NAMES_EXCLUDES = List.of(
290+
"security.authenticator.type"
291+
);
292+
288293
public static final Setting<Boolean> TELEMETRY_TRACING_ENABLED_SETTING = Setting.boolSetting(
289294
TELEMETRY_SETTING_PREFIX + "tracing.enabled",
290295
false,

modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/tracing/APMTracer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public APMTracer(Settings settings) {
9090
this.labelFilters = APMAgentSettings.TELEMETRY_TRACING_SANITIZE_FIELD_NAMES.get(settings);
9191

9292
this.filterAutomaton = buildAutomaton(includeNames, excludeNames);
93-
this.labelFilterAutomaton = buildAutomaton(labelFilters, List.of());
93+
this.labelFilterAutomaton = buildAutomaton(labelFilters, APMAgentSettings.TELEMETRY_TRACING_SANITIZE_FIELD_NAMES_EXCLUDES);
9494
this.enabled = APMAgentSettings.TELEMETRY_TRACING_ENABLED_SETTING.get(settings);
9595
}
9696

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ Collection<Object> createComponents(
10851085
serviceAccountService,
10861086
operatorPrivilegesService.get(),
10871087
customApiKeyAuthenticator,
1088-
telemetryProvider.getMeterRegistry()
1088+
telemetryProvider
10891089
)
10901090
);
10911091
components.add(authcService.get());

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import org.elasticsearch.core.TimeValue;
2323
import org.elasticsearch.http.HttpPreRequest;
2424
import org.elasticsearch.node.Node;
25+
import org.elasticsearch.telemetry.TelemetryProvider;
2526
import org.elasticsearch.telemetry.metric.MeterRegistry;
27+
import org.elasticsearch.telemetry.tracing.Tracer;
2628
import org.elasticsearch.threadpool.ThreadPool;
2729
import org.elasticsearch.transport.TransportRequest;
2830
import org.elasticsearch.xpack.core.security.authc.Authentication;
@@ -94,7 +96,7 @@ public AuthenticationService(
9496
ServiceAccountService serviceAccountService,
9597
OperatorPrivilegesService operatorPrivilegesService,
9698
CustomApiKeyAuthenticator customApiKeyAuthenticator,
97-
MeterRegistry meterRegistry
99+
TelemetryProvider telemetryProvider
98100
) {
99101
this.realms = realms;
100102
this.auditTrailService = auditTrailService;
@@ -108,13 +110,15 @@ public AuthenticationService(
108110
} else {
109111
this.lastSuccessfulAuthCache = null;
110112
}
111-
113+
MeterRegistry meterRegistry = telemetryProvider.getMeterRegistry();
114+
Tracer tracer = telemetryProvider.getTracer();
112115
final String nodeName = Node.NODE_NAME_SETTING.get(settings);
113116
this.authenticatorChain = new AuthenticatorChain(
114117
settings,
115118
operatorPrivilegesService,
116119
anonymousUser,
117120
new AuthenticationContextSerializer(),
121+
tracer,
118122
new ServiceAccountAuthenticator(serviceAccountService, nodeName, meterRegistry),
119123
new OAuth2TokenAuthenticator(tokenService, meterRegistry),
120124
new PluggableApiKeyAuthenticator(customApiKeyAuthenticator),

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Authenticator.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99

1010
import org.elasticsearch.ElasticsearchSecurityException;
1111
import org.elasticsearch.action.ActionListener;
12+
import org.elasticsearch.common.Randomness;
1213
import org.elasticsearch.common.Strings;
14+
import org.elasticsearch.common.UUIDs;
1315
import org.elasticsearch.common.settings.SecureString;
1416
import org.elasticsearch.common.util.concurrent.ThreadContext;
1517
import org.elasticsearch.core.Nullable;
18+
import org.elasticsearch.telemetry.tracing.Traceable;
1619
import org.elasticsearch.xpack.core.security.authc.Authentication;
1720
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
1821
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
@@ -90,7 +93,7 @@ static SecureString extractApiKeyFromHeader(ThreadContext threadContext) {
9093
* the next {@link Authenticator} is tried.
9194
* The extracted tokens are all appended with {@link #addAuthenticationToken(AuthenticationToken)}.
9295
*/
93-
class Context implements Closeable {
96+
class Context implements Closeable, Traceable {
9497
private final ThreadContext threadContext;
9598
private final AuthenticationService.AuditableRequest request;
9699
private final User fallbackUser;
@@ -104,6 +107,7 @@ class Context implements Closeable {
104107
private SecureString apiKeyString = null;
105108
private List<Realm> defaultOrderedRealmList = null;
106109
private List<Realm> unlicensedRealms = null;
110+
private String requestId;
107111

108112
/**
109113
* Context constructor that provides the authentication token directly as an argument.
@@ -129,6 +133,7 @@ class Context implements Closeable {
129133
// if handleNullToken is false, fallbackUser and allowAnonymous are irrelevant
130134
this.fallbackUser = null;
131135
this.allowAnonymous = false;
136+
this.requestId = UUIDs.randomBase64UUID(Randomness.get());;
132137
}
133138

134139
/**
@@ -149,6 +154,7 @@ public Context(
149154
this.fallbackUser = fallbackUser;
150155
this.allowAnonymous = allowAnonymous;
151156
this.realms = realms;
157+
this.requestId = UUIDs.randomBase64UUID(Randomness.get());;
152158
}
153159

154160
public ThreadContext getThreadContext() {
@@ -241,5 +247,10 @@ public void addUnsuccessfulMessageToMetadata(final ElasticsearchSecurityExceptio
241247
ese.addMetadata("es.additional_unsuccessful_credentials", getUnsuccessfulMessages());
242248
}
243249
}
250+
251+
@Override
252+
public String getSpanId() {
253+
return requestId;
254+
}
244255
}
245256
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticatorChain.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.common.settings.Settings;
1515
import org.elasticsearch.common.util.concurrent.ThreadContext;
1616
import org.elasticsearch.node.Node;
17+
import org.elasticsearch.telemetry.tracing.Tracer;
1718
import org.elasticsearch.xpack.core.common.IteratingActionListener;
1819
import org.elasticsearch.xpack.core.security.authc.Authentication;
1920
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
@@ -26,6 +27,7 @@
2627
import org.elasticsearch.xpack.core.security.user.User;
2728
import org.elasticsearch.xpack.security.operator.OperatorPrivileges.OperatorPrivilegesService;
2829

30+
import java.util.HashMap;
2931
import java.util.List;
3032
import java.util.Map;
3133
import java.util.function.BiConsumer;
@@ -46,12 +48,14 @@ class AuthenticatorChain {
4648
private final AuthenticationContextSerializer authenticationSerializer;
4749
private final RealmsAuthenticator realmsAuthenticator;
4850
private final List<Authenticator> allAuthenticators;
51+
private final Tracer tracer;
4952

5053
AuthenticatorChain(
5154
Settings settings,
5255
OperatorPrivilegesService operatorPrivilegesService,
5356
AnonymousUser anonymousUser,
5457
AuthenticationContextSerializer authenticationSerializer,
58+
Tracer tracer,
5559
ServiceAccountAuthenticator serviceAccountAuthenticator,
5660
OAuth2TokenAuthenticator oAuth2TokenAuthenticator,
5761
PluggableApiKeyAuthenticator pluggableApiKeyAuthenticator,
@@ -65,6 +69,7 @@ class AuthenticatorChain {
6569
this.isAnonymousUserEnabled = AnonymousUser.isAnonymousEnabled(settings);
6670
this.authenticationSerializer = authenticationSerializer;
6771
this.realmsAuthenticator = realmsAuthenticator;
72+
this.tracer = tracer;
6873
this.allAuthenticators = List.of(
6974
serviceAccountAuthenticator,
7075
oAuth2TokenAuthenticator,
@@ -123,7 +128,7 @@ private void doAuthenticate(Authenticator.Context context, ActionListener<Authen
123128
}
124129
}
125130
}),
126-
getAuthenticatorConsumer(context),
131+
getAuthenticatorConsumer(context, tracer),
127132
allAuthenticators,
128133
context.getThreadContext(),
129134
Function.identity(),
@@ -133,7 +138,7 @@ private void doAuthenticate(Authenticator.Context context, ActionListener<Authen
133138
}
134139

135140
private static BiConsumer<Authenticator, ActionListener<AuthenticationResult<Authentication>>> getAuthenticatorConsumer(
136-
Authenticator.Context context
141+
Authenticator.Context context, Tracer tracer
137142
) {
138143
return (authenticator, listener) -> {
139144
if (context.shouldExtractCredentials()) {
@@ -171,7 +176,7 @@ private static BiConsumer<Authenticator, ActionListener<AuthenticationResult<Aut
171176
listener.onFailure(e);
172177
};
173178

174-
authenticator.authenticate(context, ActionListener.wrap(result -> {
179+
ActionListener<AuthenticationResult<Authentication>> authResultListener = ActionListener.wrap(result -> {
175180
if (result.getStatus() == AuthenticationResult.Status.TERMINATE) {
176181
onFailure.accept(result.getException());
177182
return;
@@ -180,10 +185,22 @@ private static BiConsumer<Authenticator, ActionListener<AuthenticationResult<Aut
180185
context.addUnsuccessfulMessage(authenticator.name() + ": " + result.getMessage());
181186
}
182187
listener.onResponse(result);
183-
}, onFailure));
188+
}, onFailure);
189+
190+
ActionListener<AuthenticationResult<Authentication>> afterAuthResultListener = ActionListener.runAfter(authResultListener,
191+
() -> tracer.stopTrace(context));
192+
193+
tracer.startTrace(context.getThreadContext(), context, "authenticate", addUsefulMetadata(authenticator));
194+
authenticator.authenticate(context, afterAuthResultListener);
184195
};
185196
}
186197

198+
private static Map<String, Object> addUsefulMetadata(Authenticator authenticator) {
199+
Map<String, Object> attribs = new HashMap<>();
200+
attribs.put("security.authenticator.type", authenticator.name());
201+
return attribs;
202+
}
203+
187204
// Package private for test
188205
void maybeLookupRunAsUser(Authenticator.Context context, Authentication authentication, ActionListener<Authentication> listener) {
189206
if (false == runAsEnabled) {

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticatorChainTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.common.util.concurrent.ThreadContext;
2020
import org.elasticsearch.core.Tuple;
2121
import org.elasticsearch.node.Node;
22+
import org.elasticsearch.telemetry.tracing.Tracer;
2223
import org.elasticsearch.test.ESTestCase;
2324
import org.elasticsearch.test.MockLog;
2425
import org.elasticsearch.xpack.core.security.action.apikey.ApiKey;
@@ -103,6 +104,7 @@ public void init() {
103104
operatorPrivilegesService,
104105
anonymousUser,
105106
authenticationContextSerializer,
107+
Tracer.NOOP,
106108
serviceAccountAuthenticator,
107109
oAuth2TokenAuthenticator,
108110
pluggableApiKeyAuthenticator,

0 commit comments

Comments
 (0)