This commit is contained in:
Mateusz Gruszczyński committed 2026-10-05 17:05:06 +02:00
1 parent d168ee2afa
commit ddc5c6593f
70 files changed
+2293 -594

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
# Administration API access - 0.25.7
# Administration API access - 0.26.0
## Sign-in boundary
+8
View File
@@ -7,6 +7,14 @@
# Changelog
## 0.26.0
### Compiler follow-up
- Corrected the external `cargo` build failure in `server::tls_runtime`: the HTTP/1 service closure now captures its own cloned `frontend_state`, leaving the original state available for connection-level protocol-error logging after `serve_connection` returns.
- Corrected `serde_json::Map::entry` type inference in the stored-log normalizer by passing the literal `"event"` directly instead of the ambiguous `"event".into()`.
- Rust-independent language, security, source-module, documentation, UI, source and release-smoke gates pass after both corrections. A fresh compiler-enabled `cargo check/test` remains required because this packaging workspace has no Rust toolchain.
## 0.25.1
### 0.25.1 source-split compiler follow-up
+4
View File
@@ -37,3 +37,7 @@ Error messages from dependencies should still be treated as untrusted diagnostic
## Rotation
RustProxy does not implement file rollover, retention or compression. Use the system `logrotate`. The Debian package configuration in `debian/rustproxy.logrotate` already covers `/var/log/rustproxy/*.log` and uses `copytruncate`, so the default error-log path is included without a RustProxy restart.
## Stored-log explorer
The authenticated `/logs` workspace can read the configured error file alongside structured access/protocol files. It remains independent from Live Logs and can inspect an existing error file even while `error_log.enabled` is false. The browser API does not accept arbitrary paths; it resolves only the configured error-log path and applies the same bounded, read-only regular-file/symlink protections documented in [FILE_LOGS](FILE_LOGS.md).
+61
View File
@@ -0,0 +1,61 @@
# Stored log explorer
RustProxy 0.26.0 adds an authenticated, read-only **Logs** workspace at `/logs`. It is a local file-backed explorer for RustProxy logging outputs and is intentionally separate from **Live Logs**.
## Separation from Live Logs
Live Logs uses the bounded in-memory request history and Tokio broadcast channel. It remains available even when file logging is disabled and is optimized for current request activity.
The stored-log explorer does not read that memory ring. It reads only files derived from the active RustProxy logging configuration:
- the combined structured access-log path, or JSONL files directly inside the configured per-vhost directory;
- the configured optional error-log file.
Existing files remain browsable after their writer is disabled. Disabling file logging therefore stops new persistent access/protocol records but does not remove old files.
## Protocol and malformed-traffic records
Ordinary completed requests use `event: "request"`. RustProxy can also persist low-level records that fail before a normal routed HTTP exchange exists. Those records use:
```json
{
"event": "protocol_error",
"protocol_error": "tls_handshake_error",
"error_detail": "...",
"client_ip": "192.0.2.10",
"local_addr": "192.0.2.20:443",
"tls_sni": "app.example",
"transport": "tls",
"http_version": "",
"status": 0,
"outcome": "protocol_error"
}
```
Covered classes include connection-limit rejection, malformed/incomplete HTTP/1 connections, invalid/disabled h2c upgrades, HTTP/2 protocol termination, TLS ClientHello and handshake errors/timeouts, and HTTP/3/QUIC handshake, header, protocol and stream-limit failures.
These low-level records are sent only through the configured persistent structured-log sinks (file, Logstash and/or Loki). They are deliberately **not** inserted into Live Logs, so scanner noise cannot displace the ordinary bounded live request history. For `protocol_error` events, the diagnostic envelope fields shown above are always emitted even when a custom JSON field mapping is active; custom/static fields are retained, but they cannot hide or overwrite the required protocol-diagnostic fields.
Dependency error text is secret-redacted and capped before it enters the persistent record. Raw packets, request bodies and raw malformed request bytes are not captured.
## Browser security model
The browser never supplies a filesystem path. `/api/file-logs` returns opaque source IDs derived from the current configuration, and `/api/file-logs/query` accepts only those IDs or `all`.
The reader is bounded:
- up to 2 MiB scanned from the tail of one source;
- up to 8 MiB across one query;
- up to 20,000 lines across the complete query, shared fairly across selected sources;
- at most 32 sources in an `all` query;
- page size at most 500 and offset at most 10,000.
File reads run in a blocking worker rather than the async proxy runtime. Only regular files are accepted. On Unix the reader opens with `O_NOFOLLOW`; a symlink is not followed. Per-vhost enumeration also rejects a configured directory that is itself a symlink and only lists direct regular `.jsonl` children.
The endpoints are private administration routes and inherit the existing panel session/Host/Origin access controls. There is no public-proxy route to the file reader.
## Filters
The workspace supports source, case-insensitive text, event class, HTTP status class, client IP and vhost filters. Results are paged from the bounded scanned tail. Selecting a row shows the complete parsed JSON object. RAW error-log lines are wrapped as a record with `event: "error"` and a `raw` field.
The explorer is diagnostic rather than a retention system. RustProxy does not add rollover, indexing or compression; use the existing operating-system log rotation policy for retention.
+1 -1
View File
@@ -1,4 +1,4 @@
# Installing and updating RustProxy 0.25.7
# Installing and updating RustProxy 0.26.0
The scripts prepare a Linux/systemd installation. This source archive is **not a
verified production build**: Rust/Cargo, real CA operations, live systemd startup
+1 -1
View File
@@ -15,7 +15,7 @@ The CLI first probes the loopback administration `/healthz` endpoint over HTTP.
This fixes the previous reconnect loop where `--livelogs` treated the TLS listener's expected HTTP-to-HTTPS `308` response as a stream failure. The same local transport selection is used by CLI config and certificate API operations.
The live path is separate from configured file/syslog sinks. Request processing publishes into a bounded Tokio broadcast channel without waiting for viewers. A slow viewer receives an explicit `gap` event with the number of entries lost from that viewer's live queue. The CLI also consults the retained history after connecting/reconnecting and de-duplicates entries by request id. Each request now carries a fresh 64-hex-character SHA-256 `request_id`; the same protected value is forwarded to the backend as `X-Request-ID` for correlation.
The live path is separate from configured file/syslog sinks and from the `/logs` stored-log explorer. Request processing publishes ordinary routed request records into a bounded Tokio broadcast channel without waiting for viewers. Low-level malformed HTTP/TLS/HTTP2/HTTP3 protocol failures introduced in 0.26.0 are intentionally persistent-sink-only and do not enter this Live Logs ring, so scanner noise cannot evict normal live request history. A slow viewer receives an explicit `gap` event with the number of entries lost from that viewer's live queue. The CLI also consults the retained history after connecting/reconnecting and de-duplicates entries by request id. Each request now carries a fresh 64-hex-character SHA-256 `request_id`; the same protected value is forwarded to the backend as `X-Request-ID` for correlation.
## User-Agent, country and Traffic analytics
+6
View File
@@ -1,5 +1,11 @@
# Deployment and configuration migration history
## 0.25.7 -> 0.26.0
There is no SQLite schema or saved-configuration migration. Version 0.26.0 extends the existing structured access-log record with optional protocol-diagnostic fields and adds a read-only stored-log explorer. Existing logging settings remain valid and file logging remains disabled by default. Low-level malformed/TLS/protocol events are written only when at least one external structured-log sink is enabled; they do not enter the Live Logs memory ring. Existing JSONL access files and the configured error file can be browsed after upgrade without conversion.
The `/logs` explorer is authenticated administration UI. Its API derives source IDs from the active RustProxy logging configuration and does not accept filesystem paths. On Unix it refuses symlink targets when opening files for browsing.
## 0.25.6 -> 0.25.7
There is no SQLite schema or saved-configuration migration. Version 0.25.7 reuses the existing `admin_preferences` document to persist backend-table visible columns and sort order per administrator account; search text remains transient. It also adds vhost enable/disable UI actions, themed scrollbars and additional Prometheus/Grafana observability. Existing vhost `enabled` values and backend configuration remain unchanged until an administrator explicitly uses the new action.
+1 -1
View File
@@ -56,7 +56,7 @@ Only `GET` on the exact configured path without a query string is accepted. A
wrong path is `404`, wrong method `405`, disallowed peer `403`, invalid/missing
required bearer `401`, and exporter concurrency exhaustion `429`.
## Coverage audit in 0.25.7
## Coverage audit in 0.26.0
The exporter represents all scalar fields currently sampled by `diagnostics::View` when available. The per-interface `network_interfaces` list added in 0.18.6 is intentionally dashboard-only so kernel-created interface names cannot create an uncontrolled Prometheus label set. RustProxy also exports the bounded operational data already available from traffic metrics, cache, vhosts, backend pools, streams, logging, GeoIP and ACME renewal observations.
+1 -1
View File
@@ -31,7 +31,7 @@ This is the canonical index for the current project documentation. The authentic
- [NOT_FOUND_HOSTS](NOT_FOUND_HOSTS.md) - local 404 host handling.
- [DOCKER_DISCOVERY](DOCKER_DISCOVERY.md) - Docker discovery and generated vhosts.
- [ACME_PROVIDERS](ACME_PROVIDERS.md) and [RENEWAL](RENEWAL.md) - certificate automation.
- [LIVE_LOGS](LIVE_LOGS.md) and [ERROR_LOGGING](ERROR_LOGGING.md) - request/error observability, User-Agent/optional-country capture and bounded per-vhost Traffic analytics.
- [LIVE_LOGS](LIVE_LOGS.md), [FILE_LOGS](FILE_LOGS.md) and [ERROR_LOGGING](ERROR_LOGGING.md) - independent in-memory live request viewing, bounded on-disk log exploration, protocol-error persistence and optional error-file logging.
## Administration and operations
+1 -1
View File
@@ -95,7 +95,7 @@ The **Settings -> Logging** page is split into **Outputs**, **JSON format** and
`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. Supported mapped sources include `vhost`, `route`, `cache_effective_enabled`, `transform_effective_enabled`, `backend`, request/response metadata and cache outcome fields. `static_fields` adds constant string fields. Loki labels are separately configurable.
mapping remains available for aliases not representable by the simple builder. Supported mapped sources include `vhost`, `route`, `cache_effective_enabled`, `transform_effective_enabled`, `backend`, request/response metadata and cache outcome fields. `static_fields` adds constant string fields. For low-level `protocol_error` events, RustProxy always preserves the required diagnostic envelope (`event`, error kind/detail, time, client/local/TLS/protocol fields, status and outcome) even with a custom mapping, so malformed-traffic records remain searchable in the built-in Logs explorer. Loki labels are separately configurable.
`/api/status.logging.sinks` reports queue capacity, dropped events, successfully delivered
events and sink errors.
+10 -6
View File
@@ -1,18 +1,22 @@
# Verification report - RustProxy 0.25.7
# Verification report - RustProxy 0.26.0
## Scope
RustProxy 0.25.7 is a patch release focused on vhost operations and backend observability. Vhost cards now expose a direct revision-protected enable/disable action, the main Backends diagnostics table supports account-scoped persisted sort/column visibility preferences plus transient search, and the vhost editor gives the Enabled setting a distinct state treatment. Dashboard scrollbars follow the active theme. Prometheus now exports backend retry attempt/success counters and upstream-client generation, generation-request, generation-age and rotation metrics for both ordinary and Docker-discovered backends; the bundled Grafana dashboard adds matching lifecycle/retry panels. SQLite remains schema 8 and no saved-configuration migration is introduced.
RustProxy 0.26.0 is a minor release focused on persistent malformed-traffic diagnostics and a built-in stored-log explorer. Public listeners now enqueue bounded `protocol_error` records for malformed/incomplete HTTP/1 traffic, TLS ClientHello/handshake failures and timeouts, invalid/disabled h2c attempts, HTTP/2 connection/drain protocol failures, HTTP/3/QUIC handshake/request/header/stream failures and connection-limit rejects. These records use the existing asynchronous structured-log sinks and deliberately bypass the Live Logs memory ring so hostile scanner traffic cannot evict ordinary live request history.
Backend-table visible columns and sort order are presentation-only fields in the existing account-scoped `admin_preferences` document; search text is not persisted and none of these view settings alter RustProxy configuration. The quick vhost action changes only the selected vhost `enabled` flag through the same `saveSection("vhosts", ...)` revision/CSRF path as other vhost edits. Existing backend client recycling defaults remain unchanged.
The authenticated `/logs` workspace is independent from Live Logs and reads only configured RustProxy access/per-vhost/error files. It uses opaque source IDs rather than browser-supplied paths, regular-file checks plus `O_NOFOLLOW` on Unix, bounded blocking reads, a global 20,000-line / 8 MiB query budget, at most 32 sources, bounded pagination and server-side filters. Existing configured files remain browsable while their writer is disabled. Required protocol-diagnostic envelope fields are retained even when an administrator uses a custom structured JSON mapping. SQLite remains schema 8 and the saved configuration format remains compatible with 0.25.7.
## Implemented automated coverage
0.26.0-specific Rust tests cover protocol-error separation from the Live Logs ring, required diagnostic fields under custom JSON mappings, secret redaction and UTF-8-safe detail bounding. Admin tests cover visibility of existing log files while writers are disabled, bounded tail reads, oversized source-ID rejection and Unix symlink refusal. The HTTP/1, TLS/H2/h2c and HTTP/3 adapters contain explicit persistent diagnostic emission points for the failure classes listed above. These Rust tests are present in the source tree but cannot be compiled/executed in this workspace because Cargo is unavailable.
Rust unit tests in `src/web_server/tests.rs` cover GET/HEAD/index/slash redirect, MIME/length, validators, valid/invalid range handling, If-Range, escaped/repeated directory listings, per-directory index-before-listing fallback, generic 403/HEAD behavior, MIME overrides/default MIME, cache rules, source headers, `try_files`, rewrite redirect/internal behavior, `.htaccess` WordPress-style front-controller routing, nested DirectoryIndex/MIME/Header/Expires behavior, strict-mode rejection and PHP-source protection, hardening probe/method blocking and fingerprint-header stripping, precompressed representations/ranges, download headers, PHP FastCGI fixture execution/failure-source-disclosure behavior, request-body 413 enforcement, custom FastCGI/PHP_VALUE validation and reserved-parameter rejection, direct traversal rejection and Unix/Linux symlink policy. Upstream tests cover request-budget client generation recycling and disabled recycling; balancing tests cover the new keepalive defaults and validation bounds. `src/traffic/tests.rs` includes bounded-table, effective-client-IP view and fixed Prometheus User-Agent/method family coverage. Configuration tests cover pure static vhosts without a pool, mixed Web Server/Reverse Proxy routing, reuse of one Web Service by multiple vhosts, unknown service references and backward-compatible default source behavior. Vhost tests cover method-policy normalization/allow/block behavior, database tests cover retained configuration revision documents, actor metadata and previous-revision lookup, and admin tests cover account-scoped/CSRF-protected view preferences including legacy preference defaults, backend-table columns/sort persistence and invalid-column rejection, plus restoring revision 1 as revision 3 while preserving the previous document and emitting `config.restore`. ACL tests include encoded traversal/separator/malformed-encoding cases.
Rust-independent release checks additionally validate UI/JSON/language/source contracts and reproducible packaging.
During the prior 0.25.4 compiler gate, an external build exposed one module-privacy error in the history split (`CONFIG_HISTORY_CAPACITY` was private to `db::history`) and one unused `country_resolved` assignment in the location GeoIP branch. Both were corrected before the 0.25.5 baseline: the capacity is exposed to its parent module with `pub(super)`, referenced through `history::CONFIG_HISTORY_CAPACITY`, and the dead assignment is removed. This workspace still has no Rust/Cargo toolchain, so compiler confirmation of the 0.25.7 tree remains a release-host gate.
An external 0.26.0 `cargo` build exposed two compiler regressions that the Rust-independent gates could not detect: `frontend_state` was moved into the HTTP/1 service closure and then reused for connection-error logging, and `serde_json::Map::entry("event".into())` was ambiguous with the pinned dependency graph. Both are corrected in this source tree by giving the service closure its own cloned frontend state and by passing the `"event"` literal directly to `Map::entry`. The Rust-independent release gates pass after these corrections. A fresh compiler-enabled `cargo check/test` is still required to confirm there are no further Rust errors because this workspace has no Rust/Cargo toolchain.
During the prior 0.25.4 compiler gate, an external build exposed one module-privacy error in the history split (`CONFIG_HISTORY_CAPACITY` was private to `db::history`) and one unused `country_resolved` assignment in the location GeoIP branch. Both were corrected before the 0.25.5 baseline: the capacity is exposed to its parent module with `pub(super)`, referenced through `history::CONFIG_HISTORY_CAPACITY`, and the dead assignment is removed.
### Modular source-tree verification
@@ -30,8 +34,8 @@ During the prior 0.25.4 compiler gate, an external build exposed one module-priv
| `python3 scripts/langs_check.py` | PASS |
| `node --check src/admin/web/vhosts/render.js` | PASS |
| `node --check src/admin/web/vhosts/editor.js` | PASS |
| 0.25.7 vhost/dashboard source contracts | PASS: quick enable/disable, backend search/sort/column controls, account-scoped backend table preferences, themed scrollbars and distinct Enabled editor control are present |
| 0.25.7 Prometheus/Grafana source contracts | PASS: retry/client-lifecycle families exist for ordinary and Docker backends and matching dashboard queries are present |
| 0.26.0 stored-log/protocol diagnostics source contracts | PASS: persistent protocol-error emitters, Live Logs separation, authenticated `/logs` explorer, bounded source-ID file access and required diagnostic fields are present |
| retained 0.25.7 vhost/backend observability contracts | PASS: quick enable/disable, backend search/sort/column controls, retry/client-lifecycle metrics and matching dashboard coverage remain present |
| `python3 scripts/memory_contracts.py` | PASS |
| `python3 scripts/web_server_contracts.py` | PASS |
| `python3 scripts/security_contracts.py` | PASS |