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

+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: