Expand description
SEAM-1 / L2-04 DNS proxy — forward-only UDP proxy that enforces
dnsAuthority.hostnameAllowlist at the DNS protocol layer and emits one
per-query CloudEvent for every observed query.
§Phase 1 scope
- UDP only. The single-listener proxy reads a workload query, parses
the question section (see
parser), evaluates the allowlist, then either forwards the bytes verbatim to the upstreamdo53-udpresolver or builds a REFUSED response on the spot. TCP fallback is documented as a follow-up. - Forward-only. No recursion, no caching. The proxy talks to a single declared upstream resolver per cell and relays the response (rcode, answers, additional records) verbatim to the workload, preserving the transaction id binding.
- Allowlist-strict. A query whose name does not match an entry in
hostname_allowlist(literal or single-leading-*.wildcard) is short-circuited with REFUSED — the upstream resolver never sees it. - Malformed-drop. Packets that fail
parser::parse_queryare dropped without a response (matching common resolver behaviour against adversarial input). The correspondingdns_queryevent still fires withreasonCode: malformed_query. - SERVFAIL on upstream timeout. When the upstream resolver fails to
answer within
DnsProxyConfig::upstream_timeoutthe proxy synthesizes a SERVFAIL response (RCODE=2) so the workload sees a deterministic error rather than hanging.
§What this module does NOT do
- TCP fallback — workloads that get a
TC(truncated) flag from upstream are expected to retry on TCP; Phase 1 forwards UDP-only and relays the truncated answer if the upstream setsTC. A real TCP listener path is a Phase 2 follow-up. - EDNS / DoT / DoH / DoQ upstream — Phase 1’s upstream is
do53-udponly. The contract layer (DnsAuthority::resolvers[]) admits these protocols but the dataplane forwards over plain UDP. - No netns plumbing — the caller pre-binds the listening + upstream
sockets in the cell’s network namespace and hands them to
run_one_shot. This module is platform-neutral and contains nonsenterorsetnscalls.
§Caller responsibility
- Pre-bind the listener socket on the address the workload’s
/etc/resolv.confpoints at (typically the same IP/port declared as ado53-udpresolver indnsAuthority.resolvers[]so the existing nft accept rule lets the traffic through). - Pre-bind the upstream socket in the same netns; the proxy uses it for forwarding.
- Drive the loop until
shutdown.store(true)to terminate it; the listener’sread_timeoutshould be set to a short interval (e.g. 200ms) so the loop checks the shutdown flag promptly.
Modules§
- dnssec
- SEC-21 Phase 3h.1 — SEAM-1 in-netns DNS proxy DNSSEC validation.
- parser
- Minimal DNS message parser scoped to what the SEAM-1 / L2-04 proxy needs.
- spawn
- SEAM-1 Phase 2b — supervisor → cell-netns DNS proxy spawn.
- upstream
- SEC-21 Phase 3h.2 / T2.B Slot A6 — DoT/DoH/DoQ upstream support.
Structs§
- DnsProxy
Config - Configuration for
run_one_shot. - DnsProxy
Stats - Aggregate counters returned by
run_one_shotwhen the loop terminates. Mirrors the cumulative state observed across a single cell run; the supervisor logs these at teardown for sanity-checking ratios.
Traits§
- DnsQuery
Emitter - Sink the proxy uses to publish per-query events. Trait-erased so unit tests
can plug in an in-memory collector and the supervisor can adapt the
production
EventSinkto a synchronous-fire-and-forget shape.
Functions§
- run_
one_ shot - Run the proxy loop synchronously on the calling thread.
- run_
tcp_ one_ shot - SEAM-1 / L2-04 Phase 2 — TCP/53 listener variant of
run_one_shot.