Skip to content

Commit 17c05ec

Browse files
committed
fix(proxy-init): let established/related replies through enforce-drop
Address review on #484: the AB_EGRESS chain filtered purely on dest/owner/mark, so reply packets for inbound connections (which also traverse mangle OUTPUT) to an off-cluster peer would hit the terminal DROP — e.g. kubelet health-probe responses to the Kind node IP (172.18.0.0/16, outside the default CLUSTER_CIDRS=10.0.0.0/8), causing probe failures and pod restarts once enforcement is enabled. - Add `-m conntrack --ctstate ESTABLISHED,RELATED -j RETURN` as the first rule (IPv4 + IPv6). Only NEW app-initiated flows are gated; a reply is never a bypass. - IPv6: also allow link-local multicast (ff02::/16) so NDP neighbor/router solicitations work, and correct the comment that overstated NDP preservation. - README: clarify the enforce-drop injection lands in the follow-up operator PR rather than reading as already-available. - test: assert the established/related RETURN exists and is the first rule in AB_EGRESS. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
1 parent 91708b7 commit 17c05ec

3 files changed

Lines changed: 22 additions & 4 deletions

File tree

authbridge/proxy-init/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,9 @@ container automatically:
121121
- `redirect` mode (`MODE` unset) when the resolved AuthBridge mode is
122122
`envoy-sidecar`.
123123
- `enforce-drop` mode (`MODE=enforce-drop`) when `proxy-sidecar`
124-
egress enforcement is enabled (opt-in; see the kagenti-operator
125-
injector wiring).
124+
egress enforcement is enabled (opt-in). _The operator wiring that
125+
sets this lands in the follow-up kagenti-operator PR; this PR only
126+
adds the mode to the image._
126127

127128
See
128129
[`authbridge/demos/weather-agent/demo-ui-advanced.md`](../demos/weather-agent/demo-ui-advanced.md)

authbridge/proxy-init/init-iptables.sh

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ setup_enforce_drop() {
232232
# --- IPv4 ---
233233
${IPT} -t mangle -N "${CHAIN}" 2>/dev/null || true
234234
${IPT} -t mangle -F "${CHAIN}"
235+
# Replies to inbound connections (and related flows) are locally generated and
236+
# also traverse OUTPUT — a reply is never a "bypass". Let established/related
237+
# traffic through FIRST, so e.g. kubelet health-probe responses to an
238+
# off-cluster node IP (in Kind the node is 172.18.0.0/16, outside CLUSTER_CIDRS)
239+
# are not caught by the terminal DROP. Only NEW app-initiated flows are gated.
240+
${IPT} -t mangle -A "${CHAIN}" -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
235241
# ztunnel's own sockets (ambient) carry fwmark 0x539 — let them through so the
236242
# mesh/HBONE path keeps working. No-op when ambient is not installed.
237243
${IPT} -t mangle -A "${CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
@@ -256,16 +262,20 @@ setup_enforce_drop() {
256262

257263
# --- IPv6 ---
258264
# Cluster is IPv4-only by default; until v6 cluster CIDRs are wired
259-
# (CLUSTER_CIDRS6), drop all external v6 egress while allowing loopback,
260-
# link-local (NDP), the proxy UID, and ztunnel's mark.
265+
# (CLUSTER_CIDRS6), drop external v6 egress while allowing: established/related
266+
# replies, loopback, link-local unicast (fe80::/10) and link-local multicast
267+
# (ff02::/16, which carries NDP neighbor/router solicitations and MLD), the
268+
# proxy UID, and ztunnel's mark.
261269
if command -v "${IP6T%% *}" >/dev/null 2>&1 && ${IP6T} -t mangle -L >/dev/null 2>&1; then
262270
${IP6T} -t mangle -N "${CHAIN}" 2>/dev/null || true
263271
${IP6T} -t mangle -F "${CHAIN}"
272+
${IP6T} -t mangle -A "${CHAIN}" -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
264273
${IP6T} -t mangle -A "${CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
265274
${IP6T} -t mangle -A "${CHAIN}" -m owner --uid-owner "${PROXY_UID}" -j RETURN
266275
${IP6T} -t mangle -A "${CHAIN}" -o lo -j RETURN
267276
${IP6T} -t mangle -A "${CHAIN}" -d ::1/128 -j RETURN
268277
${IP6T} -t mangle -A "${CHAIN}" -d fe80::/10 -j RETURN
278+
${IP6T} -t mangle -A "${CHAIN}" -d ff02::/16 -j RETURN
269279
for cidr in $(echo "${CLUSTER_CIDRS6}" | tr ',' ' '); do
270280
[ -n "${cidr}" ] && ${IP6T} -t mangle -A "${CHAIN}" -d "${cidr}" -j RETURN
271281
done

authbridge/proxy-init/test-enforce-drop.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ echo "--- mangle ruleset ---"; echo "${dump}"
5353

5454
assert() { if echo "${dump}" | grep -qE "$2"; then echo "PASS: $1"; else echo "FAIL: $1"; fail=1; fi; }
5555
assert "AB_EGRESS hooked from OUTPUT" '^-A OUTPUT -j AB_EGRESS'
56+
assert "established/related RETURN" 'AB_EGRESS -m conntrack --ctstate (ESTABLISHED,RELATED|RELATED,ESTABLISHED) -j RETURN'
5657
assert "ztunnel mark RETURN" 'AB_EGRESS .*mark.*0x539.*-j RETURN'
5758
assert "proxy UID RETURN" 'AB_EGRESS .*--uid-owner 1337 -j RETURN'
5859
assert "loopback iface RETURN" 'AB_EGRESS -o lo -j RETURN'
@@ -64,6 +65,12 @@ pos1=$("${IPT}" -t mangle -L OUTPUT --line-numbers -n | awk '$1=="1"{print $2}')
6465
if [ "${pos1}" = "AB_EGRESS" ]; then echo "PASS: AB_EGRESS at OUTPUT position 1"
6566
else echo "FAIL: AB_EGRESS not at OUTPUT position 1 (got '${pos1}')"; fail=1; fi
6667

68+
# the established/related RETURN must be the first rule in the chain (replies
69+
# must be let through before any owner/dest evaluation).
70+
first_rule=$("${IPT}" -t mangle -S AB_EGRESS | grep '^-A AB_EGRESS' | head -1)
71+
if echo "${first_rule}" | grep -q 'conntrack'; then echo "PASS: established/related RETURN is first in AB_EGRESS"
72+
else echo "FAIL: first AB_EGRESS rule is not the conntrack RETURN (got: ${first_rule})"; fail=1; fi
73+
6774
natcount=$("${IPT}" -t nat -S | grep -cE 'AB_EGRESS|REDIRECT|PROXY_' || true)
6875
if [ "${natcount:-0}" -eq 0 ]; then echo "PASS: no nat-table rules created"
6976
else echo "FAIL: enforce-drop created nat rules"; fail=1; fi

0 commit comments

Comments
 (0)