Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,25 @@ public static void report(String hostname, InetAddress[] inetAddresses) {
// Removing them here ensures each (hostname, port) pair is counted exactly once.
Set<Integer> ports = PendingHostnamesStore.getAndRemove(hostname);

// The outbound-domains list is meant for hostnames, not raw IP literals.
// A private/internal IP literal passed straight to getAllByName (DNS-resolver
// bootstrap, service discovery connecting by IP, libraries building a private-IP
// matcher, ...) is not an outbound domain and would otherwise flood the
// "new outbound connection" feature. Skip recording those; SSRF/stored-SSRF and
// outbound-domain blocking below are unaffected.
boolean isPrivateIpLiteral = IsPrivateIP.isPrivateIp(hostname);
if (!isPrivateIpLiteral) {
if (!ports.isEmpty()) {
for (int port : ports) {
HostnamesStore.incrementHits(hostname, port);
}
} else {
// We still need to report a hit to the hostname for outbound domain blocking
HostnamesStore.incrementHits(hostname, 0);
// Don't report private/internal IP literals as outbound connections, consistent
// with the other Zen agents. A raw private IP reaching getAllByName is infrastructure,
// not a real outbound domain: the Reactor Netty resolver bootstrap resolving the
// any-address/nameservers, service discovery connecting by IP, a library building a
// private-IP matcher, etc. We fully return so we also skip outbound blocking below;
// otherwise lockdown mode (blockNewOutgoingRequests) would block these internal
// resolutions and break the application. Real domains that resolve to private IPs are
// not literals, so they fall through and are still tracked and SSRF-checked.
if (IsPrivateIP.isPrivateIp(hostname)) {
return;
}

if (!ports.isEmpty()) {
for (int port : ports) {
HostnamesStore.incrementHits(hostname, port);
}
} else {
// We still need to report a hit to the hostname for outbound domain blocking
HostnamesStore.incrementHits(hostname, 0);
}

// Block if the hostname is in the blocked domains list
Expand Down
30 changes: 30 additions & 0 deletions agent_api/src/test/java/collectors/DNSRecordCollectorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,34 @@ public void testPublicIpLiteralStillRecorded() {
assertEquals(1, entries.length);
assertEquals("1.1.1.1", entries[0].getHostname());
}

@Test
public void testPrivateIpLiteralNotBlockedInLockdownMode() throws UnknownHostException {
// Lockdown (blockNewOutgoingRequests=true) blocks any host not on the allow list.
// A private IP literal must be fully ignored via early return, so it is neither
// recorded nor blocked; otherwise lockdown would break internal resolutions.
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
true, null, 0L, null, null, null, true, List.of(), true, true, List.of()
));
InetAddress[] resolved = new InetAddress[]{InetAddress.getByName("10.0.0.0")};

assertDoesNotThrow(() -> DNSRecordCollector.report("10.0.0.0", resolved));
assertEquals(0, HostnamesStore.getHostnamesAsList().length);
}

@Test
public void testPrivateIpLiteralViaUrlInLockdownNotBlockedNorRecorded() throws UnknownHostException {
// http://10.0.0.1:8080 -> URLCollector registers pending port 8080, then
// getAllByName("10.0.0.1"). The private IP is fully ignored: not recorded, not blocked
// in lockdown, and the pending port is still consumed.
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
true, null, 0L, null, null, null, true, List.of(), true, true, List.of()
));
PendingHostnamesStore.add("10.0.0.1", 8080);
InetAddress[] resolved = new InetAddress[]{InetAddress.getByName("10.0.0.1")};

assertDoesNotThrow(() -> DNSRecordCollector.report("10.0.0.1", resolved));
assertEquals(0, HostnamesStore.getHostnamesAsList().length);
assertTrue(PendingHostnamesStore.getPorts("10.0.0.1").isEmpty());
}
}
Loading