v0.26.1
This commit is contained in:
1 parent
ddc5c6593f
commit
b9d137eee1
63 files changed
+1232
-445
No files matched your search
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Administration API access - 0.26.0
|
||||
# Administration API access - 0.26.1
|
||||
|
||||
## Sign-in boundary
|
||||
|
||||
|
||||
+45
-19
@@ -1,44 +1,72 @@
|
||||
# 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**.
|
||||
RustProxy 0.26.1 provides 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.
|
||||
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 routed 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.
|
||||
Existing files remain browsable after their writer is disabled. Low-level `protocol_error` records are sent only through configured persistent structured-log sinks and are deliberately not inserted into Live Logs.
|
||||
|
||||
## Protocol and malformed-traffic records
|
||||
## Vhost-aware protocol 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:
|
||||
Ordinary completed requests use `event: "request"`. Low-level failures use `event: "protocol_error"`. When RustProxy can safely identify a configured vhost, the record is persisted through that vhost's existing sink. In per-vhost file mode an unresolved record uses `_unmatched.jsonl`.
|
||||
|
||||
Attribution priority is:
|
||||
|
||||
1. vhost already selected by normal routing;
|
||||
2. HTTP `:authority`;
|
||||
3. HTTP `Host`;
|
||||
4. TLS/QUIC SNI;
|
||||
5. no safe match -> `_unmatched`.
|
||||
|
||||
All hostname matching reuses the normal exact/wildcard frontend resolver and listener binding rules. A listener or local port by itself is not sufficient to select a vhost.
|
||||
|
||||
A representative record is:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "protocol_error",
|
||||
"reason": "tls_handshake_error",
|
||||
"protocol_error": "tls_handshake_error",
|
||||
"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"
|
||||
"vhost": "app",
|
||||
"sni": "app.example",
|
||||
"tls_sni": "app.example",
|
||||
"authority": null,
|
||||
"matched_by": "sni",
|
||||
"duration_ms": 0
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
`matched_by` is `router`, `authority`, `host`, `sni` or `none`. Compatibility fields from 0.26.0 remain present so existing consumers do not need an immediate migration.
|
||||
|
||||
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.
|
||||
SNI, authority and Host are lookup inputs only. RustProxy never derives a filename directly from client-provided identity text. Only the configured internal vhost identifier returned by the shared resolver can select a vhost sink. Invalid internal IDs fall back to `_unmatched` rather than being sanitized into a new path.
|
||||
|
||||
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.
|
||||
Diagnostic identity values are control-character stripped and bounded before persistence. Existing error-detail redaction and limits remain in force. Raw packets, request bodies and raw malformed request bytes are not captured.
|
||||
|
||||
## Browser security model
|
||||
## Logs Explorer UI
|
||||
|
||||
The workspace supports source, case-insensitive text, event class, HTTP status class, client IP and vhost filters. Text search scans stored values, including protocol diagnostics such as `reason`, `error`, `sni` and `authority`, and refreshes with a short debounce while typing.
|
||||
|
||||
The table provides account-scoped selectable columns. The default view keeps the original concise columns while optional protocol columns expose transport, match source, SNI and authority. At least one column must remain visible.
|
||||
|
||||
The table header stays visible while the result list scrolls. Selecting a row highlights it and expands the complete JSON record inline immediately below that row; selecting it again collapses the details.
|
||||
|
||||
The configured-file inventory is collapsed by default so installations with many per-vhost files do not lose most of the screen to source paths. The source selector still contains the configured sources, including `_unmatched.jsonl` when present.
|
||||
|
||||
For structured access logs, the explorer reconstructs canonical fields from the active JSON field mapping. In particular, `client_ip` remains available to the table and filter even when the output JSON key was renamed by a custom mapping.
|
||||
|
||||
## Browser security and bounds
|
||||
|
||||
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`.
|
||||
|
||||
@@ -46,16 +74,14 @@ 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;
|
||||
- up to 20,000 lines across the complete query, shared across selected sources;
|
||||
- at most 32 sources in an `all` query;
|
||||
- page size at most 500 and offset at most 10,000.
|
||||
|
||||
If more than 32 configured files are eligible for an `all` query, the newest 32 are scanned and the response is marked truncated. A specific source can always be selected directly.
|
||||
|
||||
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.
|
||||
The explorer is diagnostic rather than a retention system. RustProxy does not add rollover, indexing or compression; use the operating-system log rotation policy for retention.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Installing and updating RustProxy 0.26.0
|
||||
# Installing and updating RustProxy 0.26.1
|
||||
|
||||
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,5 +1,9 @@
|
||||
# Deployment and configuration migration history
|
||||
|
||||
## 0.26.0 -> 0.26.1
|
||||
|
||||
No SQLite or saved-configuration migration is required. Version 0.26.1 changes persistent protocol-error attribution and the Stored Logs UI/API normalization only. Existing JSONL files remain readable, Live Logs behavior is unchanged, and unresolved protocol errors continue to use `_unmatched.jsonl`.
|
||||
|
||||
## 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.
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ This is the canonical index for the current project documentation. The authentic
|
||||
- [0.25.1 release audit](V0.25.1_AUDIT.md) - historical Traffic/module-split release scope and compiler-regression guards.
|
||||
- [0.25.0 specification audit](V0.25.0_AUDIT.md) - Web Server foundation point-by-point implementation status.
|
||||
- [0.25.0 pre-production security audit](V0.25.0_SECURITY_PREPROD_AUDIT.md) - threat-oriented source/configuration review, fixes made in-session and mandatory runtime security gates.
|
||||
- [0.26.1 implementation note](V0.26.1_IMPLEMENTATION.md) - vhost-aware protocol logging, shared resolver and Stored Logs UX changes.
|
||||
- [TEST_REPORT](TEST_REPORT.md) - verification commands, results and environment limitations.
|
||||
|
||||
## Functional documentation
|
||||
@@ -60,7 +61,7 @@ This is the canonical index for the current project documentation. The authentic
|
||||
|
||||
## Historical or version-labelled context
|
||||
|
||||
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone; they are not current-release documentation. The current audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); [V0.25.4_AUDIT.md](V0.25.4_AUDIT.md) records the prior retained-history restore milestone and [V0.25.3_AUDIT.md](V0.25.3_AUDIT.md) records the prior request-policy/history milestone and [V0.25.2_AUDIT.md](V0.25.2_AUDIT.md) records the prior backup/session hardening milestone and [V0.25.1_AUDIT.md](V0.25.1_AUDIT.md) records the prior Traffic/module-split milestone and [V0.25.0_AUDIT.md](V0.25.0_AUDIT.md) remains the Web Server foundation audit.
|
||||
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone; they are not current-release documentation. The current release implementation note is [V0.26.1_IMPLEMENTATION.md](V0.26.1_IMPLEMENTATION.md); the latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); [V0.25.4_AUDIT.md](V0.25.4_AUDIT.md) records the prior retained-history restore milestone and [V0.25.3_AUDIT.md](V0.25.3_AUDIT.md) records the prior request-policy/history milestone and [V0.25.2_AUDIT.md](V0.25.2_AUDIT.md) records the prior backup/session hardening milestone and [V0.25.1_AUDIT.md](V0.25.1_AUDIT.md) records the prior Traffic/module-split milestone and [V0.25.0_AUDIT.md](V0.25.0_AUDIT.md) remains the Web Server foundation audit.
|
||||
|
||||
Older context documents such as [INTEGRATIONS_040](INTEGRATIONS_040.md), [MANAGEMENT](MANAGEMENT.md), [SECURITY_AUDIT](SECURITY_AUDIT.md), [ACME_RFC2136](ACME_RFC2136.md) and older version audits remain available for traceability but should not override the current functional/technical documents above.
|
||||
|
||||
|
||||
+17
-32
@@ -1,55 +1,40 @@
|
||||
# Verification report - RustProxy 0.26.0
|
||||
# Verification report - RustProxy 0.26.1
|
||||
|
||||
## Scope
|
||||
|
||||
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.
|
||||
RustProxy 0.26.1 extends the persistent protocol diagnostics introduced in 0.26.0 with safe per-vhost attribution and updates the Stored Logs explorer. `protocol_error` records can now carry canonical `reason`, `error`, `sni`, `authority`, `matched_by` and resolved `vhost` diagnostics while retaining the 0.26.0 compatibility fields. Attribution reuses the normal exact/wildcard frontend resolver and never derives a filename directly from client Host/SNI input. Unresolved traffic remains `_unmatched` in per-vhost file mode, and protocol errors continue to bypass the Live Logs history/broadcast path.
|
||||
|
||||
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.
|
||||
Stored Logs now supports account-scoped selectable columns, sticky headers, highlighted rows with inline JSON details, a collapsed configured-file inventory, custom-field recovery for canonical `client_ip`, and value-based keyword search including protocol diagnostics.
|
||||
|
||||
## Implemented automated coverage
|
||||
## Source-level regression 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.
|
||||
0.26.1-specific Rust tests cover shared logging-vhost resolution and precedence, authority/Host normalization, unknown/missing SNI, safe vhost sink names, bounded protocol identity fields, persistent protocol fields under custom JSON mappings, Live Logs separation, Stored Logs canonical client-IP recovery and protocol-diagnostic text matching. Administration preference tests cover the new Stored Logs column list and backward-compatible defaults.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
- `src/admin/documentation.rs` was split so the embedded Reader document catalog lives in `src/admin/documentation/documents.rs`; both files now stay below the 500-line Rust module limit without changing Reader behavior.
|
||||
- The Rust module contract now recognizes the existing `*_tests.rs` naming convention, and the documentation contract validates embedded documents independently of `include_str!` line wrapping.
|
||||
- `python3 scripts/rust_module_contracts.py`: PASS.
|
||||
- `python3 scripts/source_module_contracts.py`: PASS. Dashboard JavaScript, Docker Agent Go implementation and operational Python modules remain within their source-size contracts.
|
||||
- `python3 scripts/docs_check.py`: PASS. The documentation index, authenticated Reader/API split and local Markdown links are consistent.
|
||||
- Cargo parsing/type checking remains a release-host requirement because this workspace has no Rust/Cargo toolchain.
|
||||
The HTTP/TLS/H2/H3 sources carry the resolved identity through the persistent logging path. TLS ClientHello SNI is retained for later handshake and HTTP/2 failures; HTTP/3 reads the completed QUIC/Rustls handshake identity for later request/connection diagnostics. Failures before a usable identity exists remain unmatched.
|
||||
|
||||
## Verification status in this workspace
|
||||
|
||||
| Check | Status |
|
||||
|---|---|
|
||||
| `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.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 |
|
||||
| `python3 scripts/security_dependency_contracts.py` | PASS |
|
||||
| `python3 scripts/source_module_contracts.py` | PASS |
|
||||
| `python3 scripts/rust_module_contracts.py` | PASS |
|
||||
| `python3 scripts/rust_module_contracts.py` | PASS after keeping modified Rust modules within the 500-line contract |
|
||||
| `python3 scripts/docs_check.py` | PASS |
|
||||
| `node scripts/web_service_ui_test.mjs` | PASS |
|
||||
| OpenAPI/language/Grafana JSON parse | PASS |
|
||||
| `node --check src/admin/web/file-logs.js` | PASS |
|
||||
| language/OpenAPI JSON parse | PASS |
|
||||
| `python3 scripts/check_source.py` | PASS; explicitly reports Rust parsing/types/runtime as not verified |
|
||||
| `python3 scripts/release_smoke.py` | PASS, including upgrade/package/reproducibility contracts |
|
||||
| `go test ./...` in `docker-agent` | PASS |
|
||||
| `python3 scripts/check_source.py` | PASS; explicitly reports Rust parsing/types/runtime not verified |
|
||||
| `python3 scripts/release_smoke.py` | PASS, including SQLite/backup/upgrade, Debian/Grafana and reproducible source-archive contracts |
|
||||
| `./dev.sh test` | Static/security/UI/smoke gates PASS; exits 127 when it reaches Rust because Cargo is unavailable |
|
||||
| `./dev.sh test` | Rust-independent gates PASS; exits 127 when the Rust stage is reached because Cargo is unavailable |
|
||||
| `./dev.sh check` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo fmt --all`, `cargo check --locked --all-targets`, `cargo test --locked --all-targets` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo fmt --check` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo clippy --all-targets --all-features -- -D warnings` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo test --all-targets --all-features` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `bash dev.sh` complete release build | BLOCKED at Rust compilation: Rust/Cargo toolchain unavailable in this workspace |
|
||||
|
||||
All Rust-independent release gates available in this workspace pass. Compiler formatting, parsing, type checking, linking and Rust unit/integration tests still require a Rust-enabled release host before treating the archive as compiler-verified production output.
|
||||
The source archive must not be described as compiler-verified until the Rust-enabled release-host gates pass. Rust-independent gates verify source structure, security contracts, documentation, browser JavaScript, packaging invariants and deterministic archive behavior, but they cannot substitute for Rust parsing, type checking, Clippy or runtime tests.
|
||||
@@ -0,0 +1,104 @@
|
||||
# RustProxy 0.26.1 implementation note
|
||||
|
||||
## Scope
|
||||
|
||||
Version 0.26.1 is a focused follow-up to the persistent protocol diagnostics and Stored Logs explorer introduced in 0.26.0. It does not change SQLite schema 8 or the saved proxy configuration format.
|
||||
|
||||
The release has two goals:
|
||||
|
||||
1. Attribute persistent `protocol_error` records to a configured vhost when that attribution is safe and deterministic.
|
||||
2. Make the Stored Logs explorer practical on installations with many vhosts and useful for protocol diagnostics.
|
||||
|
||||
Live Logs remains a separate bounded in-memory request view. Low-level `protocol_error` records are persistence-only and must not be published to its history/broadcast path.
|
||||
|
||||
## One shared vhost-resolution function
|
||||
|
||||
All protocol adapters use the same resolver; TLS, HTTP/1, HTTP/2 and HTTP/3 must not implement independent hostname matching.
|
||||
|
||||
```rust
|
||||
resolve_logging_vhost(
|
||||
local,
|
||||
tls,
|
||||
routed_vhost,
|
||||
authority,
|
||||
host,
|
||||
sni,
|
||||
) -> Option<ResolvedLogVhost>
|
||||
```
|
||||
|
||||
Resolution priority is:
|
||||
|
||||
1. a vhost already selected by normal routing;
|
||||
2. HTTP `:authority`;
|
||||
3. HTTP `Host`;
|
||||
4. TLS/QUIC SNI;
|
||||
5. no match, which means `_unmatched.jsonl` in per-vhost file mode.
|
||||
|
||||
The helper normalizes authority/Host/SNI with the existing host parser and delegates selection to the same `Snapshot::frontend` exact/wildcard and listener-binding logic used by normal request routing. It returns the configured internal vhost ID and `matched_by`; client input is never turned directly into a filename.
|
||||
|
||||
## Persistent protocol records
|
||||
|
||||
A protocol record keeps the compatibility fields from 0.26.0 and also exposes canonical diagnostics when available:
|
||||
|
||||
- `event = protocol_error`;
|
||||
- `reason` and compatibility `protocol_error`;
|
||||
- `error` and compatibility `error_detail`;
|
||||
- `client_ip` and `local_addr`;
|
||||
- `transport` and `http_version`;
|
||||
- `vhost` when safely resolved;
|
||||
- `sni` and compatibility `tls_sni`;
|
||||
- `authority`;
|
||||
- `matched_by = router | authority | host | sni | none`.
|
||||
|
||||
Diagnostic identity strings are control-character stripped and length-bounded before persistence. Existing secret redaction and error-detail limits remain in force.
|
||||
|
||||
In per-vhost file mode, a resolved record uses the existing configured vhost sink. Invalid internal vhost identifiers fall back to `_unmatched` instead of being sanitized into a new filename. In combined-file mode, sink behavior is unchanged.
|
||||
|
||||
## Protocol integration
|
||||
|
||||
TLS reads SNI from ClientHello before completing the handshake. A later handshake failure/timeout carries that SNI into the shared resolver. If ClientHello itself cannot be parsed, attribution remains unavailable and the record is unmatched.
|
||||
|
||||
HTTP/2 retains the TLS SNI for post-handshake connection/drain diagnostics. Request paths that have an HTTP request object can additionally provide authority/Host.
|
||||
|
||||
HTTP/3 obtains SNI from Quinn/Rustls handshake data after a successful QUIC handshake and uses it for later request, stream and connection protocol failures. Failures that happen before usable handshake identity exists remain unmatched.
|
||||
|
||||
No protocol path infers a vhost solely from a shared listener or local port.
|
||||
|
||||
## Stored Logs explorer changes
|
||||
|
||||
The `/logs` workspace now:
|
||||
|
||||
- highlights the selected log row;
|
||||
- expands Record details inline directly below that row;
|
||||
- removes the separate details panel below the table;
|
||||
- keeps the table header sticky while the log list scrolls;
|
||||
- lets each administrator choose visible Stored Logs columns and persists the choice in existing account preferences;
|
||||
- offers protocol columns for transport, match source, SNI and authority;
|
||||
- keeps the configured-file inventory collapsed by default;
|
||||
- canonicalizes fields through the active structured-log field mapping so `client_ip` remains visible when the JSON output key was renamed;
|
||||
- searches record values case-insensitively and includes `reason`, `error`, `sni`, `authority` and other stored values;
|
||||
- refreshes keyword results with a short debounce while typing;
|
||||
- retains the existing bounded source/byte/line/page limits and reports truncation when an `all` query contains more sources than can be scanned safely.
|
||||
|
||||
## Security invariants
|
||||
|
||||
The browser still accepts only opaque configured source IDs; it never accepts arbitrary filesystem paths. File reads remain regular-file-only and retain Unix `O_NOFOLLOW` protection.
|
||||
|
||||
SNI, Host and authority are lookup inputs only. They cannot select a filesystem path directly. Only a configured vhost resolved through the existing routing model can select a per-vhost log sink.
|
||||
|
||||
## Regression coverage
|
||||
|
||||
Source tests cover:
|
||||
|
||||
- exact and wildcard hostname precedence using the shared resolver;
|
||||
- router priority over authority/Host/SNI;
|
||||
- Host with port and case-insensitive normalization;
|
||||
- unknown and missing SNI returning no vhost;
|
||||
- mapped `client_ip` normalization in Stored Logs;
|
||||
- keyword matching against protocol diagnostic values;
|
||||
- safe per-vhost filename fallback;
|
||||
- bounded protocol identity fields;
|
||||
- protocol errors remaining outside Live Logs;
|
||||
- preference validation for selectable Stored Logs columns.
|
||||
|
||||
A Rust-enabled release host must still run `cargo fmt --check`, Clippy with warnings denied, all-target/all-feature tests and the complete `dev.sh` release build before the package is considered compiler-verified.
|
||||
Reference in new issue
Block a user