From ac1600b2f0730da598880d576e673ab248921420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Va=C5=A1ek?= Date: Thu, 7 May 2026 20:29:29 +0200 Subject: [PATCH 1/3] fix: bind Contour components to IPv6 on dual-stack/IPv6-only clusters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contour defaults its xDS server, Envoy HTTP/HTTPS listeners, and stats endpoint to 0.0.0.0 (IPv4-only). On IPv6-only clusters this prevents the control plane from communicating with Envoy and blocks all ingress traffic. Patch the Contour deployment at install time to bind xds-address, envoy-service-http-address, envoy-service-https-address, and stats-address to :: so all components listen on IPv6. Signed-off-by: Matej Vašek --- hack/cluster.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hack/cluster.sh b/hack/cluster.sh index 296ab0f4c5..2cc18381a9 100755 --- a/hack/cluster.sh +++ b/hack/cluster.sh @@ -239,8 +239,9 @@ networking() { echo "Installing a configured Contour." curl -sSL "https://github.com/knative/net-contour/releases/download/knative-${contour_version}/contour.yaml" \ + | $YQ '(select(.kind == "Deployment" and .metadata.name == "contour").spec.template.spec.containers[0].args[] | select(. == "--xds-address=0.0.0.0")) = "--xds-address=::"' \ | $YQ '(select(.kind == "Deployment" and .metadata.name == "contour").spec.template.spec.containers[0].args) - += ["--envoy-service-http-address=::", "--envoy-service-https-address=::"]' \ + += ["--envoy-service-http-address=::", "--envoy-service-https-address=::", "--stats-address=::"]' \ | $KUBECTL apply -f - sleep 5 From 2468ca29a807c81c67e3ab0a1cf1da8d541723a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Va=C5=A1ek?= Date: Sat, 9 May 2026 15:07:37 +0200 Subject: [PATCH 2/3] fix: bridge IPv4-only DNS for CoreDNS on IPv6-only Docker clusters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker's embedded DNS proxy only listens on IPv4 (moby/moby#41651), and Kind's entrypoint replaces the node's nameserver with the IPv4 bridge gateway (e.g. 172.18.0.1). On IPv6-only clusters CoreDNS pods cannot reach this IPv4 address, breaking all DNS resolution. Work around this by detecting IPv4-only nameservers on the Kind node and installing a socat UDP/TCP proxy that forwards DNS from the node's IPv6 address to the IPv4 nameserver. The CoreDNS Corefile is then patched to forward to the node's IPv6 address instead of /etc/resolv.conf. This preserves full container-runtime DNS resolution including container names and search domains. Signed-off-by: Matej Vašek --- hack/cluster.sh | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/hack/cluster.sh b/hack/cluster.sh index 2cc18381a9..224071fd16 100755 --- a/hack/cluster.sh +++ b/hack/cluster.sh @@ -599,10 +599,32 @@ localtest.me. IN AAAA ${cluster_node_addr6}\n\ *.localtest.me. IN AAAA ${cluster_node_addr6}\n" fi + # On IPv6-only clusters, CoreDNS pods can only speak IPv6 but the node's + # /etc/resolv.conf may contain only an IPv4 nameserver (e.g. 172.18.0.1 on + # Docker). Bridge this gap with socat: proxy DNS from the node's IPv6 + # address to the IPv4 nameserver. This preserves full container-runtime DNS + # resolution (container names, search domains, etc.). + local dns_forward="forward . /etc/resolv.conf" + local node_resolv + node_resolv="$($CONTAINER_ENGINE exec func-control-plane cat /etc/resolv.conf 2>/dev/null || true)" + if [[ -n "$cluster_node_addr6" ]] && ! echo "$node_resolv" | grep -qE 'nameserver\s+[0-9a-fA-F]*:'; then + local ipv4_ns + ipv4_ns="$(echo "$node_resolv" | grep -oP 'nameserver\s+\K[0-9.]+' | head -1)" + if [[ -n "$ipv4_ns" ]]; then + echo "Node has no IPv6 nameservers — proxying DNS via socat (${cluster_node_addr6} → ${ipv4_ns})" + $CONTAINER_ENGINE exec func-control-plane bash -c " + apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq socat >/dev/null 2>&1 + socat UDP6-LISTEN:53,bind=[${cluster_node_addr6}],fork,reuseaddr UDP4:${ipv4_ns}:53 & + socat TCP6-LISTEN:53,bind=[${cluster_node_addr6}],fork,reuseaddr TCP4:${ipv4_ns}:53 & + " + dns_forward="forward . ${cluster_node_addr6}" + fi + fi + $KUBECTL patch cm/coredns -n kube-system --patch-file /dev/stdin < Date: Sun, 10 May 2026 16:52:51 +0200 Subject: [PATCH 3/3] fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matej Vašek --- hack/cluster.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hack/cluster.sh b/hack/cluster.sh index 224071fd16..007a71eff4 100755 --- a/hack/cluster.sh +++ b/hack/cluster.sh @@ -604,10 +604,15 @@ localtest.me. IN AAAA ${cluster_node_addr6}\n\ # Docker). Bridge this gap with socat: proxy DNS from the node's IPv6 # address to the IPv4 nameserver. This preserves full container-runtime DNS # resolution (container names, search domains, etc.). + # Note: the node itself may be dual-stack (Docker assigns IPv4 to the + # container regardless of Kind's ipFamily), so we check whether CoreDNS + # pods have IPv4 to determine if the cluster is truly IPv6-only. local dns_forward="forward . /etc/resolv.conf" local node_resolv node_resolv="$($CONTAINER_ENGINE exec func-control-plane cat /etc/resolv.conf 2>/dev/null || true)" - if [[ -n "$cluster_node_addr6" ]] && ! echo "$node_resolv" | grep -qE 'nameserver\s+[0-9a-fA-F]*:'; then + local coredns_ip + coredns_ip="$($KUBECTL get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].status.podIP}' 2>/dev/null || true)" + if [[ "$coredns_ip" == *":"* ]] && [[ -n "$cluster_node_addr6" ]] && ! echo "$node_resolv" | grep -qE 'nameserver\s+[0-9a-fA-F]*:'; then local ipv4_ns ipv4_ns="$(echo "$node_resolv" | grep -oP 'nameserver\s+\K[0-9.]+' | head -1)" if [[ -n "$ipv4_ns" ]]; then