This commit is contained in:
gru committed 2026-10-03 00:27:09 +01:00
commit 5e23e9af02
240 files changed
+99957

No files matched your search

+346
View File
@@ -0,0 +1,346 @@
# Listener and response settings
## Configuration and update semantics
GET `/api/config` returns a revisioned envelope. PUT the entire document with
`expected_revision`. Defaults apply when fields are omitted. Unknown fields are
rejected; failed validation/persistence leaves the old snapshot active. Existing
backend/route/TLS/ACL data must be retained when changing settings. The panel does
this and preserves dirty settings forms across status/language refreshes.
```json
{
"listeners": {
"endpoints": [
{"id":"http", "name":"HTTP", "scope":"public", "enabled":true,
"binding":{"address":"0.0.0.0:80", "tls":false, "http2":false, "http3":null}},
{"id":"https", "name":"HTTPS", "scope":"public", "enabled":true,
"binding":{"address":"0.0.0.0:443", "tls":true, "http2":true,
"http2_settings":{"idle_timeout_seconds":300, "max_connection_lifetime_seconds":0, "drain_timeout_seconds":10}}}
]
},
"server_header": {"mode":"custom", "value":"RustProxy/v0.20.0"},
"compression": {"gzip":true, "br":true, "zstd":true, "deflate":false, "min_bytes":1024},
"headers": {
"request": [{"op":"set", "name":"x-proxy-name", "value":"RustProxy"}],
"response": [
{"op":"remove", "name":"x-powered-by"},
{"op":"set", "name":"x-served-by", "value":"RustProxy"}
]
}
}
```
This is a settings fragment, not a replacement for the existing routing/vhost document.
A complete opt-in example is in `examples/cache-dashboard.json`.
The dashboard exposes the complete raw JSON editor under **Settings -> Manual config**.
The historical `/configuration` browser route is retained only as a compatibility alias and
opens that Settings block; it is no longer a primary-navigation item. Prefer the structured
section editors for routine changes so unrelated sections are not republished.
## Administration HTTPS
`administration` is separate from public proxy listeners. It defaults to the startup
loopback address and plain HTTP. Persisted address/certificate changes are validated
immediately but take effect after process restart. A non-loopback persisted bind is
accepted only when `administration.tls` is true. Plain remote HTTP remains available only when process startup explicitly allows it with
`--allow-remote-admin`.
```json
{
"administration": {
"listen": "0.0.0.0:9090",
"tls": true,
"certificate_id": "admin-example",
"allowed_hosts": ["admin.example.com"],
"session_days": 30
}
}
```
`certificate_id: null` uses `data/certs/default.pem` and `default.key`, generated by
the application. A selected inventory certificate is loaded from the same private
certificate directory. `session_days` controls the absolute browser-session lifetime
and must be 7..30 (default 30). It applies without restart; changing listener/TLS/certificate
settings still requires restart. Session cookies are HttpOnly/SameSite=Strict and receive
the `Secure` attribute on HTTPS. When HTTPS is enabled, accidental plain HTTP received on the same administration
port receives a permanent redirect to the same authority/path over `https://`; the
plaintext path contains no administration handlers. An empty `allowed_hosts` accepts
valid DNS Host values on the configured HTTPS port as well as valid numeric destination
addresses. Once `allowed_hosts` contains entries it becomes a strict DNS allowlist for
browser-facing DNS authorities. An explicitly listed hostname may arrive from a trusted
reverse proxy on an external port such as 443 even when the private admin socket is 9090.
Direct IP/`localhost` access is still accepted only on the actual configured admin port.
For HTTPS termination in front of a plain local admin socket, the trusted proxy must pass
its external Host and scheme; `X-Forwarded-Host` is accepted only from loopback/configured
trusted proxy peers and `X-Forwarded-Proto` additionally follows the forwarded-proto trust
policy. Origin/CSRF comparison and the session cookie Secure flag use that external origin.
Use a firewall and normal network policy even with HTTPS.
## Structured access logging
The **Settings -> Logging** tab controls optional external sinks while the existing bounded
live ring remains available to the panel. Sink I/O is performed by a bounded background
queue; request processing only performs a non-blocking enqueue. Supported destinations:
- JSONL file append, either one combined file such as `/var/log/rustproxy/access.jsonl` or
one file per resolved vhost under a configurable directory (default `/var/log/rustproxy/vhosts`).
Unmatched traffic uses `_unmatched.jsonl`. The managed systemd service provisions
`/var/log/rustproxy` for the service account and keeps that directory writable under
`ProtectSystem=strict`. Other custom paths still need matching service permissions.
- Logstash TCP using `host:port` (IP or DNS). Each event is one JSON line.
- Loki HTTP(S) push. Optional bearer credentials are read from an environment variable
named by `bearer_token_env`; the secret itself is not stored in configuration.
`logging.format.fields` maps output field names to standard event fields; an empty map
emits the complete standard event. The panel provides Standard, Nginx-style, ECS-style
and Minimal presets plus a visual field selector/renamer with JSON preview. Advanced raw
mapping remains available for aliases not representable by the simple builder.
`static_fields` adds constant string fields. Loki labels are separately configurable.
`/api/status.logging.sinks` reports queue capacity, dropped events, successfully delivered
events and sink errors.
## Hot and cold cache tiers
`cache.max_bytes` and `cache.max_entries` bound the HOT RAM tier. `max_bytes` is charged from retained body backing allocation plus entry metadata rather than only payload length. `cache.transient_max_bytes` separately bounds in-progress fills and full bodies moving between HOT and COLD; `cache.cold_queue_max_bytes` additionally caps queued demotions in bytes, and `cache.hot_eviction_max_objects` bounds one LRU admission attempt. The source defaults are 256 MiB HOT, 16 MiB maximum object, 64 MiB transient and 32 MiB COLD queue. The Settings panel calculates conservative/recommended/performance presets from the lower of host RAM and a finite cgroup memory limit without changing the COLD disk budget. It also warns when HOT + transient is a large fraction of effective RAM, when the additional configured cache budget is large compared with current host/cgroup headroom, when the COLD queue exceeds transient RAM, or when the object ceiling cannot fit in transient RAM. Automatic presets are disabled below 256 MiB effective RAM. On a 2 GiB effective-memory VM the recommended profile selects approximately 400 MiB HOT, 96 MiB transient, 16 MiB maximum object, 48 MiB COLD queue, 6400 HOT entries and a 64-object LRU admission limit. The optional persistent
cold tier is controlled by `cold_enabled`, `cold_path`, `cold_max_bytes`,
`cold_max_entries`, `cold_gc_interval_seconds`, `cold_gc_max_delete`,
`cold_after_seconds`, `cold_on_lru`, `cold_on_idle` and `cold_on_shutdown`.
`cold_gc_interval_seconds` defaults to 60 and `cold_gc_max_delete` to 256. Pressure GC
starts at roughly 90% of the configured byte/entry budget and cleans toward roughly 80%
without deleting more than the configured per-pass cap. `cold_path` is relative to the RustProxy data directory and cannot
contain parent traversal or an absolute path. The two demotion switches independently
select LRU-pressure and idle objects; shutdown flush is a best-effort direct write during
a graceful restart. Demotions use a small bounded queue and release hot accounting before
disk persistence so cold storage does not retain an unnecessary second body buffer in RAM.
Cold metadata is readable JSON and bodies are raw `.body` files; reads are size/SHA-256
checked before promotion to RAM. Since 0.21.0 objects are sharded by the first two SHA-256
hex characters (`cache/cold/00/...` through `ff/...`), with shard directories created on
first write. The previous flat Cold Cache is intentionally not migrated. Purge operations
clear both in-memory indexes and schedule disk cleanup; selective cleanup uses the same
GC/eviction path and runs asynchronously.
Version 0.22 adds foreground fill collapsing (`collapse_enabled`,
`collapse_wait_timeout_ms`, `collapse_timeout_fallback`), detailed stale-if-error policy
(`stale_if_error_timeout`, `stale_if_error_connection`, `stale_if_error_dns`,
`stale_if_error_tls`, `stale_if_error_statuses`) and opt-in proactive refresh
(`proactive_refresh_enabled`, `proactive_refresh_before_expiry_seconds`,
`proactive_refresh_percent`, `proactive_refresh_max_inflight`). Defaults preserve the
previous stale-if-error behavior; proactive refresh is disabled by default. Selective
PURGE/BAN is an operational API/panel action rather than persisted configuration. See
[CACHE.md](CACHE.md) for request-collapse, Range and invalidation semantics.
## Static-response transformation
`transform` is a formal identity-response stage between the upstream response and shared
cache storage. `transform.enabled` is the global kill switch, but every vhost must also
set its own `transform.enabled=true`; vhosts stay disabled by default even when the global
engine is enabled. Global defaults enable JavaScript and CSS transformation and leave
HTML, SVG and JSON off. `extensions` controls extension-based detection and
`max_body_bytes` (1 KiB..32 MiB, 2 MiB default) bounds aggregation/decoding.
The pipeline is `origin -> decode supported Content-Encoding -> transform -> cache
identity representation -> client compression`. JavaScript/CSS can be enabled normally;
HTML minification, SVG optimization and JSON compaction are independent optional formats.
Recognition uses safe configured extensions and known Content-Type values. Binary/image
formats outside those explicit transform types are not transformed.
Origin `Cache-Control: no-transform` always disables transformation. When bytes change,
RustProxy removes representation-specific length/digest/encoding metadata and creates a
new strong `rustproxy-*` ETag for the transformed representation. The original origin
ETag/Last-Modified are kept separately and used for origin revalidation, so a proxy ETag
is never sent upstream as though it validated the original object. The transformed
identity body is what enters cache, preventing repeated minification on every HIT.
## Global TLS protocol and cipher policy
`tls.policy` is the global baseline inherited by every TLS host. It controls TLS 1.2,
TLS 1.3 and optional rustls cipher-suite names. An empty `cipher_suites` list uses the
ring provider defaults. Settings shows both the currently effective cipher suites and
the complete selectable suite inventory. At least one protocol version and
one compatible effective suite are required. This policy is also used by administration
HTTPS. HTTP/3 requires TLS 1.3 and a TLS 1.3 suite. Vhosts can narrow TCP TLS with
`tls_policy`; when a vhost has HTTP/3 enabled on a selected H3-capable listener it must
match the global policy because QUIC TLS is shared per UDP listener. Available suite names are exposed in authenticated status for
the Settings editor.
## Listeners: saved intent, not hot rebinding
When an HTTP, HTTPS or administration address was explicitly supplied by systemd/CLI
(or the corresponding service environment variable), that exact startup bind is a
process contract: the panel shows it as locked and configuration updates cannot remove,
disable or change it. If an address was not explicitly supplied at startup, listener
entries remain freely addable/removable/editable subject to normal validation.
`listeners.endpoints` is the only persisted frontend socket configuration. Each
endpoint has a stable id/name, an informational scope label, an enabled flag and one
primary binding containing IP:port, TLS, HTTP/2 plus its optional lifecycle settings, and optional HTTP/3 settings. Optional
`additional_addresses` reuse that same transport policy on up to 15 further addresses.
When more than one address is configured every address must be explicit; `0.0.0.0` and
`::` are rejected so a multi-IP selection cannot silently mean every interface. IP/port
and transport capability belong here, not inside a vhost. IPv6 uses `[address]:port`.
A vhost references endpoints through `frontend.listener_ids`. Listener changes are
restart-required because they alter TCP/UDP sockets or TLS ALPN capability. The
listener editor owns the HTTP/2/H2C and HTTP/3 enable/disable switches. An H2/H2C row
links to **HTTP connections -> HTTP/2** for idle, absolute-lifetime and graceful-drain
limits; an enabled H3 row links to **HTTP connections -> HTTP/3** for advertisement,
connection, stream and idle-timeout limits. Both tuning pages are listener-scoped and
do not own protocol enablement. Vhost H2/H3 switches only narrow the protocols already
enabled by the selected endpoints; they never create a protocol that Settings disabled.
The administration listener is independent and cannot share a frontend port.
Bindings must be valid local unicast/wildcard addresses; conflicting overlapping
endpoints are rejected rather than silently widened.
## Server and other client headers
`server_header.mode` is `preserve` (default), `hide`, or `custom`. Custom values are
nonempty, bounded to 256 bytes and validated against header injection. Preserve
means retain the result of origin/global/route rules; it does not add a missing
header. Hide/custom take final precedence, including proxy-generated errors.
Global and route `headers.response` apply to client responses; `headers.request`
apply only upstream. Each scope/direction supports up to 32 ordered set/append/remove
rules. Remove has no value. Global rules precede route rules. Multiple Set-Cookie
values remain distinct. Admin-session cookie names are never forwarded or set by
public proxy rules. Transport, framing and trusted forwarding/identity fields are
protected. Cache diagnostic fields are owned by the cache controls, not generic rules.
Global response rules also cover generated errors; route-specific rules apply only
after an origin route response exists. Client policy changes use hot configuration
publication and invalidate the RAM cache. In-flight exchanges retain their old
snapshot; no mixed-version configuration is applied midway through a response.
## Compression
All four flags default false. `min_bytes` defaults to 1024 (valid 32..1048576).
The library's default small-body/content-type predicate is also retained; actual
minimum for a known-size body is at least that default. Unknown-length text bodies
can be streamed without pre-buffering merely to discover their size.
Accept-Encoding quality negotiation is delegated to tower-http. Text, JSON,
JavaScript, XML, SVG and wasm are eligible; already encoded, SSE, range, upgrade,
HEAD/204/304 and privacy-sensitive responses are excluded. Cookies/authorization
bypass even when an origin marks a response public. Origin no-transform/privacy
exclusions cannot be defeated by deleting a response header in the editor. Cached
identity bodies can be streamed through compression on HIT; origin encodings are
passed through without recompression.
Vary: Accept-Encoding is added by compression; content length is removed for the
transformed body. Strong ETags are weakened, and identity-body content digests and
Accept-Ranges are removed. Encoders use their fastest preset; there is no panel
compression-quality slider in this release. Real binary interoperability tests
are still an acceptance requirement, not a result of this packaging session.
## Diagnostics and units
Samples run off the traffic path roughly every two seconds. CPU and network rates
need two successful samples. Host CPU is aggregate; process CPU uses 100% per busy
logical CPU and can exceed 100%. Neither is normalized to a container CPU quota.
Host memory is MemTotal minus MemAvailable. RSS/FDs/threads and process I/O come
from procfs. Network rates sum non-loopback interfaces in the process network
namespace, not just RustProxy traffic. Load average is OS scope, not latency.
The process's cgroup v2 memory files are read at the standard mount location when
available. Linux keeps the existing `/proc`/cgroup metric names, units and behavior.
FreeBSD/OpenBSD/NetBSD use native system interfaces where the same metric is available;
unknown or unsupported values remain null rather than being fabricated. Snapshot timestamps and sample age are shown;
cache counters and proxy request metrics are separate from OS resource metrics.
The dashboard history is browser-local and resets on reload/sign-out. No time-series
database, disk utilization chart or Windows/macOS OS sampler is implemented.
## HTTP/2 compatibility and per-vhost settings
HTTP/2 lifecycle settings are listener-scoped under `listeners.endpoints[].binding.http2_settings`:
- `idle_timeout_seconds` (default 300) retires only a truly unused H2 connection; active streams and current frame I/O postpone it.
- `max_connection_lifetime_seconds` (default 0) is an independent absolute lifetime; `0` disables this limit.
- `drain_timeout_seconds` (default 10) bounds how long accepted streams may finish after retirement begins.
Idle and lifetime retirement both send GOAWAY, stop accepting new streams, drain already accepted streams and then close TCP. RFC 8441 WebSocket/H2 Extended CONNECT streams are counted as active until their response body/tunnel ends. The same settings apply to TLS H2 and cleartext H2C on that named listener.
For backward compatibility, the top-level `http2` object is still accepted as the fallback for an H2/H2C listener that has no explicit `binding.http2_settings`. The structured UI writes explicit per-listener settings; operators should treat the top-level object and `PUT /api/config/http2` as legacy compatibility surfaces rather than the preferred configuration path.
A named TLS listener with `binding.http2: true` advertises H2 capability and always
retains HTTP/1.1 fallback. `binding.http2: false` makes that listener HTTP/1.1-only.
Cleartext H2 uses `binding.h2c: true`. Changing listener transport capability or H2 lifecycle settings requires restart.
A vhost can additionally set `http2_enabled: false`. RustProxy reads TLS ClientHello
and SNI before committing to ALPN, so a vhost that disables H2 gets HTTP/1.1-only
ALPN even while another hostname on the same listener keeps H2. `http3_enabled` is
the equivalent per-vhost H3 opt-out. The panel enables these switches only when a
selected listener provides the protocol. No failed application request is replayed
to downgrade.
Vhosts have their own backend pool, health/keepalive/affinity controls, ACL and
security/custom response headers. See VHOSTS.md. Global Server policy still takes
final precedence. There is no per-vhost compression algorithm override.
The reported `SizeAbove(u16)` compilation error is removed by a custom u64 predicate,
not a cast or panic-producing conversion. Compression thresholds retain the full
32..1048576-byte setting range. Known body length/Content-Length is compared exactly;
unknown streaming lengths are eligible without buffering the full body. Actual
compression execution still requires the compiler-enabled acceptance suite.
## Management in 0.3.0
Settings contains a direct Security shortcut (accounts, sessions and host ACLs)
and a vhost shortcut (SSL, headers, cache and pages). Compact is the default density;
Dense and Comfortable change spacing/control sizes without browser zoom or logo
redesign. The preference is local to the browser, not a global proxy setting.
Global Pages configures error overrides, direct-IP and unknown-host responses.
See MANAGEMENT.md for inheritance, allowed statuses and security implications.
Access logs have moved out of the dashboard into a separate modal/docked window.
Filters stay above the scrollable rows; counts and exports use the same selection.
## Named listeners, forwarding and diagnostics (0.4.0)
As of 0.8.0, the vhost editor no longer asks for a free-form listener IP. Configure
listener IP/port/protocol once in this Settings section, then select named endpoints in
the vhost. The pre-production inline/global listener format is no longer accepted. This separates
socket intent from host routing and avoids duplicating listening IP configuration across
vhosts.
`listeners.endpoints` is the only proxy-listener configuration. Each record has id,
name, scope, enabled and a binding with address, tls, http2, optional http2_settings, h2c and optional http3. Scope
is only a label. A vhost uses `frontend.listener_ids`; every reference must exist.
Conflicting protocols on shared sockets are rejected.
Changes to effective transport plans need restart, including H2 lifecycle and H3 limits. The UI
shows saved named entries and actual bound transports separately.
`forwarding` controls trusted CIDRs and forwarded-proto acceptance.
`diagnostic_headers` exposes only `request_id` (false initially). Vhosts can override the diagnostic
policy and Server policy; absent/null means inherit. Runtime/cache/static/error
responses pass through the same final header policy.
See INTEGRATIONS_040.md and HTTP3.md for complete JSON, limits and security details.
## 0.5.0 changes (override earlier baseline examples)
A named TLS listener with omitted http3 uses default H3 limits. Explicit null is
persisted as disabled. Overlapping named sockets must match protocols AND H3 limits;
the error identifies owners and remedies. A changed binding requires restart, including
UDP protocol/limit changes. Check UDP port availability before upgrading.
The diagnostic control is `request_id` (false by default). The removed hide_mock_origin field is not accepted; use normal response-header removal
for X-Mock-Origin. Existing custom Server values remain unchanged.
Cache decision/object inspection is under Cache, separate from Overview. Basic
manual certificate issuance is under Certificates; TSIG is never a configuration
field. See CACHE_DIAGNOSTICS.md and ACME_RFC2136.md for exact scopes and limits.
## 0.6 navigation and exporter
The Settings page now has eight switching cards: Listeners, Response, Cache,
Headers, Forwarding, Pages, Prometheus and Security. Unsaved values remain in
place while switching; switching alone never writes configuration. Security is
not a separate sidebar item. Use arrow keys/Home/End in the tab bar.
The Prometheus card controls enabled, bind IP/port, URL path, client CIDRs,
bearer-token environment variable name and optional collectors. Only the variable
name is exported; supply the actual secret to the service separately. See
PROMETHEUS.md for immediate revocation, restart rules and scrape configuration.