This commit is contained in:
Mateusz Gruszczyński committed 2026-10-25 17:45:57 +01:00
1 parent bd077754a9
commit ed7e3377c1
40 files changed
+1764 -365

No files matched your search

+6
View File
@@ -2,6 +2,12 @@
Access control can be configured globally, on a Vhost and on selected Custom locations. Policies are **cumulative**: a more specific allow rule does not cancel a denial at a broader level. Changes should be tested with both an allowed and a denied client.
## Global block / allow policy (0.34.13)
**Settings → Rules & headers → Global block / allow** applies to public HTTP traffic across all vhosts, redirects and not-found hosts before upstream access. In addition to the existing User-Agent deny patterns, use the global country block/allow list (ISO two-letter codes), client IP/CIDR and allow-only DNS ACL, HTTP method policy, request Content-Type denylist and shared per-IP rate limit. All are disabled by default except previously configured User-Agent blocking. An explicit IP allow does **not** bypass the country policy: both must permit a request. Missing GeoIP country is allowed for country block mode and denied for country allow mode.
Global restrictions add to per-vhost and path rules. Rate-limit requests are counted for each client IP **across** vhosts. Prefer a conservative shared ceiling to avoid blocking shared NAT clients. A global method denial returns 405, the content-type/ACL/UA policy returns 403, and the shared rate limiter returns 429 with `Retry-After`. Admin and TCP/UDP stream listeners are outside this HTTP scope. For global response headers, use the adjacent **Security headers** subtab; see [Security](SECURITY.md).
## IP, country and DDNS rules
- **IP/CIDR ACL:** allow or deny IPv4/IPv6 networks. Use reusable ACL profiles for settings shared by several domains. Both ordinary and Docker Vhosts use a searchable multi-select with selected badges and a preview of IP/CIDR rules, country restrictions and the default policy; the selection changes presentation only, not ACL evaluation. A Vhost can select up to eight profiles (`acl_profile_ids`, or one legacy `acl_profile_id`); Docker assignments can override or inherit the agent's profile selection.
+4
View File
@@ -11,6 +11,10 @@ ancestor even though it can host a Web Server document root. See
[Installation: external paths](INSTALL.md#linux-debrpm-read-only-access-to-external-paths)
for scoped ACLs and Certbot renewal handling.
### Certificate file permission command preview (0.34.13)
The permissions explanation now appears next to generated read/traversal ACL commands, rather than alongside the path inputs. The **Copy commands** action is offered only after a valid path selection produces a command preview. These commands grant read access to the certificate/private key and traversal of parent directories; they do not grant write permission.
## Supported certificate methods
| Mode | Requirements |
+63 -2
View File
@@ -16,7 +16,8 @@ The current bundled `src/admin/openapi.json` declares these operations (among ot
| `POST /api/config/reload` | Revalidate and hot-reload manually changed `--config` JSON; revision-aware. |
| `GET/POST /api/api-tokens` · `DELETE /api/api-tokens/{id}` | List/generate/revoke general RO/RW API tokens (browser-admin-only). |
| `GET/POST/DELETE /api/prometheus-token` | Inspect/generate/rotate/revoke a standalone Prometheus scrape token (browser-admin-only). |
| `PUT /api/config/{section}` | Update one supported configuration section. |
| `GET /api/config/{section}` · `PUT /api/config/{section}` | Read or atomically replace one of 23 supported configuration sections. |
| `POST /api/config/geoip/update` | Run a GeoIP download/check/reload immediately using already-saved GeoIP settings; does not change revision. |
| `GET /api/config/history` · `POST /api/config/history/restore` | Review/restore retained configuration snapshots. |
| `GET /api/status` · `GET /api/backends` | Operational status and backend observations. |
| `GET /api/cache` · `POST /api/cache/purge` · `POST /api/cache/invalidate` | Cache observations and authenticated invalidation. |
@@ -38,11 +39,71 @@ Certificate inspection accepts `{ "cert_file": "filename.pem" }` for a managed f
## Authentication and safe writes
- Ordinary API calls require an administrator browser/session cookie on the admin listener, or a scoped general API Bearer token for explicitly allowlisted API methods/paths. Logging in also provides CSRF context. Writes require correct Host/Origin, `X-CSRF-Token` and `X-RustProxy-Request: 1` as enforced by the server.
- Ordinary API calls require an administrator browser/session cookie on the admin listener, or a scoped general API Bearer token for explicitly allowlisted API methods/paths. Browser-session writes require the matching Origin, `X-CSRF-Token` and `X-RustProxy-Request: 1`, in addition to Host validation. Scoped Bearer-token requests are exempt from browser CSRF requirements; the server enforces the token permission, source IP allowlist and HTTPS for non-loopback access instead.
- The Swagger **API** tab uses the signed-in session. Read calls can be executed; mutations require explicit confirmation. It cannot bypass server-side authorization.
- Configuration writes are revisioned (`expected_revision`) and rejected if based on a stale save. `GET /api/config` returns `{revision, config, storage}`; `storage` is runtime metadata and must not be copied inside the editable `config` object or included as a write parameter. PUT and history-restore responses remain `{revision, config}`. Prefer section updates over replacing the entire document when practical.
- Each supported section has the same **GET** and **PUT** value shape. `GET /api/config/cache` returns `{revision, section: "cache", value: {…}}`. `PUT /api/config/cache` accepts `{"expected_revision": 12, "value": {…}}`, using the **entire** section value from a fresh GET after modification. The response is the full updated `{revision, config}` envelope (not a section view). Even changes to different sections must use the latest global revision: concurrent stale writes return 409. This is replacement, not JSON Merge Patch.
- Section boundaries: `logging` uses `{"logging": {…}, "error_log": {…}}`; `response` uses `{"server_header": {…}, "diagnostic_headers": {…}, "compression": {…}}`; `global-security` uses a **single** `GlobalSecurity` object including `user_agent_blocking`. For this section, omitting `user_agent_blocking` is an invalid PUT rather than silently clearing the denylist. Independent `user-agent-blocking` and `error_log` section endpoints do not exist. The prior logging-only `PUT /api/config/logging` payload is no longer accepted: read the full logging section and send the paired value.
- The section names use kebab-case (e.g. `not-found-hosts`, `docker-agents`, `global-security`) even when the matching configuration JSON key is snake_case (`not_found_hosts`, `docker_agents`, `global_security`). GeoIP configuration lives at `GET/PUT /api/config/geoip` (serialized from `config.geoip_updates`). After saving, `POST /api/config/geoip/update` triggers a source refresh. The old standalone `/api/geoip/update` no longer exists. This action changes loaded database files, **not** the config revision. A read-only API token cannot execute it; a read/write token can.
- Full-document config imports remain backward compatible with older root-level `user_agent_blocking`; it is migrated into `global_security.user_agent_blocking` when validated. If both positions contain conflicting policies, the import is rejected. Canonical GET and saved config use the nested field. Per-vhost `user_agent_blocking` is unchanged. RO General API tokens may GET sections, RW tokens may PUT sections; the same existing authentication, Host/Origin, revision and audit checks apply.
- `/docs`, API schemas, configuration, logs, certificates and most observations are **not anonymous**. Keep the listener restricted and use HTTPS or a tunnel for remote access.
### Available configuration sections
The section name in the URL and its corresponding JSON member in a full configuration are:
| Section path (`/api/config/…`) | JSON member(s) in `config` |
|---|---|
| `listeners` | `listeners` |
| `docker-agents` | `docker_agents` |
| `http2` | `http2` |
| `pages` | `pages` |
| `cache` | `cache` |
| `transform` | `transform` |
| `forwarding` | `forwarding` |
| `headers` | `headers` |
| `prometheus` | `prometheus` |
| `geoip` | `geoip_updates` |
| `administration` | `administration` |
| `tls` | `tls` |
| `vhosts` | `vhosts` |
| `web-services` | `web_services` |
| `php-fpm-profiles` | `php_fpm_profiles` |
| `redirects` | `redirects` |
| `not-found-hosts` | `not_found_hosts` |
| `streams` | `streams` |
| `acl-profiles` | `acl_profiles` |
| `host-policies` | `host_policies` |
| `response` | `server_header`, `diagnostic_headers`, `compression` |
| `logging` | `logging`, `error_log` |
| `global-security` | `global_security`, including `user_agent_blocking` |
Every GET returns a `value` object or array whose shape can be sent unchanged to the matching PUT. For the three grouped sections the `value` contains exactly the named members; for the other sections it is the serialized value of the matching configuration member. Saving does not alter unrelated sections.
### Complete section-update example
```http
GET /api/config/global-security
```
```json
{
"revision": 7,
"section": "global-security",
"value": {
"user_agent_blocking": {"enabled": true, "patterns": ["bad-bot"]},
"country_access": {"mode": "off", "countries": []},
"acl": null,
"method_policy": {"mode": "off", "methods": []},
"content_type_policy": {"enabled": false, "blocked": []},
"rate_limit": {"enabled": false, "requests": 100, "window_seconds": 60, "burst": 20},
"security_headers": {"hsts": {"enabled": false, "max_age_seconds": 31536000, "include_subdomains": false, "preload": false}, "nosniff": false, "frame_options": "preserve", "referrer_policy": null, "content_security_policy": null, "permissions_policy": null}
}
}
```
Use the actual **complete** `value` returned from GET as the PUT `value` after editing it; the example is illustrative, not a substitute for fetching the active defaults. Then send `PUT /api/config/global-security` with `{"expected_revision":7,"value":{...}}`. A successful PUT returns the complete new configuration envelope; HTTP **409** requires re-reading the section and applying the intended changes to the newer revision. GET never writes. The UI and API use the same validation and transaction path.
## Local CLI alternatives
The included `rust-proxy` program supports configuration and backup operations without turning browser sessions into automation tokens:
+4
View File
@@ -2,6 +2,10 @@
RustProxy provides an optional shared HTTP response cache with a bounded in-memory **HOT** tier and optional persistent-disk **COLD** tier. Both the global cache and a Vhost's effective policy must permit caching. **COLD storage is disabled by default**, and newly created Vhosts do not automatically cache dynamic content.
### Cache controls in Settings (0.34.13)
The static-extension cookie-bypass warning sits with its extension list, separate from **Cache response headers** options. Only enable this optimization for files whose bytes do not depend on the user's session. `Cookie` can then be omitted from the shared cache key for the configured extensions; responses with `Set-Cookie`, `private`, `no-store` or `Vary: Cookie` remain uncacheable.
## Enable caching safely
1. Open **Settings → Cache**, enable the global cache and choose memory limits. Enable COLD only if you want durable disk storage and have enough private disk capacity.
+21
View File
@@ -62,6 +62,27 @@ ACLs support IP/CIDR and **allow-only** DNS/DDNS hostname rules, plus reusable p
Global response headers, per-Vhost rules and Web Service-specific headers are distinct scopes. Security and proxy-protocol headers may be protected from rewriting. Compression supports eligible response encodings; availability is still constrained by global limits and the requesting client.
### Global HTTP block / allow and security headers (0.34.13)
Under **Settings → Rules & headers**, choose **Global block / allow** to configure rules applying to every public HTTP vhost, redirect and 404 host. Independent controls cover **country block/allow**, global **User-Agent deny patterns**, **IP/CIDR and allow-only DNS ACL**, **HTTP method allow/block**, **request Content-Type denylist** and a **shared per-client-IP rate limit**. Disabled policies are neutral. If country allow mode is selected, an unknown GeoIP country is denied; block mode permits unknown countries. Global IP and country checks are cumulative, as are global rules and more restrictive per-vhost policies. Verify the trusted client-IP and GeoIP configuration before enabling country or rate controls. The global rate limit uses the same bucket for a given client across all public vhosts. It does not affect TCP/UDP streams or the separate administration listener.
The adjacent **Security headers** subtab applies a global response baseline: **HSTS** (HTTPS only), **nosniff**, **X-Frame-Options**, **Referrer-Policy**, **Content-Security-Policy** and **Permissions-Policy**. Explicit per-vhost security-header values take precedence; absence of a vhost value does not cancel a global header. Test HSTS and CSP against all sites before enabling. **Global custom headers** remains a distinct subtab for arbitrary request/response header rules.
In the updated **0.34.13** API, global User-Agent blocking is nested at `config.global_security.user_agent_blocking`; it is no longer a separate root-level setting. Example (only the relevant fragment):
```json
{
"global_security": {
"user_agent_blocking": {"enabled": true, "patterns": ["bad-bot"]},
"country_access": {"mode": "off", "countries": []}
}
}
```
`GET /api/config/global-security` returns `{revision, section, value}` where `value` is the entire `global_security` object. To update it, send `PUT /api/config/global-security` with `{ "expected_revision": <current-revision>, "value": <complete-global_security-object> }`; the User-Agent member is **required** in the section PUT. Both Global block / allow and Security headers forms save this complete object, preserving unchanged controls from the current revision. Validation and persistence are atomic. The formerly separate `PUT /api/config/user-agent-blocking` is no longer supported.
On upgrade, older JSON, database rows, backups and revision history containing a root-level `user_agent_blocking` are migrated at validation/compilation to the nested policy. If both old and new keys are supplied with differing values, validation fails instead of choosing one. Old configurations without `global_security` retain disabled defaults for all newly added policies. See [Administration API](API.md), [Access](ACCESS.md) and [Security](SECURITY.md).
## Cache and transforms
Caching is opt-in. HOT holds bounded memory entries; COLD is an optional disk tier (off by default). Freshness follows eligible HTTP policy and `Vary`; private/no-store responses and protected requests are not shared. Configuration saves can selectively invalidate affected cached responses; 0.34.0 behavior needs production validation. HTTP BAN/PURGE is **disabled by default** and uses an explicit source-IP allowlist. See [Cache](CACHE.md).
+2 -2
View File
@@ -1,6 +1,6 @@
# RustProxy documentation
Practical guides for **RustProxy 0.34.12**. This index and the listed pages are also available in the authenticated panel at **`/docs → Reader`**. The adjacent **API** tab provides the embedded OpenAPI reference. Paths below are links to actual files in the source archive.
Practical guides for **RustProxy 0.34.13**. This index and the listed pages are also available in the authenticated panel at **`/docs → Reader`**. The adjacent **API** tab provides the embedded OpenAPI reference. Paths below are links to actual files in the source archive.
## Getting started
@@ -22,7 +22,7 @@ Practical guides for **RustProxy 0.34.12**. This index and the listed pages are
- [Experimental TLS post-quantum key exchange](TLS_POST_QUANTUM.md) — opt-in X25519MLKEM768, compatibility, rollback and validation.
- [TCP/UDP Streams](STREAMS.md) — non-HTTP forwarding and its security boundaries.
The Vhosts and Docker discovery guides include the shared adaptive request-rate chart scale introduced in 0.34.11. Version 0.34.12 preserves that adaptive range and adds a disabled-by-default X/Y scale display option under **Settings → Appearance → Charts**. See [Dashboard](DASHBOARD.md).
The Vhosts and Docker discovery guides include the shared adaptive request-rate chart scale introduced in 0.34.11. Version 0.34.12 preserves that adaptive range and adds a disabled-by-default X/Y scale display option under **Settings → Appearance → Charts**. Release 0.34.13 adds global HTTP security policies and UI layout improvements, groups the global User-Agent policy under `global_security`, provides consistent section-based GET/PUT APIs, and moves manual GeoIP refresh to `POST /api/config/geoip/update`. See [Dashboard](DASHBOARD.md).
## Integrations and operations
+6
View File
@@ -17,6 +17,12 @@ RustProxy provides several security controls, but it is not a complete Web Appli
- Honor forwarding identity headers only from correctly configured trusted peers. A client can forge arbitrary `X-Forwarded-*` and User-Agent headers.
- Restrict Basic Auth to HTTPS and protect Tinyauth endpoints. Authentication traffic should never be stored in public shared cache.
### Global HTTP security policy
**Settings → Rules & headers → Global block / allow** is a shared first gate for public HTTP listeners. Countries, User-Agents, IP/CIDR (including allow-only DNS), methods, declared request Content-Types and an optional shared rate limit can be configured there. These rules run before vhost routing, redirects and cache invalidation and do not replace Basic Auth, Tinyauth, application controls or OS firewalling. The country and IP decisions are both required to permit a request; a country deny cannot be bypassed by an IP allow and vice versa. Country mode **allow** is fail-closed for an unknown country; **block** mode is fail-open for unknown GeoIP data. Resolve identity from a trusted source and keep GeoIP updates healthy.
The **Security headers** subtab controls an HTTPS HSTS policy and optional global nosniff, frame protection, referrer, CSP and permissions policies. They are a common baseline; per-vhost values can set matching headers, but an unset field does not remove a global value. Rejecting only declared Content-Type is not a WAF, and changing HSTS/CSP may break sites. Changes do not apply to TCP/UDP Streams or the private administration listener.
## Content and cache privacy
- Shared cache must refuse private/no-store content and unsafe authenticated representations. Test behavior with cookies, `Authorization`, `Set-Cookie`, `Vary` and conditional requests.