This commit is contained in:
Mateusz Gruszczyński committed 2026-10-04 22:53:33 +02:00
1 parent 174389e5b1
commit e6604e41fc
73 files changed
+1240 -201

No files matched your search

+4 -1
View File
@@ -65,6 +65,7 @@ framework extraction failures are not guaranteed to have that JSON shape.
|---|---|
| `GET /healthz` | Public process health/version; not a backend health probe |
| `GET /api/config` | `{revision, config}` |
| `GET /api/config/history` | Retained revision metadata; `?revision=N` returns that document plus the previous retained document |
| `PUT /api/config` | Input `{expected_revision, config}`; returns the applied envelope; retained for atomic multi-section changes |
| `PUT /api/config/{section}` | Input `{expected_revision, value}`; replaces one validated section and returns the applied envelope |
| `GET /api/backends` | Authenticated per-vhost backend observations and active/desired frontend plans |
@@ -86,6 +87,8 @@ and rechecks session validity. It does not queue an unlimited per-viewer event l
or guarantee replay. The browser falls back to polling. An active dashboard renews
idle activity, not the absolute session deadline.
Configuration history retains the newest 512 successful manual/configuration revisions in SQLite schema 8. `GET /api/config/history` is session-protected like the rest of the administration API; `limit` is bounded to 512 and `revision` selects one retained revision for diff/inspection.
Configuration updates return 409 for a stale revision, 422 for validation failure
and 429 when the eight-operation update admission bound is reached. Persistence and
snapshot publication finish before a successful response. Disconnecting the caller
@@ -475,7 +478,7 @@ when enabled. Transformed cached representations add the `transformed` Cache-Sta
extension parameter. `X-Cache-Status` is stripped if supplied by an origin but is not
emitted by RustProxy.
`diagnostic_headers.hide_mock_origin` is removed and old documents containing it are rejected on
export; `request_id` is the only diagnostic-header switch. No revision response-header behavior remains. TLS listener
export; `request_id` is the only diagnostic-header switch. The generated request ID is a 64-hex-character SHA-256 value and the same protected value is forwarded to the origin as `X-Request-ID`. No revision response-header behavior remains. TLS listener
http3 omission is default-on; explicit null is persisted off. Legacy listeners use
https_http3 with identical default/null behavior.
+1 -1
View File
@@ -1,4 +1,4 @@
# Administration API access - 0.25.2
# Administration API access - 0.25.3
## Sign-in boundary
+4 -2
View File
@@ -91,9 +91,11 @@ Hop-by-hop/framing headers, forwarding identity headers, RustProxy request IDs a
## Access control
A location can define its own IPv4/IPv6 CIDR ACL. The location ACL is additive: it cannot override a deny enforced by the host policy, reusable ACL profile or vhost ACL.
A location can define its own IPv4/IPv6 CIDR ACL, select a reusable ACL profile, add a location-specific country block/allow-only rule, and configure an HTTP method policy (`off`, `allow` or `block`). Method names are normalized to uppercase and validated as HTTP method tokens.
Authentication, vhost rate limits, country policy, security headers, compression, custom error pages and WebSocket policy still apply through the normal vhost pipeline.
All access layers are additive. The request must pass the vhost method policy, location method policy, host/profile/vhost IP ACLs, location profile ACL, inline location ACL and every applicable country policy. A location therefore cannot override a denial from its vhost. Method denial returns `405 Method Not Allowed` (with `Allow` for allow-list mode); IP/country denials retain the normal `403` pipeline. These checks occur before backend selection/connection.
Authentication, vhost rate limits, security headers, compression, custom error pages and WebSocket policy still apply through the normal vhost pipeline.
## What is intentionally not an Nginx parser
+1 -1
View File
@@ -1,4 +1,4 @@
# Installing and updating RustProxy 0.25.2
# Installing and updating RustProxy 0.25.3
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.
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.
## User-Agent, country and Traffic analytics
+6
View File
@@ -1,5 +1,11 @@
# Deployment and configuration migration history
## 0.25.2 -> 0.25.3
SQLite schema 7 is upgraded automatically to schema 8. The migration adds `configuration_history`; on first open the current revision is seeded into history, and subsequent successful configuration saves retain the newest 512 revisions for the dashboard revision-history view. No manual SQL is required.
New vhost/location access fields are additive. Missing `method_policy`, location `acl_profile_id`, location country policy and location inline ACL fields retain the prior behavior. RustProxy-generated request IDs are now SHA-256 values and are forwarded to origins as protected `X-Request-ID`; custom request-header rules cannot replace that correlation identifier.
## 0.25.1 -> 0.25.2
There is no SQLite schema or saved-configuration migration. Full backup creation may now run while the service is active; restore still requires RustProxy to be stopped. Backup archive version 2 preserves validated internal relative symlinks such as Certbot `live/` entries pointing into the profile `archive/` tree. Existing archive-version-1 backups remain accepted for restore.
+3 -2
View File
@@ -8,7 +8,8 @@ This is the canonical index for the current project documentation. The authentic
- [ROADMAP](../ROADMAP.md) - planned and completed release scope.
- [CHANGELOG](../CHANGELOG.md) - user/operator-visible changes by release.
- [TODO](TODO.md) - open work only; completed work is not retained as TODO.
- [0.25.2 release audit](V0.25.2_AUDIT.md) - live backup, internal symlink and resumed-origin recovery scope plus release gates.
- [0.25.3 release audit](V0.25.3_AUDIT.md) - request-method policy, location ACL/country controls, hashed request IDs and configuration-history scope plus release gates.
- [0.25.2 release audit](V0.25.2_AUDIT.md) - historical live backup, internal symlink and resumed-origin recovery scope.
- [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.
@@ -57,7 +58,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.2_AUDIT.md](V0.25.2_AUDIT.md); [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 audit is [V0.25.3_AUDIT.md](V0.25.3_AUDIT.md); [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.
+1
View File
@@ -35,6 +35,7 @@ This is a settings fragment, not a replacement for the existing routing/vhost do
A complete opt-in example is in `examples/cache-dashboard.json`.
The dashboard exposes the complete raw JSON editor under **Settings -> Manual config**.
The same card exposes **Revision history**, which reads the newest 512 retained configuration revisions from SQLite schema 8 and groups them by month/day. Selecting a revision shows timestamp, actor and a bounded path-level before/after diff against the previous retained revision.
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.
+10 -3
View File
@@ -1,11 +1,16 @@
# Verification report - RustProxy 0.25.2
# Verification report - RustProxy 0.25.3
## Scope
RustProxy 0.25.2 is a focused live-backup, backup-symlink and resumed-admin-session hardening release on top of the 0.25.1 source tree. Web Services are independent configuration resources; vhosts and custom locations only choose a source type and reference a Web Service ID. The current development tree provides static GET/HEAD streaming, directory/index behavior, validators, single ranges, document-root confinement, ordered `try_files`, Web Service rewrites, a bounded migration-oriented `.htaccess` compatibility layer, expanded MIME policy, per-extension/MIME Cache-Control rules, fixed source headers, precompressed sidecars, forced-download extensions and optional bounded FastCGI/PHP-FPM. The Web Service editor is organized into General, Routing, .htaccess, MIME, Cache & headers, Delivery, PHP-FPM and Security tabs, with one-shot presets for Static/SPA/PHP/WordPress/Drupal/Laravel/Symfony/Joomla/PrestaShop/Downloads. Existing 0.25 work also includes repeatable directory listing, Web Server vhost-wide metrics, Logging sub-tabs, corrected structured-log source validation, bounded per-vhost Traffic analytics with effective-client-IP rankings/donut charts, User-Agent/optional-country logging, stable Web Service rendering after fast refreshes, generic/customizable 403 handling and automatic RSS relief for reclaimable allocator/HOT-cache memory. The `/docs` surface separates the Markdown Reader from API/OpenAPI and documents the implemented PHP-FPM runtime.
RustProxy 0.25.3 adds vhost/location HTTP method policy, location-scoped ACL-profile/IP/country controls, hashed end-to-end request IDs and retained manual-configuration history on top of 0.25.2. SQLite advances from schema 7 to schema 8 through an additive `configuration_history` migration; existing configuration remains compatible and new policy fields default to disabled/inherited behavior. The dashboard adds a calendar-style revision-history dialog without changing the existing Manual config editor workflow.
## Source contracts reviewed
- Vhost and location `method_policy` values validate and normalize HTTP methods; host and selected-location policies are cumulative and deny with 405 before upstream work. Allow-list denial includes `Allow`.
- Custom locations can select an `acl_profile_id` and add inline IP ACL plus country access policy. Profile and inline rules are compiled with the route and checked before authentication/backend selection; a location cannot weaken a vhost denial.
- Every observed exchange receives a fresh 64-hex SHA-256 `request_id`, overwrites inbound `X-Request-ID` before forwarding, and reuses the same value for diagnostics/logging. Custom header rules continue to protect `X-Request-ID`.
- SQLite schema 8 stores revision/time/actor/document in bounded `configuration_history`; successful CAS writes record history transactionally and retain the newest 512 revisions. The session-protected `/api/config/history` API supports metadata listing and one-revision detail with the previous retained document.
- The Manual config history UI groups retained revisions by month/day, lists revisions for a selected day and renders a bounded recursive path-level before/after diff using text-only DOM operations.
- `Config.web_services` is a separate persisted collection; vhost/location `source` defaults to Reverse Proxy for old documents and Web Server stores only `web_service_id`.
- Full configuration compilation validates Web Service IDs, absolute/existing document roots, index names, duplicate IDs and every active Web Service reference. A pure static vhost does not require a backend pool; mixed Web Server and Reverse Proxy locations do.
- Web Server dispatch occurs after normal frontend routing, ACL and Basic/Tinyauth authorization and before backend selection/DNS/cache/upstream work. Static responses continue through the existing custom error-page, response-header/security, compression and transport pipeline.
@@ -44,10 +49,12 @@ RustProxy 0.25.2 is a focused live-backup, backup-symlink and resumed-admin-sess
## Implemented automated coverage
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. `src/traffic/tests.rs` includes bounded-table and effective-client-IP view 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. ACL tests include encoded traversal/separator/malformed-encoding cases.
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. `src/traffic/tests.rs` includes bounded-table and effective-client-IP view 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, and database tests cover retained configuration revision documents, actor metadata and previous-revision lookup. ACL tests include encoded traversal/separator/malformed-encoding cases.
Rust-independent release checks additionally validate UI/JSON/language/source contracts and reproducible packaging.
A compiler-enabled external build subsequently exposed one module-privacy error in the new history split (`CONFIG_HISTORY_CAPACITY` was private to `db::history`) and one unused `country_resolved` assignment in the location GeoIP branch. The source package now exposes the capacity to its parent module with `pub(super)`, references it through `history::CONFIG_HISTORY_CAPACITY`, and removes the dead assignment. This workspace still has no Rust/Cargo toolchain, so compiler confirmation of the corrected tree remains a release-host gate.
### Modular source-tree verification
- `python3 scripts/rust_module_contracts.py`: PASS. Every repository `.rs` file is at most 500 lines.
+29
View File
@@ -0,0 +1,29 @@
# RustProxy 0.25.3 release audit
RustProxy 0.25.3 adds request-method policy, location-scoped access controls, end-to-end request correlation and retained manual-configuration history. SQLite moves from schema 7 to schema 8 through an additive migration.
## Access policy
| Requirement | Status | Evidence |
| --- | --- | --- |
| Allow/block HTTP methods per vhost | IMPLEMENTED | `Vhost.method_policy` validates normalized HTTP methods; the request path returns 405 before origin work when the host policy denies a method. |
| Allow/block HTTP methods per Custom location | IMPLEMENTED | `PathRoute.method_policy` is compiled and enforced after route selection and before authentication/upstream selection. |
| Reuse ACL profiles per Custom location | IMPLEMENTED | Locations accept `acl_profile_id`; profile IP and country policies are resolved during configuration compilation. |
| Add inline location IP/country policy | IMPLEMENTED | Location inline CIDR ACL and country allow/block policy compose with vhost/profile policy and fail before origin work. |
| Preserve additive security semantics | IMPLEMENTED | A location cannot weaken a denial made by vhost/profile policy; all applicable policies must allow the request. |
## Request correlation
Each accepted exchange receives a fresh 64-hex-character SHA-256 request identifier derived from an internal sequence plus cryptographic OS randomness. RustProxy overwrites the protected `X-Request-ID` request header with that value before origin forwarding. The same value is available to diagnostic response headers when enabled and to access logging as `request_id`.
## Manual configuration history
SQLite schema 8 adds `configuration_history`. On upgrade, the current configuration is seeded if no history row exists. Each successful CAS configuration replacement records revision, timestamp, actor and document in the same transaction and trims retained history to the newest 512 rows.
The authenticated `GET /api/config/history` endpoint returns retained revision metadata or a selected revision with its previous retained document. The Manual config dashboard provides a revision-history dialog grouped by month/day; selecting a revision renders actor/time metadata and bounded path-level before/after changes. The endpoint uses the existing administrator session guard.
## Compatibility and release gates
Missing new policy fields deserialize to disabled/default behavior. Existing schema-7 databases upgrade automatically. Old binaries may reject a configuration saved with new fields, so rollback should restore both the old source and the pre-upgrade data backup.
Rust-independent source, documentation, language, UI and release-smoke checks are part of the release tree. A follow-up compiler run outside this packaging workspace found and prompted correction of the history-capacity module visibility plus a dead location GeoIP assignment; the corrected source uses `history::CONFIG_HISTORY_CAPACITY` with parent-module visibility and no redundant assignment. Compiler-enabled `cargo fmt --all`, `cargo check --locked --all-targets` and `cargo test --locked --all-targets` remain mandatory on a release host with Rust installed.
+7 -6
View File
@@ -128,15 +128,16 @@ still validated, but create no pool or additional active listener.
## Paths, policies and migration
`routes` contains per-vhost paths: `id`, `path_prefix`, `enabled`, `source`, nullable `cache`,
optional `acl`, `headers` and `backend_id`. `source` selects Reverse Proxy or a reusable Web Service;
`routes` contains per-vhost paths: `id`, match fields, `enabled`, `source`, nullable `cache`,
optional `acl_profile_id`, inline `acl`, `country_access`, `method_policy`, `headers` and `backend_id`. `source` selects Reverse Proxy or a reusable Web Service;
`backend_id` is valid only for Reverse Proxy. An empty array leaves the main vhost source as the fallback. Longest
segment-boundary prefix wins. Origins receive the original path/query; prefixes
are not stripped. IDs must use ASCII letters, digits, hyphens and underscores.
Host-policy, reusable ACL-profile, vhost and route ACLs are ANDed before pool
selection/DNS/cache. A vhost can attach one reusable `acl_profile_id`; the profile may
contain an IP ACL plus a country block/allow-only rule. Inline vhost/location controls
remain independent and cannot be weakened by the profile. Their client identity is the
Host method policy, reusable ACL profiles, vhost ACL/country policy and route method/profile/inline
ACL/country policies are cumulative before pool selection/DNS/cache. A vhost and each route can
attach a reusable `acl_profile_id`; profiles may contain an IP ACL plus a country block/allow-only
rule. Inline controls remain independent and cannot be weakened by a profile. Method-policy denial
returns 405; allow-list mode also emits `Allow`. Their client identity is the
validated effective peer address, not an untrusted forwarding header.
There is no legacy routing UI/runtime or automatic importer in 0.8.0. Development
+9 -1
View File
@@ -89,6 +89,12 @@ blocking that address for the rest of the window. Busy hashing returns 503; a
blocked address returns 429 with Retry-After. This is abuse resistance, not MFA or
a replacement for perimeter rate limiting, especially for many clients behind NAT.
## HTTP method policy
Each vhost can leave method filtering **Off**, define an **Allow only** list, or define a **Block** list. Methods are validated as HTTP method tokens and normalized to uppercase. A denied request returns `405 Method Not Allowed` before authentication, cache, DNS or origin connection; allow-list mode also returns an `Allow` header containing the configured methods.
A Custom location has the same independent method policy. Vhost and location policies are cumulative: a location cannot re-enable a method rejected by its vhost. This makes patterns such as `GET, HEAD, POST` allow-only at host level and a stricter `GET, HEAD` allow-only policy on a read-only location explicit and fail-closed.
## Custom locations
The **Custom locations** tab supports ordinary prefix, priority prefix (`^~`), exact (`=`),
@@ -97,7 +103,9 @@ a selected priority prefix skips regex evaluation; regexes are tested in declara
longest ordinary prefix is the fallback. Exact paths preserve a meaningful trailing slash.
A proxy location can use the normal pool, pin one backend, inherit/override cache behavior and
apply an additional ACL. Path handling can preserve the public URL, strip a prefix, replace a
apply an additional inline ACL. It can also select a reusable ACL profile, apply its own country
allow/block policy and define an HTTP method allow/block policy. These controls are cumulative with
the vhost policies and are enforced before a backend connection is opened. Path handling can preserve the public URL, strip a prefix, replace a
prefix or perform a capture-based regex rewrite. Query parameters are preserved. Repeated literal
slashes are normalized only for routing/ACL decisions; without an explicit rewrite the backend
receives the original target, including `//`. Encoded separators, percent double-decoding and dot