Files
RustProxy/docs/INTEGRATIONS_040.md
T
2026-10-04 09:29:10 +02:00

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.