Skip to content

Verify the two unexercised paths in transparent inbound: ambient (HBONE) capture and inbound mTLS #780

Description

@huang195

Summary

Transparent inbound interception (#330) is merged and its main path is verified end-to-end. Two code paths shipped unexercised — they are implemented and structurally verified, but no test or live run has driven traffic through them. Tracking them here rather than leaving them implicit in a closed issue.

Neither is a known defect. Both are "we have not observed this work," which is a different claim from "this works."

1. Ambient (HBONE) inbound — the higher-risk one

Ambient inbound never traverses PREROUTING: ztunnel terminates HBONE and re-originates a LOCAL connection, so it appears in nat OUTPUT. It is captured by a mark-based DNAT at the head of AB_REDIRECT, ahead of that chain's ztunnel-mark RETURN.

Verified: the rule is installed with the full mark 0x539 / ! --uid-owner / --dst-type LOCAL match, ordered before the DNAT and before the RETURN that would otherwise leak; all nine inbound exemptions are present on that hook too. test-enforce-redirect.sh asserts the ordering explicitly, and the rules were inspected live in a pod netns.

Not verified: that ztunnel's re-originated connection actually matches that tuple at runtime. That is reasoned from redirect mode's precedent (init-iptables.sh, "Rule 2"), not observed. The Kind cluster used for e2e runs only istiod — no ztunnel DaemonSet, no istio-cni — so there is no ambient data plane despite namespaces carrying istio.io/dataplane-mode=ambient. Counters confirm which path was exercised: AB_INBOUND REDIRECT took 4 packets, the ambient DNAT 0.

Why it matters: a PREROUTING-only implementation looks correct while waving every mesh-delivered request through unvalidated. That is precisely the failure this rule exists to prevent, so it deserves an observation rather than an argument.

  • Run the transparent-inbound e2e in an ambient-enrolled namespace on a cluster with ztunnel deployed
  • Confirm from rule counters that the ambient DNAT — not AB_INBOUND — carried the traffic
  • Confirm the exemptions hold on that path (:9091 reachable, so probes do not gate)

2. Inbound mTLS through the transparent listener

The transparent listener reuses the reverse proxy's mTLS posture via WrapListener, so tlssniff handles permissive/strict identically for both inbound shapes. Startup logs confirm the wrap is active (Transparent inbound server listening ... mtls=true).

Not verified: no TLS client has been driven against :8083. Unit coverage exists for the wrap (TestWrapListener_NoMTLSIsPassthrough) and for tlssniff itself, but the combination of SO_ORIGINAL_DST recovery and a TLS handshake on the same connection is untested end-to-end — and that combination is the novel part, since the recovery happens on the raw conn before tlssniff peeks.

  • e2e with a TLS client against a transparent-inbound agent — done as strict throughout
    (permissive is already covered by the main module's plaintext 401, which is what makes
    the strict rejection below meaningful as an A/B)
  • Same with strict, asserting plaintext is rejected — http_code=000, vs 401 permissive
  • Beyond the original ask: a valid SVID completes the handshake and still gets 401
    from jwt-validation (test(e2e): cover inbound mTLS through the transparent listener rossoctl#2404)

Not in scope

Intra-pod loopback is deliberately not captured — containers share a netns and are one entity to every network enforcement layer, so that traffic is inside the trust boundary. See #330.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    New/ToDo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions