Skip to content

headers

Inbound header policy for tunnel traffic.

Everything the gate forwards arrives from the PUBLIC internet through the portr client, so every header on it is attacker-controlled. The headers an app trusts are precisely the ones a caller must not be able to write, because the app cannot tell a header the Databricks front door set from one a browser typed.

Policy shape: strip by default, allow by pattern

Section titled “Policy shape: strip by default, allow by pattern”

Enumerating what to remove is a losing game - a deny-list is only correct until the platform adds a header or a library starts trusting another one - so the policy is inverted. EVERY x--prefixed request header is dropped from tunnel traffic unless it matches a configured pattern. That fails CLOSED: a header nobody thought about is removed rather than trusted.

The allow-list is zero-to-many literals, globs, or /regex/es, compiled by shared-core’s pattern.toPatternMatcher, and is UNIONED with DEFAULT_FORWARD_HEADERS so extending it never silently breaks the built-in surfaces. Configure it with forwardHeaders / TUNNEL_FORWARD_HEADERS.

Non-x- headers are untouched. Standard ones (content-type, accept, authorization, cookie, …) are the app’s normal input and the gate has no business rewriting them.

PROTECTED_HEADERS is stripped BEFORE the allow-list is consulted, so a permissive pattern (x-*, or a careless *) cannot re-open impersonation:

Header What an app does with it Why spoofing it matters
x-forwarded-access-token OBO auth (AppKit asUser) Paste any workspace token and every call runs as its owner. The gate’s verified email says nothing about who a pasted credential belongs to.
x-forwarded-user Caller identity Impersonate another user. The gate sets this itself, from a verified session.
x-forwarded-email Caller identity Same.
x-forwarded-preferred-username Display name from the IdP Same.
x-forwarded-host The originally-requested host Poison absolute URLs the app builds (the classic reset-link attack).
x-forwarded-proto / -port Original scheme / port Convince the app a plaintext request arrived over TLS.
x-forwarded-for Client IP Forge the audit trail, and fan out per-IP rate-limit keys (see below).
x-real-ip Client IP Same.
x-request-id Request correlation UUID Forge or collide trace ids, making logs unreliable.

The identity four are AppKit’s OBO contract; the rest are the X-Forwarded-* set the Databricks Apps reverse proxy documents passing to an app (https://docs.databricks.com/aws/en/dev-tools/databricks-apps/http-headers), plus the conventional x-forwarded-proto/-port/x-real-ip an app or one of its libraries may read even though the table omits them.

Stripping the x-forwarded-* transport trio is safe because http-proxy-3 is configured with xfwd: true and re-adds them AFTER this policy runs - from the real socket, not from the caller’s claim. The app therefore sees the honest (loopback) values instead of whatever the internet asserted. The gate reads the client IP for rate limiting from the raw inbound headers BEFORE stripping, and takes the RIGHTMOST x-forwarded-for entry, which is the only one a proxy appended rather than a client supplied.