Scope & caveats
Security model
Section titled “Security model”- JWTs are decoded, not verified. A claim is a routing hint only. It must never grant access.
- A route match grants nothing. Existing gateway/mesh checks apply where traversed; local code must not trust a relay match as identity, and passthrough retains the target’s normal security path.
- Candidate traffic is visible to the relay. In unfiltered divert this can be all meshed traffic to the target; in inbound-mirror it is a copy of every inbound request.
- Local code receives original request data. Treat logs, breakpoints, crash dumps, and debugger state as potentially sensitive.
- No cluster-wide manager is installed. The normal footprint is in the target namespace, though
outprobe routesoptionally lists routes cluster-wide.
Divert reliability
Section titled “Divert reliability”In divert, the relay is inline for traffic selected by the VirtualService. By default that is all meshed traffic to the target. A feed: block can reduce this to a path/header subset.
Unmatched requests are proxied to the real target, and deleting the VirtualService restores direct routing immediately. However, a relay crash can interrupt the selected traffic until routing is removed. The Deployment has a readiness probe; use additional relay replicas for busy or important paths.
Mirror behavior
Section titled “Mirror behavior”mirror is observe-only. The real target serves every caller while Istio sends a copy to the relay. A relay or laptop failure cannot affect the original response.
Your local app still processes a matching copy, but its response is discarded. This mode is useful for breakpoints and observation, not for testing a changed response end-to-end.
feed: is ignored in mirror mode in v1, so all eligible caller-side traffic is copied.
Caller-side coverage
Section titled “Caller-side coverage”divert and mirror run at the caller’s Istio sidecar. A caller without a sidecar reaches the target without seeing that route.
Use an inbound mode only when those callers matter. If every caller is meshed, caller-side modes are simpler and less coupled to Istio internals.
Inbound EnvoyFilter modes
Section titled “Inbound EnvoyFilter modes”Both inbound modes patch the target’s own sidecar and therefore require:
- a target Service with a pod selector;
- target pods with
istio-proxysidecars; - a non-ambient target namespace;
- an Envoy/Istio layout compatible with the embedded patch;
- ideally,
pods/execpermission so outprobe can verify live proxy configuration.
Istio warns that EnvoyFilter exposes internal implementation details. outprobe preflights the attach point and verifies the patch when possible, but an Istio upgrade can still require a template update.
inbound-mirror
Section titled “inbound-mirror”The target sidecar mirrors 100% of inbound requests to the relay. This catches meshed and non-mesh callers but can create significant body-copying and relay load. Scale the relay or use caller-side mirror if possible.
inbound-divert
Section titled “inbound-divert”The sidecar inserts a route for the active JWT/header rule. Malformed tokens and non-matches fall through to the real app. JWT matching uses a small unverified Lua payload decoder; unusual JSON formatting or escaping can produce a sidecar miss even when the Go relay would decode the claim.
A matched request arriving at the target from the relay carries the relay pod’s service-account identity. AuthorizationPolicies that allow-list workload identities may need to account for it.
Ingress scope
Section titled “Ingress scope”North-south interception is opt-in through ingress.routes. The generated route always adds a header condition to scope traffic and win Gateway API same-path precedence.
The default exact x-outprobe: 1 gate is narrow. A presence gate such as Authorization is convenient but puts the relay inline for all matching authenticated traffic. External ALBs, CDNs, or gateways may strip custom headers before they reach the Gateway API route.
In mirror mode, ingress routes must clone an existing HTTPRoute so the original backend can remain in place.
Egress identity
Section titled “Egress identity”Egress dials originate from the relay pod:
- downstream authorization sees the relay service account, not the original caller;
- connectivity to a
STRICTmTLS service depends on relay mesh membership; - the relay can dial any host by default unless
egress.allowis set; - a non-loopback local listener can expose the bridge to your network.
Egress is for development connectivity, not faithful emulation of a caller’s in-mesh identity.
Protocol boundaries
Section titled “Protocol boundaries”The interception path handles ordinary HTTP request/response traffic. It does not support WebSocket or other HTTP upgrade tunneling.
The CLI buffers request and response bodies while forwarding to the local app, so very large or streaming bodies increase laptop and relay memory use and do not preserve streaming semantics.
Egress is different: after its handshake it carries raw TCP and can support HTTP, gRPC, databases, and long-lived streams. UDP is not supported.
Local app behavior
Section titled “Local app behavior”The local service must be reachable from the CLI at localTarget. If the CLI runs in a container, localhost points inside that container; use the runtime’s host-access address as described in Run the CLI in a container.
A local connection failure produces a 502 Bad Gateway response on a serving-mode match. Verify the origin URL, local port, and app health before driving shared traffic.
Kubeconfig and credential reach
Section titled “Kubeconfig and credential reach”The CLI uses client-go for port-forwarding and shells out to kubectl for object management. Both need access to the same context and credentials.
Cloud kubeconfigs can invoke external credential plugins. Those plugins must exist and be executable in the environment where outprobe runs, especially inside a Linux container.
Cleanup is best-effort on process failure
Section titled “Cleanup is best-effort on process failure”SIGINT and SIGTERM run routing-first teardown. A hard kill, laptop crash, network loss, or expired credentials can prevent cleanup. Use:
outprobe statusoutprobe downwith the original context and namespace to recover. Because cleanup selects outprobe.dev/tool=outprobe, inspect the namespace before manually deleting labels shared with another active session.