Skip to content

[do not merge] hack: check the atenet gateways on every IP family - #1047

Draft
Yuan Gao (ygao-g) wants to merge 5 commits into
agent-substrate:mainfrom
ygao-g:atenet-dualstack-verify
Draft

[do not merge] hack: check the atenet gateways on every IP family#1047
Yuan Gao (ygao-g) wants to merge 5 commits into
agent-substrate:mainfrom
ygao-g:atenet-dualstack-verify

Conversation

@ygao-g

@ygao-g Yuan Gao (ygao-g) commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Part of #246.

hack/check-atenet-ip-families.sh checks that both atenet gateways bind and serve on every IP family the cluster has, so the dual-stack work stops being verified by hand. It reads the families off the Service and reports every check rather than stopping at the first failure, so one run is valid on ipv4, dual or ipv6 — worth running on each, because they fail differently. A v4-wildcard bind is a silent data-path gap on dual-stack, where the Service has an IPv6 ClusterIP nothing listens on, and a crashloop on IPv6-only, where the kubelet probes the pod on its only address.

Dependencies

Only the last commit is new; the branch carries two open PRs so the script has something to run against. This branch is for reference only and is not intended to merge — #911 and #958 are the PRs to review.

IP_FAMILY in hack/create-kind-cluster.sh is already on main (#877).

Testing

IP_FAMILY=ipv6 hack/create-kind-cluster.sh, then hack/install-ate-kind.sh --deploy-ate-system, on a single-stack IPv6 kind cluster. The install completed — including atenet-egress, which times out on main. All 14 checks pass:

  • atenet-router and atenet-egress both 2/2 Running with 0 restarts.
  • admin address: [::]:9901 and [::]:15000 in the Envoy logs, and /ready returns 200 on both 127.0.0.1 and ::1 for each — the loopback that the router's health check and the egress drainer dial as IPv4 literals.
  • ipv4_compat counted from /config_dump. Not /listeners, which reports resolved bound addresses and never emits the flag, so a check written against it would pass on a manifest that had lost it.
  • ingress_http_listener and ingress_https_listener keep their 0.0.0.0 primary and gain a :: additional address.
  • Both ClusterIPs accept connections from a pod in the cluster.

Negative control: reverting only the two bind lines to 0.0.0.0 flipped exactly the six egress checks to FAIL, left every router check passing, and reproduced Startup probe failed: ... connect: connection refused. Restoring them turned it green again, so the assertions are not vacuous.

The e2e suites do run on IPv6-only once #958 is in — TestActorDirectAccess passes, including through the router's ingress. TestActorEgress still fails, but inside the actor's netns (dial udp [fd00:10:96::a]:53: connect: network is unreachable), which is #979's scope, not this one's.

The script itself has so far only been run on IPv6-only; the ipv4 and dual-stack paths are unexercised from this branch.

🤖 Generated with Claude Code

The HTTP and HTTPS ingress listeners bound 0.0.0.0 only, so on a
dual-stack cluster Envoy answered on the router Service's IPv4
ClusterIP and on nothing at all for IPv6. Each primary socket now
carries an additional "::" address on the same port.

Ipv4Compat stays false on it: setting it would clear IPV6_V6ONLY and
collide with the primary already bound to that port, and Envoy rejects
the whole listener when an additional address fails to bind, taking
down all ingress rather than the IPv6 half. IPv4-only clusters are
unaffected -- the primary is untouched, and a host without IPv6 simply
has no second socket to bind. First of three commits binding atenet's
gateways dual-stack.
The Envoy admin socket bound 0.0.0.0, and the atenet-router Service
carried no ipFamilyPolicy -- which the API server defaults to
SingleStack, one IPv4 ClusterIP and nothing else. Between them the
router had no IPv6 address to answer on. The socket now binds "::"
with ipv4_compat, one socket for both families, and the Service asks
for PreferDualStack.

ipv4_compat is load-bearing rather than incidental: dataplane.go
health-checks the admin listener over http://127.0.0.1:9901/ready, so
a bare "::" would report the dataplane component of /statusz
unhealthy. Prefer, not Require, keeps the Service valid on a
single-stack cluster, where it is a no-op; spec.ipFamilies is left
alone because the primary family is immutable and the API server
appends the secondary itself.
The gateway's admin and :443 sockets bound 0.0.0.0, so on an
IPv6-primary cluster the kubelet probed the pod on its only address
and atenet-egress crashlooped -- Envoy started fine and logged "admin
address: 0.0.0.0:15000" -- while an actor's CONNECT had no v6 path in.
Both sockets now bind "::" with ipv4_compat, and the Service asks for
PreferDualStack so a dual-stack cluster hands out an IPv6 ClusterIP to
reach them on.

ipv4_compat matters on the admin socket in particular: the ext-proc
sidecar's drainer dials 127.0.0.1:15000, and envoydrain.go reads a
refusal there as "Envoy already exited" and skips the drain silently.
@ygao-g Yuan Gao (ygao-g) added kind/bug Something isn't working / bugfixes area/network and removed kind/bug Something isn't working / bugfixes labels Aug 18, 2026
@ygao-g Yuan Gao (ygao-g) changed the title hack: check the atenet gateways on every IP family [do not merge] hack: check the atenet gateways on every IP family Aug 18, 2026
@ygao-g Yuan Gao (ygao-g) added the DO NOT MERGE This PR must not be merged yet. label Aug 19, 2026
On a fresh IP_FAMILY=ipv6 cluster nothing resolves from inside a pod and
no actor boots: CoreDNS inherits the node's IPv4 resolver, which a
v6-only pod cannot reach. Two Corefile clauses fix it -- a hosts entry
mapping kind-registry to its IPv6 address, and a forward to an IPv6
upstream, overridable with IPV6_DNS_UPSTREAM. IPv4 and dual-stack
clusters are unchanged, and atenet-egress still crashloops on v6-only
for an unrelated Envoy bind bug.

Asking once was not enough to prove that: about half of fresh clusters
do not answer the first query, and a pod that goes unanswered stays
unanswered, so the check re-asks with a new pod and prints what the pod
saw when it gives up. It lives in hack/verify-ipv6-dns.sh rather than
inline, because the hosts entry records an address the registry can move
off and there was no way to re-check a cluster without rebuilding it.
Whether the router and egress gateways really serve on every family the
cluster has was only ever checked by hand, and the checks are easy to
get wrong -- Envoy's /listeners reports resolved addresses and never
emits ipv4_compat, so an assertion written against it passes on a
manifest that lost the flag. This script reads the families off the
Service and reports every check it makes, so one run is valid on ipv4,
dual, and ipv6.

Worth running on all three, because they fail differently. A
v4-wildcard bind is a silent data-path gap on dual-stack, where the
Service has an IPv6 ClusterIP nothing listens on, and a crashloop on
IPv6-only, where the kubelet probes the pod on its only address.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/network DO NOT MERGE This PR must not be merged yet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant