v0.34.0_fina;
This commit is contained in:
1 parent
1ca019d849
commit
c207e8a03f
69 files changed
+1496
-13838
No files matched your search
+15
-229
@@ -1,243 +1,29 @@
|
||||
# Administration API access - 0.29.1
|
||||
# Administration authentication and API access
|
||||
|
||||
## Sign-in boundary
|
||||
## Default exposure and setup
|
||||
|
||||
The administration listener remains separate from reverse-proxy listeners and uses
|
||||
loopback HTTP by default. Settings -> Administration can enable HTTPS, select either
|
||||
the application-managed default certificate or an inventory certificate, persist a
|
||||
new bind address and allow explicit DNS hostnames. A persisted non-loopback bind is
|
||||
accepted when HTTPS is enabled. The legacy startup flags remain only for deliberately
|
||||
plain remote HTTP. Public vhost `/docs` paths are not captured by the admin listener.
|
||||
The administration listener uses **HTTP loopback (`127.0.0.1:9090`) by default** and is separate from normal Vhost listeners. No administrator password is bundled. Use the local CLI `admin setup-token` or `admin init` to complete first-time setup. For access from another workstation, prefer an SSH tunnel; for direct remote operation, enable **administration HTTPS**, configure a trusted certificate and narrow network access before binding outside loopback.
|
||||
|
||||
When the dashboard is published through a trusted reverse proxy, the hostname of
|
||||
the optional `administration.public_origin` is automatically allowed for panel
|
||||
Host validation. Any extra `administration.allowed_hosts` DNS entry is also
|
||||
matched against the external authority rather than the private admin socket port. `X-Forwarded-Host` is trusted only from loopback or a
|
||||
configured trusted proxy; the external `X-Forwarded-Proto` additionally requires the
|
||||
forwarded-proto trust setting except for loopback. Origin/CSRF checks and Secure cookies use
|
||||
that external origin. Direct IP/localhost access still requires the real administration port.
|
||||
## Session security
|
||||
|
||||
### Publishing the panel through RustProxy itself
|
||||
Administrator sessions use an HttpOnly, SameSite=Strict cookie, with Secure on HTTPS. Expiry is bounded by the configured absolute lifetime (7–30 days, default 30). Logout, password changes and revoke-all terminate sessions. API mutations are validated for Host/Origin, CSRF token and request marker; a cookie copied into automation is not an approved general-purpose API token.
|
||||
|
||||
0.16.0 supports the explicitly opted-in same-process topology where a public HTTPS vhost proxies to the local administration listener. The reserved `rustproxy_session` cookie is passed through only when all of these conditions are true: the browser-facing request is TLS, its DNS hostname either matches the pinned `administration.public_origin` or is explicitly listed in `administration.allowed_hosts`, the selected backend directly addresses the configured administration socket by IP, and that backend has `accept_local_listener_risk=true`. The control-plane route is forced out of shared cache.
|
||||
Unauthenticated endpoints intentionally expose only minimal setup/sign-in and health behavior. Configuration, operational status, dashboard assets, file logs, the API specification and the documentation Reader require authentication. Admin responses use no-store and security headers, and the browser does not expose the full application state before sign-in.
|
||||
|
||||
This is a narrow exception to the normal isolation rule. For every ordinary backend RustProxy still removes `rustproxy_session` before forwarding requests and strips an upstream attempt to set that cookie in responses. DNS backend names are not accepted for the special self-admin cookie path. This prevents a normal application backend from reading or replacing the administration session.
|
||||
## `/docs` Reader and Swagger
|
||||
|
||||
The anonymous page contains only the sign-in/setup form, logo, shared stylesheet,
|
||||
a dedicated login script, and filtered language strings. It does not fetch the
|
||||
configuration, status, metrics, logs, certificate inventory, dashboard modules or
|
||||
the full translation bundle. Adding a new language JSON still discovers that
|
||||
language at build time; login strings use the same fallback rules.
|
||||
The authenticated documentation page has two tabs: **Reader**, embedding the registered Markdown guides at compile time, and **API**, displaying the bundled OpenAPI schema. Both are local assets; no runtime CDN access is required for viewing the reference. A document edit affects the shipped reader **only after rebuilding** RustProxy.
|
||||
|
||||
Exact anonymous methods/paths:
|
||||
Swagger uses the current signed-in session, not a new broad credential. It requires explicit confirmation for write operations. Treat the OpenAPI schema as a request guide and RustProxy's field/authorization validation as the final authority.
|
||||
|
||||
| Method | Paths | Content |
|
||||
|---|---|---|
|
||||
| GET/HEAD | `/`, `/overview`, `/vhosts`, `/redirects`, `/streams`, `/acl-profiles`, `/configuration`, `/tls`, `/cache`, `/logging`, `/settings` | Minimal login when no valid cookie is present; the same URL returns the authenticated application shell when the supplied session is valid. `/configuration` is a compatibility alias for Settings -> Manual config. |
|
||||
| GET/HEAD | `/assets/login.js`, `/assets/login-langs.json`, `/assets/style.css`, `/favicon.svg` | Login assets; no runtime state |
|
||||
| GET/HEAD | `/healthz` | `{"status":"ok"}` only |
|
||||
| GET/HEAD | `/api/auth/status` | `{"setup_required":false}` or `true` only |
|
||||
| POST | `/api/auth/login`, `/api/auth/setup` | Credential/capability validation, exact Origin and request marker required |
|
||||
## Publishing the panel behind a reverse proxy
|
||||
|
||||
The document allowlist above is intentionally session-aware: a browser refresh on a protected
|
||||
panel path first validates the cookie server-side instead of rendering a false logged-out state.
|
||||
The login document also rechecks `/api/auth/session` when restored from browser history, preventing
|
||||
a stale back/forward-cache copy from contradicting a still-valid session. The authenticated shell
|
||||
also treats `auth.invalid_origin` during startup/session revalidation as a stale restored-page signal
|
||||
and performs at most one reload of the current same-origin URL. A repeated failure is shown normally;
|
||||
server Host/Origin, request-marker and CSRF validation is unchanged. Every other admin
|
||||
path/method, including full assets, `/api/metrics`, `/metrics`, unknown endpoints and the OpenAPI
|
||||
schema, requires an active database session.
|
||||
Missing/invalid/expired cookies return `401 {"error":"auth.required"}` before
|
||||
query/body extraction. Invalid Host/Origin may return 403 first. Only GET/HEAD
|
||||
`/docs` and `/docs/` navigate to login through a fixed `303 /?next=docs` redirect;
|
||||
API/spec/assets never return login HTML instead of a status error.
|
||||
This is a **special-risk topology**, not the default. Configure a fixed HTTPS public origin/allowed hostname and a trusted forwarding boundary. RustProxy has an explicit narrowly constrained local-listener exception, but a normal public Vhost must not pass administration cookies to an ordinary upstream. Forwarded Host/Proto values are trusted only when the direct peer satisfies the configured trust rules. Test CSRF/Origin validation and cookie security after a topology change.
|
||||
|
||||
## Certificate-only credentials
|
||||
|
||||
Administrator browser sessions have a configurable absolute lifetime of **7–30 days**
|
||||
(default 30) through `administration.session_days`. The server stores only a hash of the
|
||||
session secret, keeps the cookie HttpOnly/SameSite=Strict (and Secure on HTTPS), and
|
||||
never extends a session past its creation-time absolute expiry. The cookie is refreshed
|
||||
with only the remaining lifetime so an upgraded still-valid short session can adopt the
|
||||
new configured bound. Logout, password change and revoke-all still invalidate server-side
|
||||
sessions immediately.
|
||||
Certificate API Key ID + Bearer credentials are limited by certificate ID and by allowed action (reload by default; deploy opt-in). They do not grant login or administrative configuration access. Revocation/rotation are administrator-only actions. Keep tokens out of logs and pass them over HTTPS outside loopback.
|
||||
|
||||
Responses are no-store. Headers include CSP, nosniff, frame denial, no-referrer and
|
||||
restricted camera/microphone/geolocation permissions. Error responses do not expose
|
||||
request-body excerpts for malformed anonymous login/setup input. Anonymous responses contain no application version; fingerprint/revision
|
||||
response headers are stripped on the admin surface. Authenticated status and the
|
||||
protected schema intentionally retain the current version. The application name/logo and the single setup-needed bit are intentional
|
||||
public information, not claims of anonymity or software fingerprint prevention.
|
||||
## Offline build assets
|
||||
|
||||
Authenticated `GET /api/status` also returns a small `runtime` object with the platform,
|
||||
statistics adapter name, resolved data directory and SQLite database path. This is used by
|
||||
the Settings UI so Linux, BSD/OPNsense and custom installations show their actual runtime
|
||||
paths instead of browser-side guesses. The object is not available anonymously and does
|
||||
not expose credentials, environment values, certificate contents or arbitrary filesystem
|
||||
listings.
|
||||
|
||||
## Docs Reader and API view
|
||||
|
||||
`/docs` opens an authenticated documentation workspace. The default **Reader** tab loads
|
||||
`/docs/content.json`, a build-time bundle of the project Markdown documentation listed in
|
||||
`docs/README.md`. The bundle contains documentation source text only; it does not contain
|
||||
live configuration, credentials or runtime state. Reader links between bundled Markdown
|
||||
documents stay inside the same authenticated documentation surface.
|
||||
|
||||
The separate **API** tab uses Swagger UI. `/docs/openapi.json` describes the local endpoints and common request schemas; it
|
||||
contains no live configuration, actual credentials, generated keys, user accounts,
|
||||
cache entries or listener addresses. Extensible schemas summarize configuration;
|
||||
the Rust validator remains authoritative for the full configuration model.
|
||||
|
||||
Documentation uses the same HttpOnly `rustproxy_session` cookie as the panel.
|
||||
Do not paste session cookies into the schema or store them in browser storage.
|
||||
GET execution is enabled by default; write execution requires a checkbox and an
|
||||
explicit confirmation for each request. The script validates the current session,
|
||||
allows only documented same-origin operations, and sends the current session's
|
||||
`X-CSRF-Token` plus `X-RustProxy-Request: 1`. The browser supplies Origin. The server
|
||||
performs the real authorization checks regardless of any browser options.
|
||||
|
||||
Login/setup operations and indefinite SSE streams are documented but not executed
|
||||
by Swagger UI. Native logout/password writes can invalidate the browser session; reload or
|
||||
sign in again afterward. Ordinary API mutations still require the administrator session,
|
||||
Origin/request marker and CSRF token.
|
||||
|
||||
0.28.2 keeps the deliberately narrow Certificate API exception for automation and makes it least-privilege. An operator can create one or more **Certificate API Key ID + Bearer token** credentials in Settings -> Certificate API. Each new credential requires an explicit allowlist of 1-32 certificate IDs and is reload-only by default. Deploy access must be enabled separately.
|
||||
|
||||
A reload-only credential authorizes only:
|
||||
|
||||
- `POST /api/certificates/{certificate_id}/reload` when that ID is allowlisted.
|
||||
|
||||
Certificate API credential inspection (`GET /api/certificate-api` or `GET /api/certificate-api/{key_id}`) is likewise administrator-session only and returns no hashes/secrets. `PATCH /api/certificate-api/{key_id}` changes metadata/grants without touching the secret; `POST /api/certificate-api/{key_id}/regenerate` replaces the hash and invalidates the old token. Both writes require session + CSRF/Origin/request-marker protection. The replacement secret is revealed only once in the regeneration response. External Key ID + Bearer clients are not authorized to call any credential-management endpoint.
|
||||
|
||||
`POST /api/certificates/reload` is administrator-session only and never accepts a Certificate API token. `POST /api/certificates/deploy` accepts a Certificate API token only when the credential has explicit deploy permission and the request's `certificate_id` is also allowlisted.
|
||||
|
||||
Both `Authorization: Bearer <token>` and `X-RustProxy-Certificate-Key: <key-id>` are required. The clear token is returned only on creation/rotation; the database stores its SHA-256 hash. Up to 32 Key IDs can coexist, and each can be revoked independently or all at once. Credentials created before 0.28.2 migrate to reload-only compatibility access for all certificate IDs (`*`); rotate them to narrow the allowlist. The credential does not authorize configuration, logs, metrics, users or any other endpoint. Non-loopback use is accepted only on the configured HTTPS administration surface. Revocation from Settings invalidates that credential immediately.
|
||||
|
||||
Local host automation does not need this token. The installed binary can run
|
||||
`rust-proxy tls deploy ...` or `rust-proxy tls reload` directly; it obtains short-lived local
|
||||
CLI authority from the local database and calls the guarded loopback administration API.
|
||||
`dev.sh` is a development helper and is not required in production.
|
||||
|
||||
The external validator, query-supplied schema/server configuration and persisted
|
||||
Swagger authorization are disabled. Runtime JS/CSS are served from the same admin
|
||||
origin behind its guard. Only the two necessary Swagger vendor asset filenames, the documentation bundle and a plain-text license notice
|
||||
(`/docs/licenses.txt`) are routed; all require an administrator session.
|
||||
Swagger's inline layout styles have a document-scoped CSP exception; scripts stay
|
||||
self-only with no unsafe-eval allowance. The ordinary dashboard CSP is unchanged.
|
||||
|
||||
Logout broadcasts an in-memory notification between tabs. The panel drops its
|
||||
operational document on logout/revocation; documentation clears its schema,
|
||||
responses and write opt-in. Docs recheck sessions on actions, visibility and every
|
||||
30 seconds while visible. Browser history restoration revalidates; this is not a
|
||||
promise to erase browser/OS screenshots or data intentionally downloaded by an admin.
|
||||
|
||||
## First build / offline assets (fixed in 0.7.2)
|
||||
|
||||
No `SWAGGER_UI_DOWNLOAD_URL` environment variable or `.cargo/config.toml` is required.
|
||||
The default is Swagger UI **5.33.1**, selected by `scripts/build/swagger_assets.py` and
|
||||
called from this application's `build.rs`. The former `utoipa-swagger-ui` adapter
|
||||
is removed: there is no separate dependency build silently choosing its older
|
||||
historical browser default. Direct Cargo builds using `--manifest-path` use the
|
||||
project's absolute manifest directory, not the caller's working directory.
|
||||
|
||||
Python **3.10+ is now also required for direct Cargo builds**, as it already was
|
||||
for `dev.sh`. `build.rs` tries `python3`, then `python` if that executable is absent.
|
||||
Use `RUST_PROXY_PYTHON=/absolute/path/to/python3` to select an interpreter explicitly.
|
||||
The helper uses Python's verified HTTPS transport and standard-library ZIP reader;
|
||||
`curl` is no longer a Swagger build requirement. Missing tools/network/assets give
|
||||
an actionable build error rather than an absent-environment-variable panic.
|
||||
|
||||
Normal build (no Swagger environment setup):
|
||||
|
||||
```bash
|
||||
cd RustProxy
|
||||
bash dev.sh check
|
||||
bash dev.sh test
|
||||
bash dev.sh release
|
||||
```
|
||||
|
||||
The first build still needs the normal Cargo registry and the pinned upstream
|
||||
archive. Swagger JS/CSS and license notices are embedded in **both debug and
|
||||
release binaries**, served behind the existing admin guard. No runtime download,
|
||||
CDN, public asset endpoint or alternate authentication mechanism was introduced.
|
||||
|
||||
An offline or GitHub-restricted build can use a reviewed local copy of the same
|
||||
GitHub **tag archive**, with its original `swagger-ui-5.33.1/` directory structure:
|
||||
|
||||
```bash
|
||||
export SWAGGER_UI_DOWNLOAD_URL=file:///srv/build-assets/swagger-ui-5.33.1.zip
|
||||
# Optional: require the archive digest independently verified by your build team.
|
||||
# export SWAGGER_UI_ARCHIVE_SHA256=<64-hex-digit-verified-digest>
|
||||
bash dev.sh check
|
||||
bash dev.sh test
|
||||
bash dev.sh release
|
||||
```
|
||||
|
||||
Unset/empty source values use the default pin. Different remote URLs and old tag
|
||||
archives are rejected. Local file URLs support escaped spaces and must be absolute.
|
||||
`SWAGGER_UI_ARCHIVE_SHA256`, when set, checks the entire archive before publication.
|
||||
No upstream checksum is fabricated: HTTPS provenance, the archive's declared tag
|
||||
and its layout are not themselves a cryptographic authenticity guarantee for an
|
||||
operator-supplied ZIP. Independently review/pin local inputs.
|
||||
|
||||
The helper only reads exact, bounded members; it does not extract arbitrary ZIP
|
||||
paths. It retains licenses/notices, validates package name/version, rejects duplicate
|
||||
required members, symlinks and oversized assets, caches only validated downloads in
|
||||
Cargo's output directory, and writes outputs atomically. Failed preparation blocks
|
||||
compilation instead of embedding stale assets. A `source.json` receipt records
|
||||
archive and output SHA-256 values in `OUT_DIR/swagger-ui/`.
|
||||
|
||||
Cargo tracks changes to the helper, source/digest/interpreter environment variables,
|
||||
local or cached ZIP and generated outputs. Repeated identical preparation preserves
|
||||
output timestamps. `CARGO_NET_OFFLINE=true` forbids a new helper download; ordinary
|
||||
Cargo dependencies must also be cached for a fully offline build. A local archive
|
||||
is the recommended explicit setup for `cargo --offline`; Cargo CLI offline flags
|
||||
are not assumed to propagate automatically into subprocess environments.
|
||||
|
||||
0.24.1 adds cross-platform statistics dependencies. This packaging workspace has no Cargo toolchain, so it does not fabricate or hand-edit a resolver result. Generate and preserve `Cargo.lock` with Cargo on the compiler-enabled release host, then run `cargo check --locked` / the normal `dev.sh` gates before deployment.
|
||||
|
||||
Build-helper tests use synthetic ZIPs; they do not claim to render Swagger. This
|
||||
environment lacks Rust/Cargo and cannot fetch the upstream assets. Compilation,
|
||||
real embedding/rendering and live access-control acceptance remain to be run on a
|
||||
build server. Browser tests still label their simulated API/Swagger renderer.
|
||||
|
||||
## Focused live regression probe
|
||||
|
||||
After running the built server:
|
||||
|
||||
```bash
|
||||
python3 tools/audit/admin_audit.py --url http://127.0.0.1:9090
|
||||
```
|
||||
|
||||
The script sends anonymous read-only loopback probes, follows no redirects, ignores
|
||||
proxy environment variables, prints no response bodies, and fails on unexpected
|
||||
public content/statuses. It does not send credentials or mutate configuration. It
|
||||
is also called by the real-binary security integration suite. Rust router tests
|
||||
add write/method/duplicate-cookie/CSRF checks. A passing probe is a regression gate,
|
||||
not a complete pentest, dependency scan or guarantee against all vulnerabilities.
|
||||
|
||||
## Custom location headers
|
||||
|
||||
Vhosts -> edit -> Custom locations -> Location headers. Add rules independently
|
||||
for proxy requests (to backend) and responses (to client). `set` replaces, `append`
|
||||
adds another field value, `remove` has no value. Maximum 32 rules per direction.
|
||||
The existing JSON editor is synchronized and remains optional. Invalid JSON,
|
||||
newlines, unknown operations and protected names cannot silently save an old draft.
|
||||
|
||||
Rules apply global -> vhost -> matching location. Proxy transport, forwarding,
|
||||
verified authentication identity, configured Server/security/diagnostic policies
|
||||
remain system-managed and can override or forbid a conflicting custom rule.
|
||||
Values are literal, not Nginx variables; changing Host/forwarded identity still uses
|
||||
the designated backend/forwarding controls. Headers for sibling locations remain
|
||||
unchanged. No configuration schema migration is needed for existing header rules.
|
||||
|
||||
## References
|
||||
|
||||
- OWASP REST Security Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
|
||||
- OWASP Session Management Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
|
||||
- Swagger cookie authentication: https://swagger.io/docs/specification/v3_0/authentication/cookie-authentication/
|
||||
- Swagger UI configuration: https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
|
||||
- Cargo configuration discovery: https://doc.rust-lang.org/cargo/reference/config.html
|
||||
- Cargo build scripts: https://doc.rust-lang.org/cargo/reference/build-scripts.html
|
||||
- Pinned browser release: https://github.com/swagger-api/swagger-ui/tree/v5.33.1
|
||||
Swagger JavaScript/stylesheets are prepared by the build and embedded behind authentication. If compilation fails during Swagger asset preparation, inspect `build.rs` and `scripts/build/swagger_assets.py` and ensure the required checked/downloaded asset inputs are available. Do not replace these authenticated assets with an unaudited public CDN workaround.
|
||||
Reference in new issue
Block a user