feat: Add transparent listener + enforce-redirect for proxy-sidecar egress#485
Conversation
…gress proxy-sidecar egress enforcement could previously only DROP bypass traffic (MODE=enforce-drop), because the forward proxy only speaks explicit CONNECT — an iptables-REDIRECTed raw connection arrives with no destination it can recover, so there was nothing to redirect to. DROP is fail-closed but breaks agents that ignore HTTP_PROXY, which is why it stayed opt-in. This adds the missing half: a transparent listener that captures redirected egress, so enforcement can REDIRECT instead of DROP (capture, do not break). - authlib/listener/transparentproxy: an outbound transparent listener that recovers the original destination via SO_ORIGINAL_DST (Linux, build-tagged; pure cross-platform sockaddr parser with a unit test) and dispatches to a ConnHandler. This is the Go analogue of Envoy's original_dst listener filter. - forwardproxy.Server.HandleTransparentConn: gates on destination then blind-tunnels, reusing the exact CONNECT-path pipeline + tunnel (extracted into shared recordTunnelOpened/tunnel helpers). Emits no proxy-protocol bytes so the agent's end-to-end TLS is preserved. - Wire the listener into authbridge-proxy and authbridge-lite (proxy-sidecar shapes); new listener.transparent_proxy_addr config, default :8082. - init-iptables.sh MODE=enforce-redirect: nat OUTPUT chain that REDIRECTs external TCP to TRANSPARENT_PORT (exempting proxy UID, loopback, cluster CIDRs, ztunnel mark) and DROPs non-TCP external egress so HTTP/3 cannot bypass. Inserted at position 1 to preempt Istio ambient's nat redirect. - test-enforce-redirect.sh: netns harness proving capture + ambient preemption + non-TCP drop via packet counters. README documents the new mode. enforce-drop is retained as a no-transparent-listener fallback. Inert until the operator wires MODE=enforce-redirect (companion kagenti-operator PR). Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
|
Looking for one thing? Review this PR in Change Stack to search files, summaries, diffs, and code without losing your place. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds an optional outbound transparent-proxy listener and supporting plumbing for proxy-sidecar "enforce-redirect": config default, Linux original-destination recovery, transparent listener server, forward-proxy sniffing/tunneling and session events, binary wiring, iptables setup and tests, and README updates. ChangesTransparent Proxy Listener
Sequence Diagram (high-level transparent tunnel flow): sequenceDiagram
participant Client
participant HandleTransparent as HandleTransparentConn
participant Pipeline
participant Upstream
Client->>HandleTransparent: clientConn, dst
HandleTransparent->>Pipeline: OutboundPipeline.Run
alt Pipeline allows
HandleTransparent->>Upstream: Dial dst
HandleTransparent->>HandleTransparent: recordTunnelOpened
HandleTransparent->>HandleTransparent: tunnel(client, upstream)
loop io.Copy both directions
Client<<-->>Upstream: Transparent data
end
else Pipeline denies
HandleTransparent->>Client: Close
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
authbridge/cmd/authbridge-proxy/main.go (1)
396-422: 💤 Low valueConsider extracting
startTransparentProxyto a shared helper.This function is nearly identical to the one in
authbridge-lite/main.go(lines 352-377). Both could delegate to a shared helper inauthlibto reduce duplication. That said, the current duplication is minimal (~25 lines) and may be intentional to keep binary entrypoints independent.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@authbridge/cmd/authbridge-proxy/main.go` around lines 396 - 422, The startTransparentProxy function is duplicated; extract its logic into a single shared helper in authlib (e.g., StartTransparentProxy) and have both startTransparentProxy callers delegate to it. Implement a helper that takes the forward proxy handler (or fp *forwardproxy.Server and its HandleTransparentConn) and addr string, resolves and listens on the TCP address, creates transparentproxy.NewServer(fp.HandleTransparentConn), serves in a goroutine with the same logging/fatal-on-error behavior, and returns the *net.TCPListener; then replace the duplicate implementations (startTransparentProxy in both entrypoints) with calls to the new authlib helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@authbridge/authlib/config/presets.go`:
- Around line 19-23: The presets file currently calls
setDefault(&cfg.Listener.TransparentProxyAddr, ":8082") which overwrites an
explicitly-empty value and breaks the TransparentProxyAddr doc-comment
semantics; change this so empty string continues to mean “disabled”: remove that
setDefault call and instead set the default only when the value is truly absent
(e.g., introduce and use a new helper like setDefaultIfAbsent for string fields
or adjust setDefault to detect an explicit empty vs. absent value) and apply it
to cfg.Listener.TransparentProxyAddr so that TransparentProxyAddr == "" remains
unmodified and retains the “disable listener” behavior.
In `@authbridge/authlib/listener/transparentproxy/server.go`:
- Around line 36-37: NewServer currently stores the provided ConnHandler
directly and a nil handle will panic at dispatch (s.handle(conn, dst)); modify
NewServer to guard against nil by replacing a nil handle with a safe no-op
ConnHandler (for example a closure that logs and closes the connection or
returns immediately) so dispatch won't panic. Update NewServer to set
Server.handle = handle if non-nil else assign the safe default; also audit the
call site referenced by s.handle(conn, dst) and ensure you do not call a nil
handler elsewhere (the same nil-guard pattern should apply wherever Server is
constructed).
In `@authbridge/proxy-init/init-iptables.sh`:
- Around line 353-361: The current iptables rules in the script add a TCP
REDIRECT for all external TCP (using ${IPT}, ${CHAIN}, and ${TRANSPARENT_PORT})
but do not exclude SSH; add a rule that matches TCP destination port ${SSH_PORT}
and issues a RETURN (i.e., ${IPT} -t nat -A "${CHAIN}" -p tcp --dport
"${SSH_PORT}" -j RETURN) immediately before any REDIRECT entries that redirect
TCP to ${TRANSPARENT_PORT} (this must be done for each place that adds the
REDIRECT for TCP so SSH traffic is returned instead of redirected).
In `@authbridge/proxy-init/README.md`:
- Around line 11-12: The README mode table currently lists `enforce-redirect`
and `enforce-drop` as applicable only to `proxy-sidecar`, but the injection
section (lines ~186-192) also references `lite`; update the top mode table
entries for `enforce-redirect` and `enforce-drop` to include `lite` (or remove
`lite` from the injection section if that is intended) so both sections are
consistent; look for the mode strings `enforce-redirect`, `enforce-drop`,
`proxy-sidecar`, and `lite` in the README and make the table and injection
section match.
---
Nitpick comments:
In `@authbridge/cmd/authbridge-proxy/main.go`:
- Around line 396-422: The startTransparentProxy function is duplicated; extract
its logic into a single shared helper in authlib (e.g., StartTransparentProxy)
and have both startTransparentProxy callers delegate to it. Implement a helper
that takes the forward proxy handler (or fp *forwardproxy.Server and its
HandleTransparentConn) and addr string, resolves and listens on the TCP address,
creates transparentproxy.NewServer(fp.HandleTransparentConn), serves in a
goroutine with the same logging/fatal-on-error behavior, and returns the
*net.TCPListener; then replace the duplicate implementations
(startTransparentProxy in both entrypoints) with calls to the new authlib
helper.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 4adda382-a3c0-4690-8bbc-3996b9ea155f
📒 Files selected for processing (16)
authbridge/authlib/config/config.goauthbridge/authlib/config/presets.goauthbridge/authlib/go.modauthbridge/authlib/listener/forwardproxy/server.goauthbridge/authlib/listener/forwardproxy/transparent.goauthbridge/authlib/listener/forwardproxy/transparent_test.goauthbridge/authlib/listener/transparentproxy/origdst_linux.goauthbridge/authlib/listener/transparentproxy/origdst_other.goauthbridge/authlib/listener/transparentproxy/server.goauthbridge/authlib/listener/transparentproxy/sockaddr.goauthbridge/authlib/listener/transparentproxy/sockaddr_test.goauthbridge/cmd/authbridge-lite/main.goauthbridge/cmd/authbridge-proxy/main.goauthbridge/proxy-init/README.mdauthbridge/proxy-init/init-iptables.shauthbridge/proxy-init/test-enforce-redirect.sh
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>
End-to-end verification (capture vs. drop)Ran the real transparent listener ( The This test also caught a bug, now fixed in f884dc4: the nat table forbids Assisted-By: Claude (Anthropic AI) noreply@anthropic.com |
- config: clarify TransparentProxyAddr doc — the proxy-sidecar/lite presets default it to :8082 (always-on by design); empty only disables it for modes without a preset default. Removes the misleading 'empty disables' claim that the preset contradicts. - transparentproxy.NewServer: guard against a nil ConnHandler (fall back to a close-and-log handler) so a misconfiguration degrades to no-capture instead of a dispatch-time panic. - proxy-init README: list 'lite' alongside 'proxy-sidecar' in the mode table to match the injection section. Declined: SSH (port 22) exclusion for enforce-redirect. Excluding 22 would punch a deliberate egress bypass hole, contradicting the fail-closed purpose; the transparent listener blind-tunnels any TCP so captured SSH still works (unlike redirect mode, which forwards to Envoy's HTTP listener — the reason it excludes 22). enforce-drop does not exclude 22 either. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
Address follow-up review on the transparent listener: - transparentproxy.dispatch: drop connections whose SO_ORIGINAL_DST is a loopback address. A genuinely REDIRECTed connection never has a loopback original destination (the enforce-redirect iptables RETURN loopback before the REDIRECT), so a loopback dst means a direct dial to the listener port where SO_ORIGINAL_DST reports the listener's own address — tunnelling to it would spiral into ever more connections/goroutines. Defense-in-depth. - HandleTransparentConn: document the gating-key difference. CONNECT gates on the hostname (r.Host); captured traffic gates on the SO_ORIGINAL_DST IP:port, so domain-based egress policy does not match captured bypass traffic the way it matches explicit-proxy traffic. Acceptable while opt-in; the always-on operator PR must first add SNI-peek for parity or consciously accept IP-level policy. Tracked as a prerequisite for the default flip. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
Follow-up review items — resolved (commit 6646b57)1. Capture gates on IP:port (SO_ORIGINAL_DST) vs CONNECT's hostname — host/domain policy gap. 2. No guard against a recovered dst pointing at the listener itself (self-redirect spiral). 3. Assisted-By: Claude (Anthropic AI) noreply@anthropic.com |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@authbridge/authlib/listener/transparentproxy/server.go`:
- Around line 79-92: The current loop-guard only drops loopback dsts but misses
self-dials to the listener's non-loopback address; update the check (in the
block handling dst before calling HandleTransparentConn) to also detect and drop
connections whose original destination equals the listener itself: parse dst
with net.SplitHostPort/net.ParseIP, compare the host:port to the listener's own
address (listener.Addr().String() or the configured listenAddr) and/or check if
the dst IP matches any of the machine's local interface addresses
(net.InterfaceAddrs()) with the same port; if it matches, log a warning (same
style as existing slog.Warn) and conn.Close() to prevent recursive re-dials via
HandleTransparentConn.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 82a63596-9874-4d48-8cc0-2c6d786b6a45
📒 Files selected for processing (2)
authbridge/authlib/listener/forwardproxy/transparent.goauthbridge/authlib/listener/transparentproxy/server.go
🚧 Files skipped from review as they are similar to previous changes (1)
- authbridge/authlib/listener/forwardproxy/transparent.go
… policy parity Captured (iptables-REDIRECTed) traffic previously gated on the SO_ORIGINAL_DST IP:port, while the explicit-proxy CONNECT path gates on the hostname — so host/domain egress policy did not apply to captured bypass traffic the same way. Add forwardproxy/sniff.go: peek the connection's first bytes (without consuming them — replayed into the tunnel via a bufio-backed peekedConn) and recover the destination hostname: - HTTPS: parse the TLS ClientHello SNI, leaning on crypto/tls's own parser via a throwaway GetConfigForClient handshake (no hand-rolled ClientHello parsing). - HTTP: parse the Host header from the request preamble. Fall back to the IP when neither is recoverable. HandleTransparentConn uses the recovered name as pctx.Host; the dial target ALWAYS stays the SO_ORIGINAL_DST IP (never re-resolve the name). Sniffing is gated to HTTP/TLS-typical ports (80/443/8080/8443) so non-HTTP, often server-first protocols (SSH, SMTP, ...) are not delayed by the peek; a read deadline bounds it regardless. Tests: real ClientHello (via crypto/tls) yields the SNI; a plaintext request yields the Host header (port stripped); opaque bytes yield no name; all three prove the peeked bytes are replayed verbatim. End-to-end (container) verified: an agent dialing the IP while sending Host: api.example.test has the listener recover api.example.test, and capture-vs-drop still holds. Trust caveat (documented): for captured traffic the agent controls both the name and the IP, so name-based policy is reliable against a cooperative or misconfigured agent but is not a hard control against a hostile one — only the IP is ground truth. The always-on operator PR must decide that threat model. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
Update: hostname recovery now implemented (commit 66f82e9)Earlier I deferred the IP-vs-hostname gating gap to the operator PR. On reflection it was worth doing here, for both HTTP and HTTPS, so captured traffic gates the same way the CONNECT path does:
The peeked bytes are replayed into the tunnel verbatim (bufio-backed Tests: real ClientHello → SNI; plaintext request → Host header (port stripped); opaque bytes → no name; all assert verbatim replay. End-to-end (container) confirmed: an agent dialing the IP while sending Residual caveat (left for the always-on operator PR): for captured traffic the agent controls both the name and the IP the bytes actually go to, so name-based policy is reliable against a cooperative/misconfigured agent but is not a hard control against a hostile one — only the IP is ground truth. Hard enforcement would need IP-set allowlists or SNI/cert cross-checks; that's a threat-model decision for the default flip, now documented in Assisted-By: Claude (Anthropic AI) noreply@anthropic.com |
enforce-drop existed only because, before the transparent listener, a REDIRECT had no receiver so DROP was the only fail-closed option. Now that every proxy-sidecar/lite image ships the transparent listener, enforce-redirect captures bypass egress instead of dropping it, and the operator only emits enforce-redirect. enforce-drop is dead. Remove the enforce-drop MODE (setup_enforce_drop + AB_EGRESS chain + the dispatch + the mode-table/env-var/injection docs) and test-enforce-drop.sh. The init script now accepts only redirect | enforce-redirect. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
mrsabath
left a comment
There was a problem hiding this comment.
Solid, production-careful PR — approving.
What it does: adds the "capture instead of drop" half of proxy-sidecar egress enforcement. The new transparentproxy listener recovers the original destination of iptables-REDIRECTed connections via SO_ORIGINAL_DST and tunnels them through the same outbound auth pipeline as the CONNECT path (shared recordTunnelOpened/tunnel helpers), emitting no proxy-protocol bytes so the agent's end-to-end TLS is preserved. enforce-drop becomes enforce-redirect (nat REDIRECT for TCP + mangle DROP for non-TCP, position-1 to preempt ambient).
Why it's in good shape:
- The design reasoning is captured where it matters: the nat-vs-mangle split (nat forbids DROP), the OUTPUT hook ordering for ambient preemption, SNI/Host sniffing with the IP kept as ground truth, and the explicit trust caveat that name-based policy is reliable against a cooperative agent but not a hostile one.
- Tests are meaningful: the sockaddr parser is pure + table-driven,
TestHandleTransparentConn_Tunnelsproves no200 Connection Establishedleaks (the agent's first bytes are the upstream's), sniff tests cover TLS/HTTP/opaque, and theunshare --netharness proves capture + preemption + non-TCP drop via packet counters. go.modchange is clean:golang.org/x/syspromoted indirect→direct at the samev0.42.0, nogo.sumchurn.- Bind failures are fatal (good — a dead transparent port would otherwise break all egress silently); nil
ConnHandlerdegrades to no-capture rather than panicking. - I agree with declining the SSH-port exemption: the listener blind-tunnels any TCP, so SSH still works end-to-end, and exempting :22 would punch exactly the bypass hole enforce-redirect exists to close.
Areas reviewed: Go (listeners, entrypoints, config), Shell (init-iptables.sh + netns test), Docs (README), go.mod.
Commits: 6, all signed-off, conventional prefixes.
CI: all green.
One optional defense-in-depth note inline on the self-loop guard. No blocking issues.
| // means a direct dial to the listener port, where SO_ORIGINAL_DST reports the | ||
| // listener's own address — tunnelling to it would spiral into ever more | ||
| // connections/goroutines. Drop it. | ||
| if host, _, splitErr := net.SplitHostPort(dst); splitErr == nil { |
There was a problem hiding this comment.
suggestion (non-blocking): the loopback-only self-loop guard is correct for the realistic cases — a direct dial to the listener with no DNAT record already fails originalDst() and is dropped above, and traffic to the pod's own IP is RETURNed by the CLUSTER_CIDRS rule before the REDIRECT, so it never reaches here.
The one caveat: that pod-IP safety is load-bearing on CLUSTER_CIDRS being set correctly, which the README itself flags as a required operator override off Kind. As cheap insurance against a misconfigured CLUSTER_CIDRS, you could also drop when dst equals the listener's own conn.LocalAddr() (host+port), matching CodeRabbit's suggested diff in the sibling thread. Entirely optional — your call whether the extra branch earns its keep given the iptables-level protection is the primary control.
There was a problem hiding this comment.
@huang195 to be clear, this is a nice-to-have, not something I'd hold the merge on — the iptables layer is doing the real work here. 防患于未然 (fáng huàn yú wèi rán, "guard against trouble before it arises"): the extra LocalAddr() check only matters in the misconfigured-CLUSTER_CIDRS world, so it's purely belt-and-suspenders. Happy either way — feel free to resolve if you'd rather keep the guard lean. 👍
There was a problem hiding this comment.
Done in 178c66e — added the dst == conn.LocalAddr() check alongside the loopback drop, so a podIP:8082 self-dial is caught even if CLUSTER_CIDRS is misconfigured. Agreed it's belt-and-suspenders behind the iptables RETURN, but it's a cheap branch and removes the load-bearing dependency on CLUSTER_CIDRS for the self-loop case. Thanks for the catch.
There was a problem hiding this comment.
防患于未然 indeed — added it. The extra LocalAddr() check is one line and only fires in the misconfigured-CIDR world, so the guard stays lean while no longer relying solely on CLUSTER_CIDRS for self-loop protection. 👍
Extend the self-loop guard (review follow-up from mrsabath + CodeRabbit): in addition to a loopback original destination, drop when the recovered dst equals the connection's own LocalAddr (host:port). This covers a podIP:<port> self-dial that could slip past the iptables CLUSTER_CIDRS RETURN if CLUSTER_CIDRS is misconfigured — belt-and-suspenders behind the iptables layer, which remains the primary control. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
Problem
proxy-sidecar egress enforcement could previously only DROP bypass traffic (
MODE=enforce-drop, #484). The forward proxy only speaks explicitCONNECT— it reads the destination from the request line/Hostheader — so an iptables-REDIRECTed raw connection arrives with no recoverable destination. There was nothing on the proxy that could serve a redirected connection, which is exactly why enforcement had to drop rather than redirect.DROP is fail-closed but breaks any agent that doesn't honor
HTTP_PROXY, which is whyenforce-dropstays opt-in and off by default.envoy-sidecar mode handles redirected traffic only because Envoy supplies the
original_dstlistener filter +ORIGINAL_DSTcluster; the auth pipeline itself is already shared between modes.What this PR does
Adds the missing half so enforcement can capture instead of drop — and therefore eventually be always-on with no breakage.
Transparent listener (
authlib/listener/transparentproxy)SO_ORIGINAL_DST(Linux, build-tagged; a!linuxstub keeps dev hosts buildable). The pure sockaddr parser is cross-platform and unit-tested.original_dstfilter; dispatches to aConnHandler.forwardproxy.Server.HandleTransparentConnrecordTunnelOpened/tunnelhelpers, sohandleConnectis simplified too).200 Connection Established, no hijack): the agent believes it is talking directly to the destination, so its end-to-end TLS is preserved — identical to today's CONNECT blind-tunnel and Envoy'stcp_proxypassthrough. (HTTPS remains opaque; this is L4 capture + destination policy, not content inspection.)authbridge-proxyandauthbridge-lite(the proxy-sidecar shapes). Newlistener.transparent_proxy_addrconfig, default:8082.init-iptables.shMODE=enforce-redirectnat OUTPUTchain (AB_REDIRECT) inserted at position 1 to preempt Istio ambient's nat redirect. Exempts (RETURN) ztunnel fwmark0x539, the proxy UID, loopback, andCLUSTER_CIDRS(in-cluster stays direct).TRANSPARENT_PORT; DROPs non-TCP external egress so HTTP/3 (QUIC) cannot bypass — clients fall back to TCP and get captured.enforce-dropis retained as a no-transparent-listener fallback.Testing
transparentproxy: unit test for theSO_ORIGINAL_DSTsockaddr parser (table-driven, cross-platform).forwardproxy:net.Pipetunnel test forHandleTransparentConn— asserts the agent's first bytes are the upstream's, proving no proxy-protocol leak.test-enforce-redirect.sh:unshare --netharness proving (1)AB_REDIRECTstructure + position-1 hook, (2) external TCP captured toTRANSPARENT_PORTwhile preempting a simulatedISTIO_OUTPUTredirect, (3) external UDP dropped — all via packet counters.Rollout / coordination
MODE=enforce-redirect. The companion kagenti-operator PR (wiring + removing theegressEnforcementflag, making enforcement always-on for proxy-sidecar) lands after this, and can only be released once aproxy-init+authbridge/authbridge-litebuild containing this PR is published.Assisted-By: Claude (Anthropic AI) noreply@anthropic.com
Summary by CodeRabbit
New Features
Behavior Changes
Documentation
Platform
Tests