v0.24.5
This commit is contained in:
commit
5e23e9af02
240 files changed
+99957
No files matched your search
@@ -0,0 +1,161 @@
|
||||
# RustProxy 0.4.0 - forwarding, Tinyauth, listeners and certificate library
|
||||
|
||||
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.
|
||||
|
||||
```json
|
||||
"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:
|
||||
|
||||
```json
|
||||
"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:
|
||||
|
||||
```json
|
||||
"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.
|
||||
Reference in new issue
Block a user