fix: replace ip6tables with disable_ipv6 sysctl in vlan50 egress guard
Some checks failed
validate / lint (push) Failing after 1s

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 <noreply@anthropic.com>
This commit is contained in:
Nik Afiq 2026-08-24 22:08:34 +09:00
parent 26894d16ce
commit 7ce0dee711

View File

@ -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