- Web Server reads static files inline; proxy middleware chain flattened - About window with license summary and project links; AGPL-3.0-or-later - Rewritten documentation: feature list, screenshots, performance guide - Fix false spikes in per-Vhost request-rate charts
14 KiB
Administration API and configuration automation
RustProxy exposes an authenticated administration API on the separate administration listener (normally 127.0.0.1:9090). Open /docs → API after signing in to view the bundled OpenAPI/Swagger schema, including request bodies and available actions. That schema is the endpoint reference; server validation remains authoritative.
Selected API endpoints
The current bundled src/admin/openapi.json declares these operations (among others):
| Method and path | Purpose |
|---|---|
GET /healthz |
Minimal health response, without privileged state. |
GET /api/auth/status |
Whether first-time administrator setup is required. |
POST /api/auth/login · POST /api/auth/logout |
Start/end a browser admin session. |
GET /api/auth/session |
Validate current admin session. |
GET /api/config · PUT /api/config |
Read or replace validated, revisioned configuration. GET also returns read-only storage metadata (SQLite/JSON source and paths). |
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). |
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. |
GET /api/logs · GET /api/live-logs |
Recent and streaming request logs. |
GET /api/file-logs · GET /api/file-logs/query |
Stored-log exploration. |
GET /api/traffic · GET /api/system-info |
Traffic and system observations. |
GET /api/metrics/json · GET /api/metrics/summary |
Full JSON snapshot (includes all Prometheus samples and summary) or compact computed summary only, both pull-only. Monitoring. |
GET /api/docker · POST /api/docker/refresh |
Docker-agent observation and refresh. |
GET /api/certificates · POST /api/certificates/upload |
Certificate inventory/import. |
POST /api/certificates/inspect |
Read DNS SAN coverage from a managed/external public PEM path for the editor; admin browser session only. |
GET /api/acme · POST /api/acme/issue · POST /api/acme/renew |
Managed certificate jobs. |
GET/PUT /api/preferences |
Personal UI settings. |
GET /api/dashboard-presets and named preset operations |
Saved Overview presets. |
GET /docs/openapi.json |
Protected machine-readable API specification. |
Some paths have additional DELETE/PATCH operations, job-status resources, query parameters or restricted actions. Use OpenAPI for the actual fields/methods, not guesses based on a table. GET /metrics here is not a replacement for the separate Prometheus exporter.
Certificate inspection accepts { "cert_file": "filename.pem" } for a managed file or an absolute external path and returns { "cert_file": "...", "covered_hosts": ["example.com"] }. It applies the existing certificate-file trust and size checks and reads no private key. It makes no configuration or permission changes. This POST requires the signed-in administrator's normal Origin, request-marker and CSRF checks; general RO/RW API tokens and certificate-only credentials cannot call it.
Access and authentication
The administration listener is HTTP on loopback (127.0.0.1:9090) by default and is separate from the public Vhost listeners. No administrator password is bundled: complete first-time setup with the local CLI (admin setup-token or admin init). For remote access prefer an SSH tunnel; for direct access enable administration HTTPS with a trusted certificate and restrict the network before binding outside loopback.
| Credential | Scope |
|---|---|
| Browser session | Full administration. HttpOnly, SameSite=Strict cookie (Secure on HTTPS), absolute lifetime 7–30 days. Writes require matching Origin, X-CSRF-Token and X-RustProxy-Request: 1. |
General API token (ro / rw) |
Created in Settings → Security → API tokens. Displayed once, stored as a hash, bound to a mandatory IP/CIDR allowlist. Sent as Authorization: Bearer <token>. ro reads resources and sections; rw also writes configuration sections and selected operations. Cannot manage authentication, backups, tokens or certificate credentials. |
| Certificate API key | Key ID + Bearer token limited to specific certificate IDs and actions (reload by default, deploy opt-in). |
| Prometheus token | Scrape-only credential for the separate exporter; see Monitoring. |
IP checks use the transport peer, never forwarded headers. Outside loopback, Bearer credentials require administration HTTPS.
/docs Reader and Swagger
The authenticated /docs page has two tabs: Reader with the guides embedded at build time, and API with the bundled OpenAPI schema rendered by Swagger UI. All assets are served locally; no CDN is used. Swagger uses the current session, executes read calls directly and asks for confirmation before any write.
Publishing the panel behind a reverse proxy
This is a special-risk topology. Configure a fixed HTTPS public origin, an allowed hostname and a trusted forwarding boundary. Ordinary public Vhosts never forward administration cookies to an upstream. Re-test CSRF/Origin validation and cookie security after any topology change.
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. Browser-session writes require the matching Origin,
X-CSRF-TokenandX-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.
- The serialized configuration limit is 2 MiB across all sections; JSON request bodies allow a bounded 64 KiB formatting overhead. Saves over the limit are rejected without applying partial changes.
- Configuration writes are revisioned (
expected_revision) and rejected if based on a stale save.GET /api/configreturns{revision, config, storage};storageis runtime metadata and must not be copied inside the editableconfigobject 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/cachereturns{revision, section: "cache", value: {…}}.PUT /api/config/cacheaccepts{"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:
logginguses{"logging": {…}, "error_log": {…}};responseuses{"server_header": {…}, "diagnostic_headers": {…}, "compression": {…}};global-securityuses a singleGlobalSecurityobject includinguser_agent_blocking. For this section, omittinguser_agent_blockingis an invalid PUT rather than silently clearing the denylist. Independentuser-agent-blockinganderror_logsection endpoints do not exist. The prior logging-onlyPUT /api/config/loggingpayload 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 atGET/PUT /api/config/geoip(serialized fromconfig.geoip_updates). After saving,POST /api/config/geoip/updatetriggers a source refresh. The old standalone/api/geoip/updateno 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 intoglobal_security.user_agent_blockingwhen validated. If both positions contain conflicting policies, the import is rejected. Canonical GET and saved config use the nested field. Per-vhostuser_agent_blockingis 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. An ordinary Vhost may configure mutually exclusive oidc, authelia and authentik objects alongside existing tinyauth/basic_auth settings; every optional provider defaults to disabled. OIDC uses /_rustproxy/oidc/callback on an exact Vhost HTTPS hostname and requires issuer/client/session secrets. See Access.
Complete section-update example
GET /api/config/global-security
{
"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:
rust-proxy --data-dir /var/lib/rustproxy config get
rust-proxy --data-dir /var/lib/rustproxy config validate config.json
rust-proxy --data-dir /var/lib/rustproxy config apply config.json
rust-proxy --data-dir /var/lib/rustproxy config reload
rust-proxy --data-dir /var/lib/rustproxy backup create --output /private/site.rpb
Run CLI commands as an authorized local operator with the correct private data directory. The admin subcommands provide first setup, password recovery, session revocation and status; tls supports certificate generate/import/deploy/reload. Run rust-proxy --help for exact options.
Restricted Certificate API credentials
Settings → Certificate API can create credentials containing a Key ID + Bearer token with explicit certificate-ID grants. New keys default to reload-only; deploy permission is separate. The clear token is displayed at creation/rotation and is not a general admin API token.
An authorized client sends:
Authorization: Bearer <secret>
X-RustProxy-Certificate-Key: <key-id>
The credential can be used only for allowed certificate reload/deploy operations, not configuration, users, metrics or secret management. Non-loopback use requires the appropriate administration HTTPS security boundary. Rotate broad or legacy credentials. See Certificates.