Skip to content

Commit f884dc4

Browse files
committed
fix(proxy-init): split enforce-redirect into nat REDIRECT + mangle DROP
An end-to-end test (real transparent listener + real init-iptables.sh in a privileged container, agent dialing an off-box upstream) caught that the nat table forbids `-j DROP` ("the use of DROP is therefore inhibited"). The original enforce-redirect put the non-TCP DROP in the nat AB_REDIRECT chain, so the init script errored at runtime. Fix: two chains. - nat OUTPUT / AB_REDIRECT: exemptions + REDIRECT external TCP to TRANSPARENT_PORT (no DROP). - mangle OUTPUT / AB_NOTCP: ESTABLISHED,RELATED + same exemptions, then `-p tcp -j RETURN` (TCP falls through to the nat REDIRECT) and a terminal DROP for external non-TCP (UDP/QUIC). mangle runs before nat in the OUTPUT hook, so non-TCP drops on its original destination and TCP reaches the nat REDIRECT. Both inserted at position 1 to preempt Istio's appended chains. Updates test-enforce-redirect.sh to assert the two-chain structure (nat has no DROP; mangle AB_NOTCP has the tcp-RETURN + DROP) and reads the non-TCP DROP counter from mangle. README updated to document the two-chain design. End-to-end verified: under enforce-redirect a uid!=PROXY_UID agent dialing an external upstream directly is captured (reaches it via the transparent listener, SO_ORIGINAL_DST recovers the original dst); under enforce-drop the same connection times out; re-enabling enforce-redirect recovers delivery. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
1 parent bb15d20 commit f884dc4

3 files changed

Lines changed: 131 additions & 73 deletions

File tree

authbridge/proxy-init/README.md

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,27 +43,28 @@ default 8082), which recovers the original destination via
4343
Because nothing is dropped, agents that ignore `HTTP_PROXY` keep working
4444
— which is what lets enforcement be always-on.
4545

46-
`init-iptables.sh` builds a dedicated `AB_REDIRECT` chain hooked from
47-
**`nat` OUTPUT at position 1** (REDIRECT is a nat-table target), with
48-
this order:
49-
50-
1. `RETURN` ztunnel's own sockets (fwmark `0x539`) — no-op without ambient.
51-
2. `RETURN` the proxy's own re-originated egress (`--uid-owner $PROXY_UID`, default 1337) — avoids the redirect loop.
52-
3. `RETURN` loopback (the app → forward proxy hop) and in-cluster CIDRs (`CLUSTER_CIDRS`, mesh/DNS) — left direct.
53-
4. `REDIRECT` external **TCP** to `TRANSPARENT_PORT` — captured.
54-
5. `DROP` everything else — external **non-TCP** (UDP/QUIC), so HTTP/3 cannot bypass; well-behaved clients fall back to TCP and get captured.
55-
56-
An IPv6 mirror applies the same exemptions, REDIRECTs external v6 TCP,
57-
and drops other v6 egress. There is no conntrack `ESTABLISHED` rule —
58-
nat only evaluates the first packet of a flow, so replies and
59-
established connections are not re-translated.
60-
61-
`enforce-redirect` is inserted at `nat OUTPUT` position 1, ahead of
62-
Istio's appended (`-A`) `ISTIO_OUTPUT` chain, so it preempts ambient's
63-
nat redirect for external destinations — exactly as `redirect` mode does
64-
for the Envoy path. See
46+
`init-iptables.sh` installs **two** chains, because `REDIRECT` is a
47+
nat-table target but the nat table forbids `DROP` (`iptables` errors with
48+
"the use of DROP is therefore inhibited"):
49+
50+
- **`nat` OUTPUT / `AB_REDIRECT`** (position 1): `RETURN` ztunnel mark
51+
`0x539`, the proxy UID (`--uid-owner $PROXY_UID`, avoids the loop),
52+
loopback, and `CLUSTER_CIDRS`; then `REDIRECT` external **TCP** to
53+
`TRANSPARENT_PORT`.
54+
- **`mangle` OUTPUT / `AB_NOTCP`** (position 1): the same exemptions
55+
(plus `ESTABLISHED,RELATED` first, so UDP conntrack replies like DNS
56+
pass), then `-p tcp -j RETURN` (TCP is handled by the nat REDIRECT) and
57+
a terminal `DROP` for external **non-TCP** (UDP/QUIC), so HTTP/3 cannot
58+
bypass — well-behaved clients fall back to TCP and get captured.
59+
60+
Because the OUTPUT hook order is `raw → mangle → nat → filter`, the
61+
mangle chain drops non-TCP on its original destination while TCP falls
62+
through to the nat REDIRECT. Both chains are inserted at position 1,
63+
ahead of Istio's appended (`-A`) chains, so they preempt ambient's nat
64+
redirect for external destinations — exactly as `redirect` mode does for
65+
the Envoy path. IPv6 mirrors apply the same rules. See
6566
[`test-enforce-redirect.sh`](./test-enforce-redirect.sh), which proves
66-
both the capture and the preemption via packet counters.
67+
the capture, the preemption, and the non-TCP drop via packet counters.
6768

6869
`CLUSTER_CIDRS` has the same Kind-shaped default and OCP/EKS override
6970
requirement as documented under `enforce-drop` below.

authbridge/proxy-init/init-iptables.sh

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -333,35 +333,62 @@ setup_enforce_drop() {
333333
# Unlike enforce-drop there is no conntrack ESTABLISHED rule: nat only evaluates
334334
# the first packet of a flow, so replies and established connections are not
335335
# re-translated.
336+
# Two chains are needed because REDIRECT is a nat-table target but the nat table
337+
# forbids DROP ("the use of DROP is therefore inhibited"):
338+
# * nat OUTPUT / AB_REDIRECT — REDIRECT external TCP to TRANSPARENT_PORT.
339+
# * mangle OUTPUT / AB_NOTCP — DROP external non-TCP (UDP/QUIC) so HTTP/3
340+
# cannot bypass; `-p tcp -j RETURN` lets TCP
341+
# fall through to the nat REDIRECT.
342+
# mangle runs before nat in the OUTPUT hook, so non-TCP is dropped on its
343+
# original destination and TCP is passed to the nat REDIRECT. Both are inserted
344+
# at position 1 to precede Istio's appended chains.
336345
setup_enforce_redirect() {
337-
CHAIN="AB_REDIRECT"
346+
REDIR_CHAIN="AB_REDIRECT"
347+
NOTCP_CHAIN="AB_NOTCP"
338348

339-
echo "enforce-redirect: installing fail-closed egress capture (nat OUTPUT, chain ${CHAIN})"
340-
echo "enforce-redirect: external TCP -> 127.0.0.1:${TRANSPARENT_PORT}; exempt proxy UID=${PROXY_UID}; direct in-cluster CIDRs=${CLUSTER_CIDRS}"
349+
echo "enforce-redirect: installing fail-closed egress capture"
350+
echo "enforce-redirect: external TCP -> 127.0.0.1:${TRANSPARENT_PORT} (nat REDIRECT); external non-TCP -> DROP (mangle)"
351+
echo "enforce-redirect: exempt proxy UID=${PROXY_UID}; direct in-cluster CIDRs=${CLUSTER_CIDRS}"
341352

342-
# --- IPv4 ---
343-
${IPT} -t nat -N "${CHAIN}" 2>/dev/null || true
344-
${IPT} -t nat -F "${CHAIN}"
353+
# --- IPv4: nat REDIRECT for TCP ---
354+
${IPT} -t nat -N "${REDIR_CHAIN}" 2>/dev/null || true
355+
${IPT} -t nat -F "${REDIR_CHAIN}"
345356
# ztunnel's own sockets (ambient) carry fwmark 0x539 — let them through.
346-
${IPT} -t nat -A "${CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
357+
${IPT} -t nat -A "${REDIR_CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
347358
# the AuthBridge proxy's own re-originated egress (runs as PROXY_UID) — avoids
348359
# redirecting the proxy's upstream dial back into itself.
349-
${IPT} -t nat -A "${CHAIN}" -m owner --uid-owner "${PROXY_UID}" -j RETURN
360+
${IPT} -t nat -A "${REDIR_CHAIN}" -m owner --uid-owner "${PROXY_UID}" -j RETURN
350361
# app -> forward proxy over loopback (HTTP_PROXY target), and any loopback.
351-
${IPT} -t nat -A "${CHAIN}" -o lo -j RETURN
352-
${IPT} -t nat -A "${CHAIN}" -d 127.0.0.0/8 -j RETURN
362+
${IPT} -t nat -A "${REDIR_CHAIN}" -o lo -j RETURN
363+
${IPT} -t nat -A "${REDIR_CHAIN}" -d 127.0.0.0/8 -j RETURN
353364
# in-cluster traffic (pods / services / DNS) — left direct, carried by the mesh.
354365
for cidr in $(echo "${CLUSTER_CIDRS}" | tr ',' ' '); do
355-
[ -n "${cidr}" ] && ${IPT} -t nat -A "${CHAIN}" -d "${cidr}" -j RETURN
366+
[ -n "${cidr}" ] && ${IPT} -t nat -A "${REDIR_CHAIN}" -d "${cidr}" -j RETURN
356367
done
357368
# external TCP that bypassed the forward proxy — capture it transparently.
358-
${IPT} -t nat -A "${CHAIN}" -p tcp -j REDIRECT --to-port "${TRANSPARENT_PORT}"
359-
# external non-TCP (UDP/QUIC) — cannot be redirected to a TCP listener; drop so
360-
# HTTP/3 cannot bypass. Clients fall back to TCP, which the rule above captures.
361-
${IPT} -t nat -A "${CHAIN}" -j DROP
362-
# Hook at position 1 so we run before any appended Istio nat chain.
363-
if ! ${IPT} -t nat -C OUTPUT -j "${CHAIN}" 2>/dev/null; then
364-
${IPT} -t nat -I OUTPUT 1 -j "${CHAIN}"
369+
${IPT} -t nat -A "${REDIR_CHAIN}" -p tcp -j REDIRECT --to-port "${TRANSPARENT_PORT}"
370+
if ! ${IPT} -t nat -C OUTPUT -j "${REDIR_CHAIN}" 2>/dev/null; then
371+
${IPT} -t nat -I OUTPUT 1 -j "${REDIR_CHAIN}"
372+
fi
373+
374+
# --- IPv4: mangle DROP for non-TCP ---
375+
${IPT} -t mangle -N "${NOTCP_CHAIN}" 2>/dev/null || true
376+
${IPT} -t mangle -F "${NOTCP_CHAIN}"
377+
# established/related replies (incl. UDP conntrack, e.g. DNS replies) first.
378+
${IPT} -t mangle -A "${NOTCP_CHAIN}" -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
379+
${IPT} -t mangle -A "${NOTCP_CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
380+
${IPT} -t mangle -A "${NOTCP_CHAIN}" -m owner --uid-owner "${PROXY_UID}" -j RETURN
381+
${IPT} -t mangle -A "${NOTCP_CHAIN}" -o lo -j RETURN
382+
${IPT} -t mangle -A "${NOTCP_CHAIN}" -d 127.0.0.0/8 -j RETURN
383+
for cidr in $(echo "${CLUSTER_CIDRS}" | tr ',' ' '); do
384+
[ -n "${cidr}" ] && ${IPT} -t mangle -A "${NOTCP_CHAIN}" -d "${cidr}" -j RETURN
385+
done
386+
# TCP is handled by the nat REDIRECT above — let it pass mangle untouched.
387+
${IPT} -t mangle -A "${NOTCP_CHAIN}" -p tcp -j RETURN
388+
# everything else == external non-TCP egress (UDP/QUIC) — drop it.
389+
${IPT} -t mangle -A "${NOTCP_CHAIN}" -j DROP
390+
if ! ${IPT} -t mangle -C OUTPUT -j "${NOTCP_CHAIN}" 2>/dev/null; then
391+
${IPT} -t mangle -I OUTPUT 1 -j "${NOTCP_CHAIN}"
365392
fi
366393
echo "enforce-redirect: IPv4 egress capture configured"
367394

@@ -370,21 +397,38 @@ setup_enforce_redirect() {
370397
# loopback + link-local (fe80::/10 unicast, ff02::/16 NDP/MLD multicast) and
371398
# the proxy UID / ztunnel mark; REDIRECT external v6 TCP; DROP other v6 egress.
372399
if command -v "${IP6T%% *}" >/dev/null 2>&1 && ${IP6T} -t nat -L >/dev/null 2>&1; then
373-
${IP6T} -t nat -N "${CHAIN}" 2>/dev/null || true
374-
${IP6T} -t nat -F "${CHAIN}"
375-
${IP6T} -t nat -A "${CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
376-
${IP6T} -t nat -A "${CHAIN}" -m owner --uid-owner "${PROXY_UID}" -j RETURN
377-
${IP6T} -t nat -A "${CHAIN}" -o lo -j RETURN
378-
${IP6T} -t nat -A "${CHAIN}" -d ::1/128 -j RETURN
379-
${IP6T} -t nat -A "${CHAIN}" -d fe80::/10 -j RETURN
380-
${IP6T} -t nat -A "${CHAIN}" -d ff02::/16 -j RETURN
400+
${IP6T} -t nat -N "${REDIR_CHAIN}" 2>/dev/null || true
401+
${IP6T} -t nat -F "${REDIR_CHAIN}"
402+
${IP6T} -t nat -A "${REDIR_CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
403+
${IP6T} -t nat -A "${REDIR_CHAIN}" -m owner --uid-owner "${PROXY_UID}" -j RETURN
404+
${IP6T} -t nat -A "${REDIR_CHAIN}" -o lo -j RETURN
405+
${IP6T} -t nat -A "${REDIR_CHAIN}" -d ::1/128 -j RETURN
406+
${IP6T} -t nat -A "${REDIR_CHAIN}" -d fe80::/10 -j RETURN
407+
${IP6T} -t nat -A "${REDIR_CHAIN}" -d ff02::/16 -j RETURN
408+
for cidr in $(echo "${CLUSTER_CIDRS6}" | tr ',' ' '); do
409+
[ -n "${cidr}" ] && ${IP6T} -t nat -A "${REDIR_CHAIN}" -d "${cidr}" -j RETURN
410+
done
411+
${IP6T} -t nat -A "${REDIR_CHAIN}" -p tcp -j REDIRECT --to-port "${TRANSPARENT_PORT}"
412+
if ! ${IP6T} -t nat -C OUTPUT -j "${REDIR_CHAIN}" 2>/dev/null; then
413+
${IP6T} -t nat -I OUTPUT 1 -j "${REDIR_CHAIN}"
414+
fi
415+
416+
${IP6T} -t mangle -N "${NOTCP_CHAIN}" 2>/dev/null || true
417+
${IP6T} -t mangle -F "${NOTCP_CHAIN}"
418+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
419+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -m mark --mark "${ZTUNNEL_MARK}" -j RETURN
420+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -m owner --uid-owner "${PROXY_UID}" -j RETURN
421+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -o lo -j RETURN
422+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -d ::1/128 -j RETURN
423+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -d fe80::/10 -j RETURN
424+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -d ff02::/16 -j RETURN
381425
for cidr in $(echo "${CLUSTER_CIDRS6}" | tr ',' ' '); do
382-
[ -n "${cidr}" ] && ${IP6T} -t nat -A "${CHAIN}" -d "${cidr}" -j RETURN
426+
[ -n "${cidr}" ] && ${IP6T} -t mangle -A "${NOTCP_CHAIN}" -d "${cidr}" -j RETURN
383427
done
384-
${IP6T} -t nat -A "${CHAIN}" -p tcp -j REDIRECT --to-port "${TRANSPARENT_PORT}"
385-
${IP6T} -t nat -A "${CHAIN}" -j DROP
386-
if ! ${IP6T} -t nat -C OUTPUT -j "${CHAIN}" 2>/dev/null; then
387-
${IP6T} -t nat -I OUTPUT 1 -j "${CHAIN}"
428+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -p tcp -j RETURN
429+
${IP6T} -t mangle -A "${NOTCP_CHAIN}" -j DROP
430+
if ! ${IP6T} -t mangle -C OUTPUT -j "${NOTCP_CHAIN}" 2>/dev/null; then
431+
${IP6T} -t mangle -I OUTPUT 1 -j "${NOTCP_CHAIN}"
388432
fi
389433
echo "enforce-redirect: IPv6 egress capture configured"
390434
else

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

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
#
66
# It validates, in a private network namespace:
77
# 1. Rule STRUCTURE — the AB_REDIRECT chain is hooked from nat OUTPUT at
8-
# position 1 with the expected RETURN exemptions, a `-p tcp` REDIRECT to
9-
# TRANSPARENT_PORT, and a terminal DROP; and no mangle rules are created.
8+
# position 1 with the expected RETURN exemptions and a `-p tcp` REDIRECT to
9+
# TRANSPARENT_PORT (no DROP — the nat table forbids it); and the AB_NOTCP
10+
# chain is hooked from mangle OUTPUT with `-p tcp RETURN` then a terminal
11+
# DROP for external non-TCP egress.
1012
# 2. CAPTURE (not drop) + AMBIENT ROBUSTNESS — external TCP egress is
1113
# REDIRECTed to TRANSPARENT_PORT, preempting a simulated Istio ambient
1214
# "nat OUTPUT REDIRECT" appended after our chain. Proven via packet
1315
# counters: our REDIRECT increments, the simulated ISTIO REDIRECT does not.
1416
# 3. NON-TCP DROP — an external UDP datagram (QUIC/HTTP-3 bypass attempt) hits
15-
# the terminal DROP, proving non-TCP external egress cannot bypass.
17+
# the mangle AB_NOTCP DROP, proving non-TCP external egress cannot bypass.
1618
#
1719
# Requirements: root (for unshare --net + iptables), iproute2, iptables-nft,
1820
# bash, the dummy kernel module. Runs on Linux / CI (e.g. ubuntu-latest); not on
@@ -51,26 +53,37 @@ env MODE=enforce-redirect PROXY_UID=1337 CLUSTER_CIDRS=10.0.0.0/8 \
5153
IPTABLES_CMD="${IPT}" IP6TABLES_CMD=ip6tables-nft \
5254
sh "${INIT}" || { echo "FAIL: init script exited non-zero"; exit 1; }
5355

54-
dump=$("${IPT}" -t nat -S)
55-
echo "--- nat ruleset ---"; echo "${dump}"
56+
natdump=$("${IPT}" -t nat -S)
57+
mangledump=$("${IPT}" -t mangle -S)
58+
echo "--- nat ruleset ---"; echo "${natdump}"
59+
echo "--- mangle ruleset ---"; echo "${mangledump}"
5660

57-
assert() { if echo "${dump}" | grep -qE "$2"; then echo "PASS: $1"; else echo "FAIL: $1"; fail=1; fi; }
58-
assert "AB_REDIRECT hooked from OUTPUT" '^-A OUTPUT -j AB_REDIRECT'
59-
assert "ztunnel mark RETURN" 'AB_REDIRECT .*mark.*0x539.*-j RETURN'
60-
assert "proxy UID RETURN" 'AB_REDIRECT .*--uid-owner 1337 -j RETURN'
61-
assert "loopback iface RETURN" 'AB_REDIRECT -o lo -j RETURN'
62-
assert "loopback cidr RETURN" 'AB_REDIRECT -d 127.0.0.0/8 -j RETURN'
63-
assert "cluster cidr RETURN" 'AB_REDIRECT -d 10.0.0.0/8 -j RETURN'
64-
assert "tcp REDIRECT to transparent" "AB_REDIRECT -p tcp -j REDIRECT --to-ports ${TPORT}"
65-
assert "terminal DROP (non-tcp)" 'AB_REDIRECT -j DROP'
61+
assert() { if echo "$3" | grep -qE "$2"; then echo "PASS: $1"; else echo "FAIL: $1"; fail=1; fi; }
62+
# nat AB_REDIRECT — TCP capture (no DROP; nat forbids it).
63+
assert "AB_REDIRECT hooked from nat OUTPUT" '^-A OUTPUT -j AB_REDIRECT' "${natdump}"
64+
assert "nat ztunnel mark RETURN" 'AB_REDIRECT .*mark.*0x539.*-j RETURN' "${natdump}"
65+
assert "nat proxy UID RETURN" 'AB_REDIRECT .*--uid-owner 1337 -j RETURN' "${natdump}"
66+
assert "nat loopback iface RETURN" 'AB_REDIRECT -o lo -j RETURN' "${natdump}"
67+
assert "nat loopback cidr RETURN" 'AB_REDIRECT -d 127.0.0.0/8 -j RETURN' "${natdump}"
68+
assert "nat cluster cidr RETURN" 'AB_REDIRECT -d 10.0.0.0/8 -j RETURN' "${natdump}"
69+
assert "nat tcp REDIRECT to transparent" "AB_REDIRECT -p tcp -j REDIRECT --to-ports ${TPORT}" "${natdump}"
70+
if echo "${natdump}" | grep -qE 'AB_REDIRECT -j DROP'; then
71+
echo "FAIL: nat AB_REDIRECT must not contain DROP (nat table forbids it)"; fail=1
72+
else echo "PASS: nat AB_REDIRECT has no DROP (correctly delegated to mangle)"; fi
73+
# mangle AB_NOTCP — non-TCP drop, TCP passes through to the nat REDIRECT.
74+
assert "AB_NOTCP hooked from mangle OUTPUT" '^-A OUTPUT -j AB_NOTCP' "${mangledump}"
75+
assert "mangle established/related RETURN" 'AB_NOTCP -m conntrack --ctstate (ESTABLISHED,RELATED|RELATED,ESTABLISHED) -j RETURN' "${mangledump}"
76+
assert "mangle proxy UID RETURN" 'AB_NOTCP .*--uid-owner 1337 -j RETURN' "${mangledump}"
77+
assert "mangle cluster cidr RETURN" 'AB_NOTCP -d 10.0.0.0/8 -j RETURN' "${mangledump}"
78+
assert "mangle tcp RETURN (defer to nat)" 'AB_NOTCP -p tcp -j RETURN' "${mangledump}"
79+
assert "mangle terminal DROP (non-tcp)" 'AB_NOTCP -j DROP' "${mangledump}"
6680

6781
pos1=$("${IPT}" -t nat -L OUTPUT --line-numbers -n | awk '$1=="1"{print $2}')
68-
if [ "${pos1}" = "AB_REDIRECT" ]; then echo "PASS: AB_REDIRECT at OUTPUT position 1"
69-
else echo "FAIL: AB_REDIRECT not at OUTPUT position 1 (got '${pos1}')"; fail=1; fi
70-
71-
manglecount=$("${IPT}" -t mangle -S | grep -cE 'AB_REDIRECT|AB_EGRESS' || true)
72-
if [ "${manglecount:-0}" -eq 0 ]; then echo "PASS: no mangle-table rules created"
73-
else echo "FAIL: enforce-redirect created mangle rules"; fail=1; fi
82+
if [ "${pos1}" = "AB_REDIRECT" ]; then echo "PASS: AB_REDIRECT at nat OUTPUT position 1"
83+
else echo "FAIL: AB_REDIRECT not at nat OUTPUT position 1 (got '${pos1}')"; fail=1; fi
84+
mpos1=$("${IPT}" -t mangle -L OUTPUT --line-numbers -n | awk '$1=="1"{print $2}')
85+
if [ "${mpos1}" = "AB_NOTCP" ]; then echo "PASS: AB_NOTCP at mangle OUTPUT position 1"
86+
else echo "FAIL: AB_NOTCP not at mangle OUTPUT position 1 (got '${mpos1}')"; fail=1; fi
7487

7588
echo "### Capture + preemption test: append a simulated ISTIO_OUTPUT nat REDIRECT"
7689
"${IPT}" -t nat -A OUTPUT -p tcp -d "${EXTERNAL}" -j REDIRECT --to-ports 19999
@@ -89,8 +102,8 @@ fi
89102

90103
echo "### Non-TCP drop test: send an external UDP datagram (QUIC bypass attempt)"
91104
timeout 2 bash -c "echo -n x >/dev/udp/${EXTERNAL}/53" 2>/dev/null || true
92-
dropc=$("${IPT}" -t nat -L AB_REDIRECT -n -v | awk '/DROP/{print $1; exit}')
93-
echo "AB_REDIRECT DROP pkts=${dropc:-?}"
105+
dropc=$("${IPT}" -t mangle -L AB_NOTCP -n -v | awk '/DROP/{print $1; exit}')
106+
echo "mangle AB_NOTCP DROP pkts=${dropc:-?}"
94107
if [ "${dropc:-0}" -gt 0 ]; then
95108
echo "PASS: external UDP dropped (HTTP/3 cannot bypass)"
96109
else

0 commit comments

Comments
 (0)