This commit is contained in:
Mateusz Gruszczyński committed 2026-10-08 11:27:18 +02:00
1 parent d940f9995f
commit 65e082b9d1
61 files changed
+1225 -581

No files matched your search

+14 -10
View File
@@ -88,7 +88,7 @@ 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. `POST /api/config/history/restore` also requires the normal CSRF/request-marker protections, rejects stale `expected_revision`, and never rewinds the revision counter: restoring revision 85 while current revision is 120 creates revision 121.
Configuration history, introduced in SQLite schema 8 and retained in current schema 9, keeps the newest 512 successful manual/configuration revisions. `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. `POST /api/config/history/restore` also requires the normal CSRF/request-marker protections, rejects stale `expected_revision`, and never rewinds the revision counter: restoring revision 85 while current revision is 120 creates revision 121.
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
@@ -380,8 +380,8 @@ contracts and INTEGRATIONS_040.md for configuration examples.
## 0.22.3 certificate hot reload and external deployment
`POST /api/certificates/reload` is an authenticated, Origin/CSRF-protected control-plane
operation. It re-reads every certificate/key file referenced by the current desired
`POST /api/certificates/reload` is an administrator-session-only, Origin/CSRF-protected control-plane
operation. Certificate API bearer tokens are intentionally not accepted by this global endpoint. It re-reads every certificate/key file referenced by the current desired
configuration, validates a fresh immutable routing/TLS snapshot and publishes it at the
**same configuration revision**. Invalid or mismatched material returns an error and the
previous snapshot stays active. The response contains `reloaded`, `revision`, `public_tls`,
@@ -389,7 +389,7 @@ previous snapshot stays active. The response contains `reloaded`, `revision`, `p
handshakes use the refreshed certificates; established connections are not renegotiated.
Administration TLS is startup-fixed and therefore reports when a process restart is needed.
`POST /api/certificates/{certificate_id}/reload` is the targeted variant for a manual/file-backed certificate (`issuer == null`). It validates the requested configured ID and atomically republishes the immutable TLS snapshot at the same configuration revision. It is intended for external renewers that replace the files already referenced by RustProxy. A manual entry may use simple filenames from `<data-dir>/certs` or two absolute external paths such as `/etc/letsencrypt/live/example/fullchain.pem` and `/etc/letsencrypt/live/example/privkey.pem`. Absolute paths are resolved again on every reload, so Certbot's `live/` symlinks follow the newly renewed generation. Application-managed ACME certificate IDs are rejected with `409` because their renewal pipeline owns those files. The endpoint accepts either the normal administrator session + CSRF flow or a scoped Certificate API token.
`POST /api/certificates/{certificate_id}/reload` is the targeted variant for a manual/file-backed certificate (`issuer == null`). It validates the requested configured ID and atomically republishes the immutable TLS snapshot at the same configuration revision. It is intended for external renewers that replace the files already referenced by RustProxy. A manual entry may use simple filenames from `<data-dir>/certs` or two absolute external paths such as `/etc/letsencrypt/live/example/fullchain.pem` and `/etc/letsencrypt/live/example/privkey.pem`. Absolute paths are resolved again on every reload, so Certbot's `live/` symlinks follow the newly renewed generation. Application-managed ACME certificate IDs are rejected with `409` because their renewal pipeline owns those files. The endpoint accepts either the normal administrator session + CSRF flow or a Certificate API token whose grant explicitly contains that certificate ID.
`POST /api/certificates/deploy` accepts
`{"certificate_id":"shared-cert","cert_pem":"...","key_pem":"..."}` under the same
@@ -418,9 +418,11 @@ the generic PEM staging endpoint; deploy/upload request bodies never accept host
paths. Absolute paths may only be saved explicitly in administrator TLS configuration as a
manual certificate source, where they are validated and never returned as PEM contents.
## 0.23.0 scoped Certificate API credentials
## 0.28.2 least-privilege Certificate API credentials
External automation no longer needs a full administrator browser session. From **Settings -> Certificate API**, an administrator can create multiple dedicated credentials (up to 32), each consisting of a Key ID, Bearer token and optional non-secret comment. Each Key ID can be rotated independently. The clear token is returned only by the create/rotation response; SQLite stores only its SHA-256 hash plus metadata/comment. Every credential has exactly two scopes: certificate deploy and certificate reload.
External automation does not need a full administrator browser session. From **Settings -> Certificate API**, an administrator can create multiple dedicated credentials (up to 32), each consisting of a Key ID, Bearer token, optional non-secret comment and an explicit allowlist of 1-32 certificate IDs. New credentials are **reload-only by default**. Deploy is an independent opt-in permission.
The clear token is returned only by the create/rotation response; SQLite stores only its SHA-256 hash plus metadata and the grant. Credentials created before 0.28.2 migrate to a reload-only compatibility grant for all certificate IDs (`*`). Rotate those credentials after upgrading to replace the wildcard with explicit IDs.
API clients send both headers:
@@ -429,7 +431,7 @@ Authorization: Bearer <token>
X-RustProxy-Certificate-Key: <key-id>
```
Example Certbot/acme.sh post-renew reload for an external-path certificate:
A reload-only token may call only the targeted endpoint for an allowlisted certificate:
```bash
curl --fail --show-error -X POST \
@@ -440,14 +442,16 @@ curl --fail --show-error -X POST \
https://proxy-admin.example/api/certificates/shared-cert/reload
```
A Certificate API credential is accepted only by `POST /api/certificates/deploy`, `POST /api/certificates/reload`, and `POST /api/certificates/{certificate_id}/reload`; it cannot read configuration, logs, metrics or any other administration endpoint. Non-loopback use requires the configured HTTPS administration surface. The normal administrator session + Origin/request-marker + CSRF path remains valid for the same operations. Credential management remains administrator-only: `GET /api/certificate-api` lists metadata, `PUT /api/certificate-api` creates/rotates one Key ID, `DELETE /api/certificate-api/{key_id}` revokes one token, and `DELETE /api/certificate-api` revokes all tokens.
Certificate API tokens are **not** accepted by the global `POST /api/certificates/reload` endpoint. This prevents a compromised renewal token from forcing reads/reloads of unrelated certificate material. `POST /api/certificates/deploy` is available only when the credential was explicitly created with deploy permission, and the `certificate_id` in the request body must also be present in that credential's allowlist.
Example external deploy:
A Certificate API credential cannot read configuration, logs, metrics, users or any other administration endpoint. Non-loopback use requires the configured HTTPS administration surface. The normal administrator session + Origin/request-marker + CSRF path remains valid for certificate operations. Credential management remains administrator-only: `GET /api/certificate-api` lists metadata/grants, `PUT /api/certificate-api` creates or rotates one Key ID, `DELETE /api/certificate-api/{key_id}` revokes one token, and `DELETE /api/certificate-api` revokes all tokens.
Example external deploy for a token that has **Also allow deploy** enabled and contains `shared-cert` in its allowlist:
```bash
curl --fail --show-error \
-H 'Authorization: Bearer TOKEN' \
-H 'X-RustProxy-Certificate-Key: certbot-main' \
-H 'X-RustProxy-Certificate-Key: deploy-hook' \
-H 'Content-Type: application/json' \
--data-binary @deploy.json \
https://proxy-admin.example/api/certificates/deploy