Skip to content

Scope & caveats

  • 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 routes optionally lists routes cluster-wide.

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 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.

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.

Both inbound modes patch the target’s own sidecar and therefore require:

  • a target Service with a pod selector;
  • target pods with istio-proxy sidecars;
  • a non-ambient target namespace;
  • an Envoy/Istio layout compatible with the embedded patch;
  • ideally, pods/exec permission 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.

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.

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.

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 dials originate from the relay pod:

  • downstream authorization sees the relay service account, not the original caller;
  • connectivity to a STRICT mTLS service depends on relay mesh membership;
  • the relay can dial any host by default unless egress.allow is 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.

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.

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.

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.

SIGINT and SIGTERM run routing-first teardown. A hard kill, laptop crash, network loss, or expired credentials can prevent cleanup. Use:

Terminal window
outprobe status
outprobe down

with 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.