# kaya-forwarded Trusted-proxy forwarded header handling for the Kaya web framework. Without this package, Kaya exposes the raw socket peer address as `ctx.client` / `ws.client` and ignores `Forwarded` / `X-Forwarded-*` headers entirely (they are client-controllable and trivially spoofable when the app is directly exposed). `ForwardedHeadersMixin` opts the application into honoring those headers, but only when the direct socket peer is a trusted proxy, identified by a list of trusted CIDRs/IPs. ## Usage ```python from kaya.core import KayaApp, HttpContext from kaya.forwarded import ForwardedHeadersMixin app = KayaApp(mixins=[ ForwardedHeadersMixin(trusted_proxies=['127.0.0.1', '::1', '10.0.0.0/8']) ]) @app.GET('/whoami') async def whoami(ctx: HttpContext): host, port = ctx.client await ctx.send_str(200, f'{host}:{port}') ``` ## How it works When a request arrives: 1. If the socket peer IP does not belong to any trusted CIDR (or there is no peer address), the mixin leaves the context untouched — `client` remains the socket peer and all proxy headers are ignored. 2. Otherwise the client address is resolved from the headers, in order: - `Forwarded` (RFC 7239): the `for=` entries are walked **from right to left**, skipping entries that are themselves trusted proxies (and `unknown`); the first untrusted entry is the client. This defeats spoofing when the edge proxy *appends* to the header (e.g. nginx with `$proxy_add_x_forwarded_for`), because attacker-supplied leftmost entries are never selected. A `:port` in the selected `for=` value also populates the port. - `X-Forwarded-For`: same right-to-left trusted-proxy walk; the port comes from `X-Forwarded-Port` when present and valid. - `X-Forwarded-Host`: first entry; port from `X-Forwarded-Port` as above. 3. If none of the headers are present or usable, the socket peer is kept. If every entry in the chain is a trusted proxy, the leftmost entry is used (the whole chain is trusted, so the leftmost is the original client). The resolved address is exposed by wrapping the request context / websocket (the same pattern as `kaya-session`), so both ASGI and RSGI keep working and `ctx.session` from other mixins is preserved. ## Note Even with this mixin, the edge proxy should still strip or overwrite inbound `Forwarded` / `X-Forwarded-*` headers from clients — the mixin protects the application, the proxy protects the chain.