From 7ce0dee71114c0dcafb595ad7805857964407695 Mon Sep 17 00:00:00 2001 From: Nik Afiq Date: Mon, 24 Aug 2026 22:08:34 +0900 Subject: [PATCH] fix: replace ip6tables with disable_ipv6 sysctl in vlan50 egress guard nicolaka/netshoot:v0.11 (the pinned tag actually in use) is built on Alpine 3.18.0, where ip6tables is packaged separately from iptables; this image's Dockerfile only installs the latter, so ip6tables does not exist in the container at all (confirmed live: "ip6tables: not found", and against Alpine's own v3.18 package index, not master's). Write 1 to /proc/sys/net/ipv6/conf/all/disable_ipv6 instead - per the kernel's own ip-sysctl docs this is equivalent to also setting conf/default/disable_ipv6 and every existing per-interface disable_ipv6 (lo/eth0/net1) at once, and is strictly stronger than an iptables DROP policy would have been: no IPv6 activity happens on any interface at all, not just filtered output. Needs no capability beyond the NET_ADMIN the guard init container already has. Co-Authored-By: Claude Sonnet 5 --- .../multus/vlan50-egress-guard-script.yaml | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/manifests/multus/vlan50-egress-guard-script.yaml b/manifests/multus/vlan50-egress-guard-script.yaml index fb06a9e..8b192b1 100644 --- a/manifests/multus/vlan50-egress-guard-script.yaml +++ b/manifests/multus/vlan50-egress-guard-script.yaml @@ -190,9 +190,38 @@ data: iptables -A VLAN50-GUARD -o eth0 -j DROP echo "[vlan50-egress-guard] blocking IPv6 entirely (both interfaces) — IPv4-only design" - ip6tables -P OUTPUT DROP - ip6tables -P FORWARD DROP - ip6tables -A OUTPUT -o lo -j ACCEPT + # sysctl, not an ip6tables DROP policy: nicolaka/netshoot:v0.11 (the + # pinned tag actually in use) is built on Alpine 3.18.0 per its real, + # tag-pinned Dockerfile — confirmed live, 2026-08-24, "ip6tables: not + # found". Alpine 3.18 packages ip6tables SEPARATELY from iptables + # (confirmed against Alpine's own v3.18 package index), and this + # image's Dockerfile only installs the latter, so ip6tables genuinely + # does not exist in this container at all — not a PATH issue (the + # iptables calls just above this ran fine from the same image). + # Disabling IPv6 at the netns level is also strictly stronger than a + # DROP policy would have been anyway: no IPv6 address, neighbor + # discovery, or routing activity happens on any interface here at + # all, not just filtered OUTPUT/FORWARD traffic. A single write to + # conf/all/disable_ipv6 is sufficient by itself — the kernel's own + # ip-sysctl documentation defines it as equivalent to writing + # conf/default/disable_ipv6 (for any interface created afterward) + # *and* every existing per-interface disable_ipv6 (lo/eth0/net1) all + # at once, not merely an aggregate read. Namespaced net.ipv6.* + # sysctls are writable directly by a process with CAP_NET_ADMIN in + # its own netns — no pod-spec sysctls: field is needed for this. + DISABLE_IPV6=/proc/sys/net/ipv6/conf/all/disable_ipv6 + if [ ! -w "${DISABLE_IPV6}" ]; then + echo "[vlan50-egress-guard] FATAL: ${DISABLE_IPV6} is not writable — refusing to continue without confirmed IPv6 disablement" >&2 + print_diagnostics + exit 1 + fi + echo 1 > "${DISABLE_IPV6}" + if [ "$(cat "${DISABLE_IPV6}")" != "1" ]; then + echo "[vlan50-egress-guard] FATAL: wrote 1 to ${DISABLE_IPV6} but it did not stick — refusing to continue without confirmed IPv6 disablement" >&2 + print_diagnostics + exit 1 + fi + echo "[vlan50-egress-guard] IPv6 disabled (conf/all/disable_ipv6=1)" echo "[vlan50-egress-guard] final state:" ip route show