Replay reports a mock mismatch for DNS names capture deliberately never records (NXDOMAIN search-expansion)
Parent: #2426
Summary
Capture deliberately drops non-Success DNS responses. Replay then counts those same
queries as mock mismatches and fails the test. The two halves disagree, and with
ndots:5 the cost is large: every hostname emits 2–3 search-expansion misses, so one app
doing one lookup can fail an entire test set on names that never resolved in production
either.
Evidence
Locally, after fixing the cross-namespace DNS search domain (#2428), a clean run still failed 8/8 with 16 DNS mismatches — and every single one was a search-expanded name:
6 × A mongo.prod.svc.cluster.local.prod.svc.cluster.local.
6 × AAAA mongo.prod.svc.cluster.local.prod.svc.cluster.local.
2 × A openbao-prod-active.prod.svc.cluster.local.prod.svc.cluster.local.
2 × AAAA openbao-prod-active.prod.svc.cluster.local.prod.svc.cluster.local.Those doubled names are what the resolver probes first under ndots:5; they NXDOMAIN, and
then the absolute name succeeds. Recorded DNS mocks: 4 — exactly the successful absolute
A/AAAA lookups for those two hosts.
The capture side is explicit that this is intentional
(enterprise/pkg/agent/proxy/dns_capture.go):
// Mirror keploy's existing DNS server: skip non-Success rcodes (NXDOMAIN,
// SERVFAIL, REFUSED). These are usually search-domain expansion noise
// and would pollute mocks.yaml without adding replay value.
if msg.Rcode != dns.RcodeSuccess { return }pinned by TestDNSCapture_SkipsNXDomainResponses. That reasoning is sound — recording them
would multiply DNS mocks several-fold.
But the replay side (keploy/keploy pkg/agent/proxy/dns.go, resolveUncachedDNSResponse)
treats their absence as a defect: on a mock miss it forwards upstream, gets a negative
answer, falls through to the synthetic response and then reports
ErrMockNotFound + a MockMismatchReport.
Note the inconsistency already present in that function: NODATA (NOERROR + 0 answers) relays and returns early, producing no mismatch. Only NXDOMAIN falls through to the report — even though capture skips both equally.
What NOT to do
Synthesising NXDOMAIN back to the app would regress #2006 — the code says so:
// When mocking is enabled, a negative answer must NOT reach the app: any
// unknown name should resolve to the proxy IP so eBPF can intercept it and
// match against recorded mocks (relaying NXDOMAIN crashes apps using bare
// service names like "localstack" or "postgres" — issue #2006).The #2006 steering must stay.
Fix
Keep the steering; stop reporting an expected negative. Only when upstream returned a definitive negative answer — if upstream was unreachable we cannot tell, so keep reporting there. This makes NXDOMAIN consistent with the NODATA branch above it.
Validated locally: DNS mismatches 16 → 0, and combined with the other fixes the run went to 8/8 passing.
Source: keploy/keploy