Skip to content

Commit fb26c72

Browse files
committed
Publish http.client_ip address
1 parent e2105b9 commit fb26c72

11 files changed

Lines changed: 194 additions & 83 deletions

File tree

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE, REQUEST
3636
extends ServerDecorator {
3737

3838
private static final Logger log = LoggerFactory.getLogger(HttpServerDecorator.class);
39+
private static final int UNSET_PORT = 0;
3940

4041
public static final String DD_SPAN_ATTRIBUTE = "datadog.span";
4142
public static final String DD_DISPATCH_SPAN_ATTRIBUTE = "datadog.span.dispatch";
@@ -176,36 +177,40 @@ public AgentSpan onRequest(
176177
}
177178

178179
String ip = null;
180+
int port = UNSET_PORT;
179181
if (connection != null) {
180182
ip = peerHostIP(connection);
181-
final int port = peerPort(connection);
182-
if (ip != null) {
183-
if (ip.indexOf(':') > 0) {
184-
span.setTag(Tags.PEER_HOST_IPV6, ip);
185-
} else {
186-
span.setTag(Tags.PEER_HOST_IPV4, ip);
187-
}
188-
}
189-
setPeerPort(span, port);
190-
Flow<Void> flow = callIGCallbackSocketAddress(span, ip, port);
191-
if (flow.getAction().isBlocking()) {
192-
span.markForBlocking();
193-
}
183+
port = peerPort(connection);
194184
}
195185

186+
String inferredAddressStr = null;
196187
if (config.isTraceClientIpResolverEnabled()) {
197-
InetAddress inferredAddress = ClientIpAddressResolver.doResolve(context);
188+
InetAddress inferredAddress = ClientIpAddressResolver.resolve(context);
198189
// As a fallback, if no IP was resolved, the peer IP address should be checked
199190
// to see if it is public and used as the resolved IP if it is.
200191
// If no public IP address, then a private IP address should reported as a fall back.
201192
if (inferredAddress == null && ip != null) {
202193
inferredAddress = ClientIpAddressResolver.parseIpAddress(ip);
203194
}
204195
if (inferredAddress != null) {
205-
span.setTag(Tags.HTTP_CLIENT_IP, inferredAddress.getHostAddress());
196+
inferredAddressStr = inferredAddress.getHostAddress();
197+
span.setTag(Tags.HTTP_CLIENT_IP, inferredAddressStr);
206198
}
207199
}
208200

201+
if (ip != null) {
202+
if (ip.indexOf(':') > 0) {
203+
span.setTag(Tags.PEER_HOST_IPV6, ip);
204+
} else {
205+
span.setTag(Tags.PEER_HOST_IPV4, ip);
206+
}
207+
}
208+
setPeerPort(span, port);
209+
Flow<Void> flow = callIGCallbackAddressAndPort(span, ip, port, inferredAddressStr);
210+
if (flow.getAction().isBlocking()) {
211+
span.markForBlocking();
212+
}
213+
209214
return span;
210215
}
211216

@@ -362,17 +367,32 @@ private void onRequestEndForInstrumentationGateway(@Nonnull final AgentSpan span
362367
}
363368
}
364369

365-
private Flow<Void> callIGCallbackSocketAddress(
366-
@Nonnull final AgentSpan span, @Nonnull final String ip, final int port) {
370+
private Flow<Void> callIGCallbackAddressAndPort(
371+
@Nonnull final AgentSpan span,
372+
final String ip,
373+
final int port,
374+
final String inferredClientIp) {
367375
CallbackProvider cbp = tracer().getCallbackProvider(RequestContextSlot.APPSEC);
368-
if (cbp == null || (ip == null && port == UNSET_PORT)) {
376+
if (cbp == null || (ip == null && inferredClientIp == null && port == UNSET_PORT)) {
369377
return Flow.ResultFlow.empty();
370378
}
371379
RequestContext ctx = span.getRequestContext();
372-
if (ctx != null) {
380+
if (ctx == null) {
381+
return Flow.ResultFlow.empty();
382+
}
383+
384+
if (inferredClientIp != null) {
385+
BiFunction<RequestContext, String, Flow<Void>> inferredAddrCallback =
386+
cbp.getCallback(EVENTS.requestInferredClientAddress());
387+
if (inferredAddrCallback != null) {
388+
inferredAddrCallback.apply(ctx, inferredClientIp);
389+
}
390+
}
391+
392+
if (ip != null || port != UNSET_PORT) {
373393
TriFunction<RequestContext, String, Integer, Flow<Void>> addrCallback =
374394
cbp.getCallback(EVENTS.requestClientSocketAddress());
375-
if (null != addrCallback) {
395+
if (addrCallback != null) {
376396
return addrCallback.apply(ctx, ip != null ? ip : "0.0.0.0", port);
377397
}
378398
}

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/http/ClientIpAddressResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static InetAddress resolve(AgentSpan.Context.Extracted context) {
3737
}
3838
}
3939

40-
public static InetAddress doResolve(AgentSpan.Context.Extracted context) {
40+
private static InetAddress doResolve(AgentSpan.Context.Extracted context) {
4141
if (context == null) {
4242
return null;
4343
}

dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecoratorTest.groovy

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
3333
def decorator = newDecorator()
3434

3535
when:
36-
decorator.onRequest(span, null, req, null)
36+
decorator.onRequest(this.span, null, req, null)
3737

3838
then:
3939
if (req) {
40-
1 * span.setTag(Tags.HTTP_METHOD, "test-method")
41-
1 * span.setTag(DDTags.HTTP_QUERY, _)
42-
1 * span.setTag(DDTags.HTTP_FRAGMENT, _)
43-
1 * span.setTag(Tags.HTTP_URL, url)
44-
1 * span.setTag(Tags.HTTP_HOSTNAME, req.url.host)
45-
1 * span.getRequestContext()
46-
1 * span.setResourceName({ it as String == req.method.toUpperCase() + " " + req.path }, ResourceNamePriorities.HTTP_PATH_NORMALIZER)
40+
1 * this.span.setTag(Tags.HTTP_METHOD, "test-method")
41+
1 * this.span.setTag(DDTags.HTTP_QUERY, _)
42+
1 * this.span.setTag(DDTags.HTTP_FRAGMENT, _)
43+
1 * this.span.setTag(Tags.HTTP_URL, url)
44+
1 * this.span.setTag(Tags.HTTP_HOSTNAME, req.url.host)
45+
1 * this.span.getRequestContext()
46+
1 * this.span.setResourceName({ it as String == req.method.toUpperCase() + " " + req.path }, ResourceNamePriorities.HTTP_PATH_NORMALIZER)
4747
}
4848
0 * _
4949

@@ -63,26 +63,26 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
6363
def decorator = newDecorator()
6464

6565
when:
66-
decorator.onRequest(span, null, req, null)
66+
decorator.onRequest(this.span, null, req, null)
6767

6868
then:
6969
if (expectedUrl) {
70-
1 * span.setTag(Tags.HTTP_URL, expectedUrl)
71-
1 * span.getRequestContext()
70+
1 * this.span.setTag(Tags.HTTP_URL, expectedUrl)
71+
1 * this.span.getRequestContext()
7272
}
7373
if (expectedUrl && tagQueryString) {
74-
1 * span.setTag(DDTags.HTTP_QUERY, expectedQuery)
75-
1 * span.setTag(DDTags.HTTP_FRAGMENT, expectedFragment)
74+
1 * this.span.setTag(DDTags.HTTP_QUERY, expectedQuery)
75+
1 * this.span.setTag(DDTags.HTTP_FRAGMENT, expectedFragment)
7676
}
7777
if (url != null) {
78-
1 * span.setResourceName({ it as String == expectedPath }, ResourceNamePriorities.HTTP_PATH_NORMALIZER)
78+
1 * this.span.setResourceName({ it as String == expectedPath }, ResourceNamePriorities.HTTP_PATH_NORMALIZER)
7979
if (req.url.host != null) {
80-
1 * span.setTag(Tags.HTTP_HOSTNAME, req.url.host)
80+
1 * this.span.setTag(Tags.HTTP_HOSTNAME, req.url.host)
8181
}
8282
} else {
83-
1 * span.setResourceName({ it as String == expectedPath })
83+
1 * this.span.setResourceName({ it as String == expectedPath })
8484
}
85-
1 * span.setTag(Tags.HTTP_METHOD, null)
85+
1 * this.span.setTag(Tags.HTTP_METHOD, null)
8686
0 * _
8787

8888
where:
@@ -112,16 +112,16 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
112112
def decorator = newDecorator()
113113

114114
when:
115-
decorator.onRequest(span, null, req, null)
115+
decorator.onRequest(this.span, null, req, null)
116116

117117
then:
118-
1 * span.setTag(Tags.HTTP_URL, expectedUrl)
119-
1 * span.setTag(Tags.HTTP_HOSTNAME, req.url.host)
120-
1 * span.setTag(DDTags.HTTP_QUERY, expectedQuery)
121-
1 * span.setTag(DDTags.HTTP_FRAGMENT, null)
122-
1 * span.getRequestContext()
123-
1 * span.setResourceName({ it as String == expectedResource }, ResourceNamePriorities.HTTP_PATH_NORMALIZER)
124-
1 * span.setTag(Tags.HTTP_METHOD, null)
118+
1 * this.span.setTag(Tags.HTTP_URL, expectedUrl)
119+
1 * this.span.setTag(Tags.HTTP_HOSTNAME, req.url.host)
120+
1 * this.span.setTag(DDTags.HTTP_QUERY, expectedQuery)
121+
1 * this.span.setTag(DDTags.HTTP_FRAGMENT, null)
122+
1 * this.span.getRequestContext()
123+
1 * this.span.setResourceName({ it as String == expectedResource }, ResourceNamePriorities.HTTP_PATH_NORMALIZER)
124+
1 * this.span.setTag(Tags.HTTP_METHOD, null)
125125
0 * _
126126

127127
where:
@@ -140,7 +140,7 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
140140
def decorator = newDecorator()
141141

142142
when:
143-
decorator.onRequest(span, conn, null, ctx)
143+
decorator.onRequest(this.span, conn, null, ctx)
144144

145145
then:
146146
_ * ctx.getForwarded() >> null
@@ -159,20 +159,20 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
159159
_ * ctx.getTrueClientIp() >> null
160160
_ * ctx.getVia() >> null
161161
if (conn) {
162-
1 * span.setTag(Tags.PEER_PORT, 555)
162+
1 * this.span.setTag(Tags.PEER_PORT, 555)
163163
if (ipv4) {
164-
1 * span.setTag(Tags.PEER_HOST_IPV4, "10.0.0.1")
165-
1 * span.setTag(Tags.HTTP_CLIENT_IP, "10.0.0.1")
164+
1 * this.span.setTag(Tags.PEER_HOST_IPV4, "10.0.0.1")
165+
1 * this.span.setTag(Tags.HTTP_CLIENT_IP, "10.0.0.1")
166166
} else if (ipv4 != null) {
167-
1 * span.setTag(Tags.PEER_HOST_IPV6, "3ffe:1900:4545:3:200:f8ff:fe21:67cf")
168-
1 * span.setTag(Tags.HTTP_CLIENT_IP, "3ffe:1900:4545:3:200:f8ff:fe21:67cf")
167+
1 * this.span.setTag(Tags.PEER_HOST_IPV6, "3ffe:1900:4545:3:200:f8ff:fe21:67cf")
168+
1 * this.span.setTag(Tags.HTTP_CLIENT_IP, "3ffe:1900:4545:3:200:f8ff:fe21:67cf")
169169
}
170170
}
171-
_ * span.getRequestContext() >> null
171+
_ * this.span.getRequestContext() >> null
172172
0 * _
173173

174174
when:
175-
decorator.onRequest(span, conn, null, ctx)
175+
decorator.onRequest(this.span, conn, null, ctx)
176176

177177
then:
178178
_ * ctx.getForwarded() >> "by=<identifier>;for=<identifier>;host=<host>;proto=<http|https>"
@@ -190,27 +190,27 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
190190
_ * ctx.getCustomIpHeader() >> null
191191
_ * ctx.getTrueClientIp() >> null
192192
_ * ctx.getVia() >> null
193-
1 * span.setTag(Tags.HTTP_FORWARDED, "by=<identifier>;for=<identifier>;host=<host>;proto=<http|https>")
194-
1 * span.setTag(Tags.HTTP_FORWARDED_PROTO, "https")
195-
1 * span.setTag(Tags.HTTP_FORWARDED_HOST, "somehost")
193+
1 * this.span.setTag(Tags.HTTP_FORWARDED, "by=<identifier>;for=<identifier>;host=<host>;proto=<http|https>")
194+
1 * this.span.setTag(Tags.HTTP_FORWARDED_PROTO, "https")
195+
1 * this.span.setTag(Tags.HTTP_FORWARDED_HOST, "somehost")
196196
if (ipv4) {
197-
1 * span.setTag(Tags.HTTP_FORWARDED_IP, "10.1.1.1, 192.168.1.1")
198-
1 * span.setTag(Tags.PEER_HOST_IPV4, "10.0.0.1")
197+
1 * this.span.setTag(Tags.HTTP_FORWARDED_IP, "10.1.1.1, 192.168.1.1")
198+
1 * this.span.setTag(Tags.PEER_HOST_IPV4, "10.0.0.1")
199199
} else if (conn?.ip) {
200-
1 * span.setTag(Tags.HTTP_FORWARDED_IP, "0::1")
201-
1 * span.setTag(Tags.PEER_HOST_IPV6, "3ffe:1900:4545:3:200:f8ff:fe21:67cf")
200+
1 * this.span.setTag(Tags.HTTP_FORWARDED_IP, "0::1")
201+
1 * this.span.setTag(Tags.PEER_HOST_IPV6, "3ffe:1900:4545:3:200:f8ff:fe21:67cf")
202202
} else {
203-
1 * span.setTag(Tags.HTTP_FORWARDED_IP, "0::1")
203+
1 * this.span.setTag(Tags.HTTP_FORWARDED_IP, "0::1")
204204
}
205-
1 * span.setTag(Tags.HTTP_FORWARDED_PORT, "123")
205+
1 * this.span.setTag(Tags.HTTP_FORWARDED_PORT, "123")
206206
if (conn) {
207-
1 * span.setTag(Tags.PEER_PORT, 555)
207+
1 * this.span.setTag(Tags.PEER_PORT, 555)
208208
if (conn.ip) {
209-
1 * span.setTag(Tags.HTTP_CLIENT_IP, conn.ip)
209+
1 * this.span.setTag(Tags.HTTP_CLIENT_IP, conn.ip)
210210
}
211211
}
212-
1 * span.setTag(Tags.HTTP_USER_AGENT, "some-user-agent")
213-
_ * span.getRequestContext() >> null
212+
1 * this.span.setTag(Tags.HTTP_USER_AGENT, "some-user-agent")
213+
_ * this.span.getRequestContext() >> null
214214
0 * _
215215

216216
where:
@@ -226,20 +226,20 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
226226
def decorator = newDecorator()
227227

228228
when:
229-
decorator.onResponse(span, resp)
229+
decorator.onResponse(this.span, resp)
230230

231231
then:
232232
if (status) {
233-
1 * span.setHttpStatusCode(status)
233+
1 * this.span.setHttpStatusCode(status)
234234
}
235235
if (error) {
236-
1 * span.setError(true)
236+
1 * this.span.setError(true)
237237
}
238238
if (status == 404) {
239-
1 * span.setResourceName({ it as String == "404" }, ResourceNamePriorities.HTTP_404)
239+
1 * this.span.setResourceName({ it as String == "404" }, ResourceNamePriorities.HTTP_404)
240240
}
241241
if (resp) {
242-
1 * span.getRequestContext()
242+
1 * this.span.getRequestContext()
243243
}
244244
0 * _
245245

dd-java-agent/appsec/src/main/java/com/datadog/appsec/event/data/KnownAddresses.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ public interface KnownAddresses {
1717
/** The unparsed request uri, incl. the query string. */
1818
Address<String> REQUEST_URI_RAW = new Address<>("server.request.uri.raw");
1919

20-
/** The deduced IP address of the client. */
20+
/** The socket IP address of the client. */
2121
Address<String> REQUEST_CLIENT_IP = new Address<>("server.request.client_ip");
2222

2323
/** The peer port */
2424
Address<Integer> REQUEST_CLIENT_PORT = new Address<>("_server.request.client_port");
2525

26+
/** The inferred IP address of the client */
27+
Address<String> REQUEST_INFERRED_CLIENT_IP = new Address<>("http.client_ip");
28+
2629
/** The verb of the HTTP request. */
2730
Address<String> REQUEST_METHOD = new Address<>("server.request.method");
2831

@@ -104,6 +107,8 @@ static Address<?> forName(String name) {
104107
return REQUEST_CLIENT_IP;
105108
case "_server.request.client_port":
106109
return REQUEST_CLIENT_PORT;
110+
case "http.client_ip":
111+
return REQUEST_INFERRED_CLIENT_IP;
107112
case "server.request.method":
108113
return REQUEST_METHOD;
109114
case "server.request.path_params":

dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class AppSecRequestContext implements DataBundle, Closeable {
6060
private boolean finishedResponseHeaders;
6161
private String peerAddress;
6262
private int peerPort;
63+
private String inferredClientIp;
6364

6465
private volatile StoredBodySupplier storedRequestBodySupplier;
6566

@@ -272,6 +273,14 @@ public void setPeerPort(int peerPort) {
272273
this.peerPort = peerPort;
273274
}
274275

276+
void setInferredClientIp(String ipAddress) {
277+
this.inferredClientIp = ipAddress;
278+
}
279+
280+
String getInferredClientIp() {
281+
return inferredClientIp;
282+
}
283+
275284
void setStoredRequestBodySupplier(StoredBodySupplier storedRequestBodySupplier) {
276285
this.storedRequestBodySupplier = storedRequestBodySupplier;
277286
}

dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/GatewayBridge.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ public void init() {
286286
return maybePublishRequestData(ctx);
287287
});
288288

289+
subscriptionService.registerCallback(
290+
EVENTS.requestInferredClientAddress(),
291+
(ctx_, ip) -> {
292+
AppSecRequestContext ctx = ctx_.getData(RequestContextSlot.APPSEC);
293+
ctx.setInferredClientIp(ip);
294+
return NoopFlow.INSTANCE; // expected to be called before requestClientSocketAddress
295+
});
296+
289297
subscriptionService.registerCallback(
290298
EVENTS.responseStarted(),
291299
(ctx_, status) -> {
@@ -465,7 +473,8 @@ private Flow<Void> maybePublishRequestData(AppSecRequestContext ctx) {
465473
KnownAddresses.REQUEST_URI_RAW,
466474
KnownAddresses.REQUEST_QUERY,
467475
KnownAddresses.REQUEST_CLIENT_IP,
468-
KnownAddresses.REQUEST_CLIENT_PORT);
476+
KnownAddresses.REQUEST_CLIENT_PORT,
477+
KnownAddresses.REQUEST_INFERRED_CLIENT_IP);
469478
}
470479
MapDataBundle bundle =
471480
new MapDataBundle.Builder(CAPACITY_6_10)
@@ -477,6 +486,7 @@ private Flow<Void> maybePublishRequestData(AppSecRequestContext ctx) {
477486
.add(KnownAddresses.REQUEST_QUERY, queryParams)
478487
.add(KnownAddresses.REQUEST_CLIENT_IP, ctx.getPeerAddress())
479488
.add(KnownAddresses.REQUEST_CLIENT_PORT, ctx.getPeerPort())
489+
.add(KnownAddresses.REQUEST_INFERRED_CLIENT_IP, ctx.getInferredClientIp())
480490
.build();
481491

482492
return producerService.publishDataEvent(initialReqDataSubInfo, ctx, bundle, false);

0 commit comments

Comments
 (0)