atenet/dns: answer non-A actor queries instead of SERVFAIL - #874
atenet/dns: answer non-A actor queries instead of SERVFAIL#874Yuan Gao (ygao-g) wants to merge 3 commits into
Conversation
7a240f7 to
0272dc0
Compare
|
Bowei Du (@bowei) Julian Gutierrez Oschmann (@juli4n) mind taking a look? This is the SERVFAIL half of #246, step one of your three: the actor zone can't Julian Gutierrez Oschmann (@juli4n), the substance is three Verified against |
f4bc2e5 to
fd6e47c
Compare
|
Yuan -- it would be good to be very specific on what we are doing in this PR. From what I can tell:
Is this what you are trying to do? |
|
Close, but that's two PRs and this is the smaller one. This PR doesn't publish an AAAA. It makes the zone return a correct rcode for what it doesn't answer — NODATA for a real actor name on a non-A qtype, NXDOMAIN for a name in the zone that doesn't exist. Both SERVFAIL today, on IPv4-only clusters too, which is why musl-based actors can't resolve each other at all: #888. Publishing AAAA is #938. The three together:
Every actor name resolves to the same address — the router ClusterIP, with per-actor demux at Envoy on the Host header — so an AAAA for a Substrate name is those three things, and no one of them is useful alone. End state on dual-stack: an actor name resolves in both families and Envoy answers on either. Each PR carries its own verification; the real gap is that dual-stack isn't testable until #877 lands, so #911's dual-stack behaviour is argued rather than run. On "fix any associated things" — past DNS, three more sit between a published AAAA and a usable IPv6 path, now filed as step-3 sub-tasks: #943, #944, #945. |
1634ab3 to
f8b1782
Compare
482ec1d to
9cf8384
Compare
9cf8384 to
fd76e7b
Compare
8c2a75e to
8eaf242
Compare
Not intended to merge. The unit test in agent-substrate#874 pins the rendered zone but nothing exercises CoreDNS, so this serves that zone with the pinned coredns/coredns:1.11.1 and checks the rcode returned for A, AAAA, HTTPS, SRV, a name in the zone that is not an actor, and a malformed one, plus an Alpine getent for the musl path. Pointing --corefile at a zone rendered before the fix turns the run into a negative control: the AAAA case SERVFAILs and getent stops resolving, while plain dig A still succeeds.
|
haiyanmeng Bowei Du (@bowei) PTAL when you have time. Thanks! |
haiyanmeng
left a comment
There was a problem hiding this comment.
LGTM
Left a comment regarding the test cvoerage
The zone answered A queries and failed everything else -- AAAA for a valid actor, and any name in the zone that is not an actor -- and no test caught it, because Go's resolver masks a SERVFAIL that musl treats as fatal. These assert the rcode class rather than the record: a non-A qtype and a name that misses the actor regex must come back NODATA or NXDOMAIN, and an A query must carry the router's ClusterIP. A separate test covers the AAAA record, skipped where the router has no v6 address. Second of two commits. The assertions are red until agent-substrate#874 and agent-substrate#938 land, so this stays a draft until then. Part of agent-substrate#246.
The zone answered A queries and failed everything else -- AAAA for a valid actor, and any name in the zone that is not an actor -- and no test caught it, because Go's resolver masks a SERVFAIL that musl treats as fatal. These assert the rcode class rather than the record: a non-A qtype and a name that misses the actor regex must come back NODATA or NXDOMAIN, and an A query must carry the router's ClusterIP. A separate test covers the AAAA record, skipped where the router has no v6 address. Second of two commits. The assertions are red until agent-substrate#874 and agent-substrate#938 land, so this stays a draft until then. Part of agent-substrate#246.
8eaf242 to
dca53af
Compare
The zone answered A queries and failed everything else -- AAAA for a valid actor, and any name in the zone that is not an actor -- and no test caught it, because Go's resolver masks a SERVFAIL that musl treats as fatal. These assert the rcode class rather than the record: a non-A qtype and a name that misses the actor regex must come back NODATA or NXDOMAIN, and an A query must carry the router's ClusterIP. A separate test covers the AAAA record, skipped where the router has no v6 address. Second of two commits. The assertions are red until agent-substrate#874 and agent-substrate#938 land, so this stays a draft until then. Part of agent-substrate#246.
dca53af to
669ef44
Compare
669ef44 to
3dde23e
Compare
Bowei Du (bowei)
left a comment
There was a problem hiding this comment.
Please go through and delete the AI agent self-conversation text. The comments need to be simplified.
| // RouterNamespace and RouterService locate the atenet router. Exported so | ||
| // that suites addressing the same Service or its pods do not have to | ||
| // redeclare them. | ||
| RouterNamespace = "ate-system" |
There was a problem hiding this comment.
If we are going to export these, the constants should be located in a package like cmd/atenet/const.
There was a problem hiding this comment.
Done, but in internal/atenetconsts rather than cmd/atenet/const. PTAL
| "k8s.io/client-go/kubernetes" | ||
| ) | ||
|
|
||
| // clusterIPsByFamily splits a Service's cluster IPs into its IPv4 and IPv6 |
There was a problem hiding this comment.
This comment is a mix of describing the implementation and the func purpose.
It should only describe function purpose.
// clusterIPsByFamily gets the Service's cluster IPs split by IPv4 and IPv6.
// If a given family does not exist, the value will be empty string.
This implementation is very complicated, can we do something like this:
// serviceClusterIPs holds the IPv4 and IPv6 cluster IP addresses of a Service.
type serviceClusterIPs struct {
IPv4 string
IPv6 string
}
// getServiceClusterIPs extracts the IPv4 and IPv6 ClusterIPs from a Kubernetes Service.
// It handles single-stack, dual-stack, headless, and legacy Service definitions.
func getServiceClusterIPs(svc *corev1.Service) serviceClusterIPs {
var result serviceClusterIPs
if svc == nil {
return result
}
// Gather all candidate IPs. Prefer Spec.ClusterIPs (k8s 1.20+),
// falling back to Spec.ClusterIP for legacy objects.
ips := svc.Spec.ClusterIPs
if len(ips) == 0 && svc.Spec.ClusterIP != "" {
ips = []string{svc.Spec.ClusterIP}
}
for _, ipStr := range ips {
// Ignore headless services or unallocated states
if ipStr == "" || ipStr == corev1.ClusterIPNone {
continue
}
addr, err := netip.ParseAddr(ipStr)
if err != nil {
continue
}
if addr.Is4() && result.IPv4 == "" {
result.IPv4 = ipStr
} else if addr.Is6() && result.IPv6 == "" {
result.IPv6 = ipStr
}
}
return result
}
There was a problem hiding this comment.
Took the struct and the nil guard. Kept !Is4In6(): without it ::ffff:10.96.0.10 files as IPv6. Table case covers it. PTAL
|
Fix the AI generated commit comments as well. |
3dde23e to
c97f279
Compare
Before, the actor zone answered A queries and failed everything else. clients percieve these as temporary errors and retry. After, those queries return a correct empty answer, and one that resolvers can cache.
To prepare for asserting actor DNS zone in followup commits. More specifically, add a helper function to retrieve IPv4 and IPv6 addresses given a K8s Service.
Before, no e2e tests could query the actor DNS zone and assert CoreDNS serves. Suites reach actors by port-forwarding atenet-router with a Host header. The new tests assert that every query into the zone must come back with an answer or a cacheable negative.
c97f279 to
cd1cfc6
Compare
Fixes #888
The actor DNS zone only answered
A; everything else —AAAAfor a validactor, and any other name in the zone — got SERVFAIL. Strict resolvers treat
that as a hard network error rather than an empty answer, and musl maps it to
EAI_AGAIN, sinking the pairedAquery with it, so Alpine actors could notresolve each other even on an IPv4-only cluster.
Valid actor names now return NODATA (NOERROR + SOA) for non-A types, and
anything else in the zone returns NXDOMAIN — both negatively cacheable. This
does not publish an
AAAArecord; that is #938.The last two commits cover this end to end. The harness could not query the
zone at all before them: every suite reaches an actor by port-forwarding
atenet-router with a
Hostheader, so what CoreDNS serves went unasserted.