Skip to content

Commit 608a268

Browse files
author
dmitrii
committed
Skip recording DNS noise for private IP literals with no pending port
Private IP literal hostnames with no pending port (resolver bootstrap noise, network capability probing, etc.) flood the outbound-connections dashboard. Outbound domain blocking and SSRF detection stay fully unconditional either way - only the dashboard hit is skipped.
1 parent 8de3298 commit 608a268

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

agent_api/src/main/java/dev/aikido/agent_api/collectors/DNSRecordCollector.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import dev.aikido.agent_api.vulnerabilities.ssrf.SSRFException;
1313
import dev.aikido.agent_api.helpers.logging.LogManager;
1414
import dev.aikido.agent_api.helpers.logging.Logger;
15+
import dev.aikido.agent_api.vulnerabilities.ssrf.IsPrivateIP;
1516
import dev.aikido.agent_api.vulnerabilities.ssrf.StoredSSRFDetector;
1617
import dev.aikido.agent_api.vulnerabilities.ssrf.StoredSSRFException;
1718

@@ -41,7 +42,8 @@ public static void report(String hostname, InetAddress[] inetAddresses) {
4142
for (int port : ports) {
4243
HostnamesStore.incrementHits(hostname, port);
4344
}
44-
} else {
45+
}
46+
if (ports.isEmpty() && !IsPrivateIP.isPrivateIp(hostname)) {
4547
// We still need to report a hit to the hostname for outbound domain blocking
4648
HostnamesStore.incrementHits(hostname, 0);
4749
}

agent_api/src/test/java/collectors/DNSRecordCollectorTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,62 @@ public void testSSRFStillRunsWhenPendingPortIsZero() {
203203
assertEquals("Aikido Zen has blocked a server-side request forgery", exception.getMessage());
204204
}
205205

206+
@Test
207+
public void testPrivateIpLiteralWithNoPendingPortNotRecorded() {
208+
Context.set(null);
209+
DNSRecordCollector.report("10.20.11.143", new InetAddress[]{inetAddress2});
210+
assertEquals(0, HostnamesStore.getHostnamesAsList().length);
211+
}
212+
213+
@Test
214+
public void testPrivateIpLiteralWithNoPendingPortNotRecordedButBlockedInLockdown() {
215+
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
216+
true, null, 0L, null, null, null, true, List.of(), true, true, List.of()
217+
));
218+
Context.set(null);
219+
assertThrows(BlockedOutboundException.class, () ->
220+
DNSRecordCollector.report("10.20.11.143", new InetAddress[]{inetAddress2})
221+
);
222+
assertEquals(0, HostnamesStore.getHostnamesAsList().length);
223+
}
224+
225+
@Test
226+
public void testPrivateIpLiteralWithPendingPortStillRecordedAndBlockedInLockdown() {
227+
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
228+
true, null, 0L, null, null, null, true, List.of(), true, true, List.of()
229+
));
230+
PendingHostnamesStore.add("10.20.11.143", 443);
231+
Context.set(mock(ContextObject.class));
232+
233+
assertThrows(BlockedOutboundException.class, () ->
234+
DNSRecordCollector.report("10.20.11.143", new InetAddress[]{inetAddress2})
235+
);
236+
Hostnames.HostnameEntry[] entries = HostnamesStore.getHostnamesAsList();
237+
assertEquals(1, entries.length);
238+
assertEquals(443, entries[0].getPort());
239+
}
240+
241+
@Test
242+
public void testSsrfStillDetectedForPrivateIpLiteralWithPendingPort() {
243+
ServiceConfigStore.updateBlocking(true);
244+
PendingHostnamesStore.add("169.254.169.254", 80);
245+
Context.set(new EmptySampleContextObject("http://169.254.169.254:80/latest/meta-data/"));
246+
247+
Exception exception = assertThrows(SSRFException.class, () ->
248+
DNSRecordCollector.report("169.254.169.254", new InetAddress[]{imdsAddress1})
249+
);
250+
assertEquals("Aikido Zen has blocked a server-side request forgery", exception.getMessage());
251+
}
252+
253+
@Test
254+
public void testNamedHostnameResolvingToPrivateIpWithNoPendingPortStillRecorded() {
255+
Context.set(null);
256+
DNSRecordCollector.report("internal-service.local", new InetAddress[]{inetAddress2});
257+
Hostnames.HostnameEntry[] entries = HostnamesStore.getHostnamesAsList();
258+
assertEquals(1, entries.length);
259+
assertEquals("internal-service.local", entries[0].getHostname());
260+
}
261+
206262
@Test
207263
public void testStoredSSRFWithNoContext() throws InterruptedException {
208264
ServiceConfigStore.updateBlocking(true);

0 commit comments

Comments
 (0)