9.3 KiB
RustProxy 0.4.0 integrations - historical implementation record
Historical context. This document records the 0.4.0 integration milestone and is retained for traceability. Current behavior is documented in the functional/technical guides linked from README.md.
This is source implementation, not a verified binary. Read TEST_REPORT.md before deployment. The admin panel remains local; public frontend TLS does not make the admin listener remote-safe.
Forwarded identity and the trust boundary
Every origin request now gets canonical X-Forwarded-For, Forwarded, X-Real-IP,
X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port and X-Forwarded-URI.
Without configured trusted proxies, incoming proxy headers are discarded. The socket
peer IP is used, so a client cannot supply an arbitrary address in X-Forwarded-For.
Both header formats are emitted; RFC 7239 IPv6 nodes are quoted and bracketed.
There is no ephemeral client port in the representation fingerprint.
When RustProxy is behind a real reverse proxy, Settings -> Forwarding accepts explicit
IPv4/IPv6 CIDRs (max 32). Do not enter application client networks as trusted proxies.
The last hop must sanitize incoming forwarding headers. 0.0.0.0/0 and ::/0 are rejected.
With a trusted socket peer, X-Forwarded-For is preferred; Forwarded's for= chain is the
fallback. The chain is walked right-to-left and anything before the nearest untrusted
hop is discarded. Malformed or oversized chains fall back to the actual peer.
X-Forwarded-Proto is honored only with the separate trust_forwarded_proto opt-in.
Authority/Host always comes from validated Host or HTTP/2/3 :authority, not X-Forwarded-Host.
ACLs and IP affinity use this centrally resolved client address. Listener selection
continues to use the REAL local destination socket and actual TLS state.
"forwarding": {
"trusted_proxies": ["10.87.7.10/32"],
"trust_forwarded_proto": true
}
That address is illustrative, not a default. Leave the list empty for direct clients. RFC 7239: https://www.rfc-editor.org/rfc/rfc7239.html
Tinyauth per vhost
Open Vhosts -> Edit -> Authentication. Enable protection, enter the verification
server and the public login URL, allow a private verifier explicitly if needed,
and save. An origin URL automatically appends /api/auth/nginx; an explicit URL
path remains unchanged. No public /tinyauth proxy route needs to be created.
The following matches the supplied nginx-style setup:
"tinyauth": {
"enabled": true,
"url": "http://10.87.7.99:3000/api/auth/nginx",
"login_url": "https://auth.linuxiarz.pl/login",
"allow_private": true,
"address_allowlist": ["10.87.7.99/32"],
"connect_timeout_seconds": 5,
"timeout_seconds": 10,
"request_headers": ["cookie", "authorization", "x-api-key", "user-agent", "accept"],
"response_headers": ["remote-user", "remote-email"],
"redirect_forbidden": true
}
Verification is an internal body-free GET, carrying canonical forwarding headers, X-Original-URL and X-Original-Method. Original method, path and query remain available to Tinyauth, while the upload body is sent only to the application after authorization. The RustProxy admin session cookie is never passed to the verifier or application. Only 2xx approves access. Selected Remote-* / safe X-* response headers become trusted identity headers. Incoming Remote-* and configured identity headers are removed, including after administrator request-header rules. Missing identity fields are not filled from client values. Transport/framing/forwarding fields cannot be selected as identity headers. Each direction supports at most 32 names; approved identity values are bounded to 8 KiB in total.
401 redirects to a validated X-Tinyauth-Location on the configured login origin,
or constructs login_url?redirect_uri=<percent-encoded original URL>.
403 stays 403 unless the provider returns an allowed same-origin redirect and the
403-redirect option is enabled. Cross-origin locations are rejected. Verifier errors
fail closed (502/504), not open. Auth denial/error is not replaced by custom error pages.
Public login URLs require HTTPS; HTTP is allowed only for loopback test servers.
Additional CA PEM can extend trust for Tinyauth HTTPS verification; there is no
verify-off switch for the authentication verifier. The per-backend tls_verify option
does not apply to Tinyauth.
Configure Tinyauth itself to trust the ACTUAL RustProxy source IP, not 0.0.0.0/0.
Authorization runs before origin connection, static pages, WebSocket upgrade to the
origin and every cache lookup. Authenticated vhosts always BYPASS the internal shared
cache and emit Cache-Control: private, no-store, even when their cache toggle is on.
This intentionally also disables privacy-sensitive compression through the existing
predicate. Login/session validity is NOT cached. Revocation is checked on each new
HTTP request or WebSocket handshake; an already upgraded socket is not periodically
reauthenticated. There is no automatic replay of POST or failed upgrades.
Do not enable this protection on the Tinyauth login vhost itself (login loop).
Provider reference: https://tinyauth.app/docs/guides/nginx-proxy-manager/ Header reference: https://tinyauth.app/docs/reference/headers/
Named listeners
Settings -> Listeners owns every proxy socket. Each endpoint has a stable ID, display name, scope label, enabled state and a binding with numeric IP:port, TLS, HTTP/2 and optional HTTP/3 parameters. Scope labels do not install firewall rules.
Vhosts reference endpoints only through frontend.listener_ids. Inline frontend
listener definitions and the old global listener-pair compatibility fields are not
accepted in 0.8.0. A named endpoint can be shared by multiple vhosts; same-socket
transport settings must agree. Disabled endpoints create no socket and grant no vhost
binding. Unknown/duplicate IDs and admin-port conflicts are rejected.
Listener transport changes require restart. The panel shows desired and actually bound endpoints separately. A wildcard bind never widens a vhost's own destination-IP match.
Certificate library
Certificates is an independent sidebar section. Import a PEM certificate/full chain and PEM private key, give the entry an ID, specify domain names to validate the pair, and save it to the inventory. In Vhost -> SSL certificates, select the saved entry. Import does not add public SNI names by itself; active vhost assignment supplies names. The advanced filename editor remains for CLI-managed existing pairs and direct SNI maps. The library shows assignment and default status. ACME/Let's Encrypt and expiry renewal are not implemented; the inventory/API boundary is ready for that future work.
Uploads: at most 128 KiB per file, key matching/name validation, immutable randomly named private files, limited parallel imports and mode 600 on Unix. Config stores filenames and IDs, never PEM private key bytes. File inputs are cleared after import. Failure between staging and configuration publication may leave unused private files; they are not automatically activated or deleted. Removing inventory metadata does not delete key files. Back up the cert directory together with SQLite. Existing referenced/default entries cannot be removed in the UI without unassigning them. Certificate changes affect new handshakes; existing sessions continue normally. The resolver is shared by listeners: one DNS name cannot use different certificates on different destination IPs. Conflicts are rejected rather than silently chosen. HTTP-only startup also prepares the private certificate directory, but creates no TLS socket unless configured. No existing certificates are overwritten.
Response policies and cache on extra frontends
Global and per-vhost server_header accept preserve, hide, custom plus value.
Omitted/null vhost policy inherits the global choice. Final policy applies to origin
responses, HITs, local pages and errors for inline/named TCP/TLS/H3 listeners.
Generic header rules run before this dedicated Server policy.
As of 0.18.8, diagnostic_headers exposes only the request-ID switch:
"diagnostic_headers": {
"request_id": false
}
There is no revision response-header switch or special X-Rust-Proxy-Revision handling. If an upstream or ordinary response-header rule supplies that name, it behaves like any other non-protected response header.
Request IDs remain available internally/upstream and in logs. X-Mock-Origin is preserved and can
be removed with ordinary global or per-vhost response-header rules. An explicit vhost diagnostic
object overrides the request-ID choice; null inherits.
Cache keys now include the actual frontend IP:port and stable canonical forwarding metadata. Empty HTTP/2/HTTP/3 GET bodies without an exact length are checked by peeking at most eight frames, skipping empty DATA frames; nonempty data/trailers are replayed to the origin without buffering the full body. A stream with data/trailers bypasses cache. Reusing the same socket with a different client source PORT no longer changes the forwarded representation. Client IP still affects the conservative all-header fingerprint, so different clients can have separate cache entries. Auth, cookies, no-store, private, upgrades and partial bodies still bypass caching. Cookies created by a login or the sticky-session feature are not a cache bug. Close/consume the entire response before expecting a stored HIT.