v0.28.1
This commit is contained in:
1 parent
3b3f0db377
commit
d940f9995f
47 files changed
+542
-187
No files matched your search
+15
-3
@@ -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. 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 scoped Certificate API token.
|
||||
|
||||
`POST /api/certificates/deploy` accepts
|
||||
`{"certificate_id":"shared-cert","cert_pem":"...","key_pem":"..."}` under the same
|
||||
@@ -414,8 +414,9 @@ CLI deploy hook, which calls that loopback API after bounded local file checks:
|
||||
The command resolves Certbot's local `live/` symlinks, checks bounded regular files/private-key permissions, then sends the pair to `POST /api/certificates/deploy`. The running service validates the certificate/key and DNS SANs, stages generation-specific files inside RustProxy's private certificate directory, and creates or switches the **manual** certificate id through the normal revision-CAS configuration path. A single id may already be selected by multiple vhosts, so those vhosts switch together
|
||||
without individual edits. RustProxy-managed ACME entries (`issuer != null`) are rejected to
|
||||
avoid two renewal owners for the same lineage. The existing `/api/certificates/upload` remains
|
||||
the generic PEM staging endpoint; private certificate paths are never accepted from a remote
|
||||
request.
|
||||
the generic PEM staging endpoint; deploy/upload request bodies never accept host filesystem
|
||||
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
|
||||
|
||||
@@ -428,6 +429,17 @@ Authorization: Bearer <token>
|
||||
X-RustProxy-Certificate-Key: <key-id>
|
||||
```
|
||||
|
||||
Example Certbot/acme.sh post-renew reload for an external-path certificate:
|
||||
|
||||
```bash
|
||||
curl --fail --show-error -X POST \
|
||||
-H 'Authorization: Bearer TOKEN' \
|
||||
-H 'X-RustProxy-Certificate-Key: certbot-main' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{}' \
|
||||
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.
|
||||
|
||||
Example external deploy:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Administration API access - 0.28.0
|
||||
# Administration API access - 0.28.1
|
||||
|
||||
## Sign-in boundary
|
||||
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ each Docker host, so the proxy container never needs Docker-socket access. See
|
||||
Build locally with a normal OCI builder:
|
||||
|
||||
```bash
|
||||
docker build -t rustproxy:0.28.0 .
|
||||
docker build -t rustproxy:0.28.1 .
|
||||
```
|
||||
|
||||
Persist `/var/lib/rustproxy`; it contains the SQLite database, certificates and the
|
||||
@@ -22,7 +22,7 @@ docker volume create rustproxy-data
|
||||
docker run --rm -d --name rustproxy \
|
||||
-p 8080:8080 \
|
||||
-v rustproxy-data:/var/lib/rustproxy \
|
||||
rustproxy:0.28.0
|
||||
rustproxy:0.28.1
|
||||
|
||||
docker exec rustproxy rust-proxy admin setup-token
|
||||
```
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Stored log explorer
|
||||
|
||||
RustProxy 0.28.0 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent.
|
||||
RustProxy 0.28.1 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent.
|
||||
|
||||
## Separation from Live Logs
|
||||
|
||||
|
||||
+37
-7
@@ -1,4 +1,4 @@
|
||||
# Installing and updating RustProxy 0.28.0
|
||||
# Installing and updating RustProxy 0.28.1
|
||||
|
||||
The scripts prepare a Linux/systemd installation. This source archive is **not a
|
||||
verified production build**: Rust/Cargo, real CA operations, live systemd startup
|
||||
@@ -200,8 +200,36 @@ The provider script is embedded in the RustProxy binary; this command is diagnos
|
||||
|
||||
If another service already owns a certificate through the system Certbot, keep that lineage
|
||||
outside RustProxy's managed ACME profiles and create/use a **manual** RustProxy certificate id.
|
||||
A Certbot deploy hook can publish every successful renewal to all vhosts that already select
|
||||
that id:
|
||||
|
||||
The preferred 0.28.1 workflow is to select **Existing server files / external paths** and then
|
||||
**External files (absolute paths)** in the Certificates panel. For example:
|
||||
|
||||
```text
|
||||
Certificate: /etc/letsencrypt/live/example.com/fullchain.pem
|
||||
Private key: /etc/letsencrypt/live/example.com/privkey.pem
|
||||
```
|
||||
|
||||
RustProxy does not copy these files into its data directory. On every targeted reload it resolves
|
||||
Certbot's `live/` symlinks again, validates the new certificate/key pair and atomically publishes a
|
||||
new public TLS snapshot. A renewal wrapper can therefore call:
|
||||
|
||||
```bash
|
||||
curl --fail --show-error -X POST \
|
||||
-H 'Authorization: Bearer TOKEN' \
|
||||
-H 'X-RustProxy-Certificate-Key: certbot-main' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{}' \
|
||||
https://proxy-admin.example/api/certificates/shared-cert/reload
|
||||
```
|
||||
|
||||
The `rustproxy` service identity must be able to traverse the parent directories and read both
|
||||
files. An external private key may be `0600` when owned by that identity or `0640` with a
|
||||
restricted group readable by RustProxy; world access, group write and executable bits are
|
||||
rejected. These external files are not included in a RustProxy backup.
|
||||
|
||||
If you do not want the RustProxy service to read `/etc/letsencrypt`, keep the previous copy/deploy
|
||||
workflow instead. A Certbot deploy hook can publish every successful renewal into RustProxy's
|
||||
managed certificate directory:
|
||||
|
||||
```bash
|
||||
/opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy tls deploy --id shared-cert \
|
||||
@@ -209,10 +237,12 @@ that id:
|
||||
--key /etc/letsencrypt/live/example/privkey.pem
|
||||
```
|
||||
|
||||
The command reads the Certbot files locally and sends them to the authenticated loopback `POST /api/certificates/deploy` endpoint. The running RustProxy service writes the private generation with its own filesystem identity and hot-publishes it for new public TLS/H2/H3 handshakes. No per-vhost edit is needed. If another
|
||||
local process instead replaces the exact files already referenced by RustProxy, run
|
||||
`/opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy tls reload` (or authenticated `POST /api/certificates/reload`) afterward. If the replacement belongs to one configured manual certificate ID, automation may instead call `POST /api/certificates/{certificate_id}/reload`; the same endpoint accepts a scoped Certificate API Key ID + Bearer token and does not change the configuration revision.
|
||||
Administration TLS is startup-fixed and may still require a service restart.
|
||||
The deploy command reads the Certbot files locally and sends them to the authenticated loopback
|
||||
`POST /api/certificates/deploy` endpoint. The running RustProxy service writes a private managed
|
||||
generation and hot-publishes it for new public TLS/H2/H3 handshakes. Do not use `deploy` for a
|
||||
certificate ID configured with external absolute paths; renew those files in place and call the
|
||||
targeted reload endpoint instead. Administration TLS is startup-fixed and may still require a
|
||||
service restart.
|
||||
|
||||
Optional environment overrides go in a root-owned `/etc/rustproxy/environment`
|
||||
(mode 0600); create the directory when needed. Restart after changing service
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ This is the canonical index for the current project documentation. The authentic
|
||||
- [0.25.1 release audit](V0.25.1_AUDIT.md) - historical Traffic/module-split release scope and compiler-regression guards.
|
||||
- [0.25.0 specification audit](V0.25.0_AUDIT.md) - Web Server foundation point-by-point implementation status.
|
||||
- [0.25.0 pre-production security audit](V0.25.0_SECURITY_PREPROD_AUDIT.md) - threat-oriented source/configuration review, fixes made in-session and mandatory runtime security gates.
|
||||
- [0.28.1 implementation note](V0.28.1_IMPLEMENTATION.md) - external certificate paths, Certbot symlink reloads, backup semantics and certificate-source UX.
|
||||
- [0.28.0 implementation note](V0.28.0_IMPLEMENTATION.md) - manual/hot-reloaded GeoIP, listener status UX, cache Via/narrow invalidation and header-rule layout.
|
||||
- [0.27.3 implementation note](V0.27.3_IMPLEMENTATION.md) - HTTP/3 HEAD cache parity, COLD/rate-limit memory reduction, backup scope and package-path update.
|
||||
- [0.27.2 implementation note](V0.27.2_IMPLEMENTATION.md) - managed Certbot relocation/renewal repair.
|
||||
@@ -72,7 +73,7 @@ This is the canonical index for the current project documentation. The authentic
|
||||
|
||||
## Historical or version-labelled context
|
||||
|
||||
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.28.0_IMPLEMENTATION.md](V0.28.0_IMPLEMENTATION.md); [V0.27.3_IMPLEMENTATION.md](V0.27.3_IMPLEMENTATION.md) is retained as the prior HTTP/3/cache-maintenance milestone, [V0.27.2_IMPLEMENTATION.md](V0.27.2_IMPLEMENTATION.md) is retained as the prior managed-ACME repair, [V0.27.1_IMPLEMENTATION.md](V0.27.1_IMPLEMENTATION.md) as the prior managed-runtime/list-sorting milestone, and older version notes remain historical evidence. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain available for traceability.
|
||||
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.28.1_IMPLEMENTATION.md](V0.28.1_IMPLEMENTATION.md); [V0.28.0_IMPLEMENTATION.md](V0.28.0_IMPLEMENTATION.md) is retained as the prior GeoIP/listener/cache milestone, and [V0.27.3_IMPLEMENTATION.md](V0.27.3_IMPLEMENTATION.md) is retained as the prior HTTP/3/cache-maintenance milestone, [V0.27.2_IMPLEMENTATION.md](V0.27.2_IMPLEMENTATION.md) is retained as the prior managed-ACME repair, [V0.27.1_IMPLEMENTATION.md](V0.27.1_IMPLEMENTATION.md) as the prior managed-runtime/list-sorting milestone, and older version notes remain historical evidence. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain available for traceability.
|
||||
|
||||
Older context documents such as [INTEGRATIONS_040](INTEGRATIONS_040.md), [MANAGEMENT](MANAGEMENT.md), [SECURITY_AUDIT](SECURITY_AUDIT.md), [ACME_RFC2136](ACME_RFC2136.md) and older version audits remain available for traceability but should not override the current functional/technical documents above.
|
||||
|
||||
|
||||
+25
-4
@@ -79,7 +79,27 @@ migration of its stored paths. A system-wide timer should not also manage these
|
||||
RustProxy-isolated profiles.
|
||||
|
||||
For a certificate intentionally owned by a separate system Certbot instead of this managed
|
||||
renewal subsystem, use a manual certificate id and a Certbot deploy hook that runs, for example:
|
||||
renewal subsystem, 0.28.1 can reference the Certbot lineage directly. Configure a manual
|
||||
certificate id with both absolute paths, for example
|
||||
`/etc/letsencrypt/live/example.com/fullchain.pem` and
|
||||
`/etc/letsencrypt/live/example.com/privkey.pem`. RustProxy resolves the `live/` symlinks again on
|
||||
every targeted reload, so a renewal wrapper only needs to call:
|
||||
|
||||
```bash
|
||||
curl --fail --show-error -X POST \
|
||||
-H 'Authorization: Bearer TOKEN' \
|
||||
-H 'X-RustProxy-Certificate-Key: certbot-main' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{}' \
|
||||
https://proxy-admin.example/api/certificates/shared-cert/reload
|
||||
```
|
||||
|
||||
The service identity must be able to traverse the Certbot directories and read the files. External
|
||||
private keys may be owner-only (`0600`) or group-readable (`0640`) for a restricted group; they
|
||||
must not be executable, group-writable or world-accessible. The referenced files are not copied
|
||||
into RustProxy backups.
|
||||
|
||||
If direct read access to the Certbot tree is undesirable, use the copy/deploy workflow instead:
|
||||
|
||||
```bash
|
||||
/opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy tls deploy --id shared-cert \
|
||||
@@ -87,9 +107,10 @@ renewal subsystem, use a manual certificate id and a Certbot deploy hook that ru
|
||||
--key /etc/letsencrypt/live/example/privkey.pem
|
||||
```
|
||||
|
||||
The command resolves Certbot's `live/` symlinks locally, validates and stages a new pair, then
|
||||
publishes the existing id through the normal revisioned configuration path. Do not point this
|
||||
workflow at a certificate carrying RustProxy `issuer` metadata; one lineage must have one owner.
|
||||
That command resolves the Certbot `live/` symlinks locally, validates and stages a managed pair,
|
||||
then publishes the existing id. Do not use deploy for an id configured with external absolute
|
||||
paths, and do not point either external-manual workflow at a certificate carrying RustProxy
|
||||
`issuer` metadata; one lineage must have one owner.
|
||||
|
||||
Staging certificates are not trusted by browsers. A Dry run checks renewal
|
||||
validation but does not deploy the staging result. Only an explicit production
|
||||
|
||||
@@ -39,7 +39,7 @@ The encrypted backup contains the durable application state needed for recovery,
|
||||
- other durable RustProxy data accepted by the backup manifest,
|
||||
- validated internal relative symlinks, including Certbot `live/` links into the same profile's `archive/` tree.
|
||||
|
||||
It excludes rebuildable/runtime data such as cache, logs, temporary files, sockets, PID/lock files, SQLite WAL/SHM sidecars and the managed Certbot Python environment (`acme-python`; the `acme_python` spelling is excluded as well). Certificate/ACME durable state remains included; the virtual environment is rebuilt when needed. Symlinks are accepted only when their target is relative and resolves inside the staged RustProxy data tree; absolute, escaping or dangling links fail backup/restore. Restore recreates accepted links as links rather than dereferencing them.
|
||||
It excludes rebuildable/runtime data such as cache, logs, temporary files, sockets, PID/lock files, SQLite WAL/SHM sidecars and the managed Certbot Python environment (`acme-python`; the `acme_python` spelling is excluded as well). Certificate/ACME durable state inside the RustProxy data directory remains included; the virtual environment is rebuilt when needed. Manual certificates configured with absolute external paths (for example `/etc/letsencrypt/live/...`) are intentionally **not** copied into the backup: only the configured path remains in SQLite, so the external files or mounts must be restored separately. Symlinks inside the backed-up RustProxy data tree are accepted only when their target is relative and resolves inside that staged tree; absolute, escaping or dangling links fail backup/restore. Restore recreates accepted in-tree links as links rather than dereferencing them.
|
||||
|
||||
The outer container derives a backup key with Argon2id and encrypts authenticated chunks with AES-256-GCM. The backup password is independent from the local Secret Store key.
|
||||
|
||||
|
||||
+9
-11
@@ -1,16 +1,12 @@
|
||||
# Verification report - RustProxy 0.28.0
|
||||
# Verification report - RustProxy 0.28.1
|
||||
|
||||
## Scope
|
||||
|
||||
RustProxy 0.28.0 adds authenticated on-demand GeoIP refresh through the same staging/lock path as the automatic worker, replaces coupled release/checksum bases with direct per-database sources, makes publisher SHA-256 URLs optional, supports either separate IPv4/IPv6 CSV ranges or one native MaxMind DB (`.mmdb`), and verifies hot replacement of the shared `ArcSwap` database. It also improves active-listener presentation with running `H1`/`H2`/`H2C`/`H3` badges, reports the completed ACME Python/Certbot probe in the INFO log instead of printing speculative startup state, adds configurable cache `Via` metadata, narrows cache compatibility invalidation for bypass/response-only diagnostics, and fixes the vhost Security header-rule Delete/Operation layout. It retains the 0.27.3 restore/listener/WebSocket/update-check/automatic-GeoIP fixes.
|
||||
RustProxy 0.28.1 adds absolute external certificate/key paths for manual certificates while preserving the existing managed filename mode under `<data-dir>/certs`. The intended renewal path is Certbot/acme.sh updating `/etc/letsencrypt/live/...` followed by `POST /api/certificates/{certificate_id}/reload`. External paths are canonicalized on every load/reload so Certbot `live/` symlinks follow the current archive generation, then the final file is opened as a bounded regular file. Mixed managed/external pairs are rejected, certificate/key matching remains mandatory and managed private keys retain owner-only checks, while external keys may be `0640` for a restricted service group but cannot be group-writable, executable or world-accessible.
|
||||
|
||||
SQLite schema 8 and COLD cache format 3 remain unchanged. Older saved configurations remain compatible: legacy GeoIP `release_url`/`checksum_url` values are accepted and migrated into the direct-source model, while the old moving default checksum source is deliberately treated as unpinned and therefore optional. `geoip_updates` and the new `cache.via_header` are serde-defaulted when absent. The cache `Via` value defaults to `RustProxy`; `null` disables it.
|
||||
The targeted reload path remains revision-stable and republishes one immutable TLS snapshot. New public H1/H2/H3 handshakes use the refreshed material; invalid replacement files leave the previous snapshot active. The deploy endpoint refuses an ID configured with absolute external paths so a renewal wrapper cannot accidentally change storage ownership. Externally referenced files are excluded from RustProxy backups; only their configured paths survive, and the operator must restore or mount the external material separately.
|
||||
|
||||
The GeoIP request path reads `state.geoip.load()` when a frontend/location policy, country rate limit or country log capture needs a country. Manual and automatic updates both publish through the same `ArcSwap<Database>`. The regression test keeps an old `Arc`, publishes a replacement database and verifies that the old snapshot remains valid while subsequent slot loads return the new country mapping. CSV mode validates both IP families and ordering; MMDB mode validates MaxMind DB v2 structure and supports standard `country.iso_code`, `registered_country.iso_code`, sapics-style top-level `country_code`, and a two-letter root string used by simple custom databases.
|
||||
|
||||
When publisher checksum URLs are empty, downloads remain size-bounded and structurally validated. RustProxy computes the staged file's local SHA-256 and compares it with the installed file, so unchanged content is not republished. If an optional checksum URL is configured, the publisher digest is additionally enforced. A disabled MMDB downloader may use a locally managed `<data-dir>/geoip/country.mmdb`; the manual refresh action validates and hot-reloads that local file.
|
||||
|
||||
Cache compatibility hashing intentionally ignores `bypass_query`, `bypass_prefixes` and response-only cache diagnostic/Via fields. Those settings affect admission/wire behavior on subsequent requests but do not mutate a stored representation. Representation/origin-affecting cache, transform, header, forwarding and vhost changes remain in the compatibility identity.
|
||||
The certificate UI now distinguishes RustProxy-managed filenames from external absolute paths and provides explicit English/Polish storage guidance for ACME, Import and Existing-files modes. 0.28.1 retains all 0.28.0 GeoIP direct-source/MMDB/hot-swap, listener protocol-badge, ACME runtime-reporting, cache Via/narrow invalidation and Security-editor behavior. SQLite schema 8 and COLD cache format 3 remain unchanged.
|
||||
|
||||
## Verification status in this workspace
|
||||
|
||||
@@ -25,6 +21,7 @@ Rust/Cargo is not installed in this workspace. Rust-independent release checks c
|
||||
| Cache Via/narrow invalidation source contracts | PASS |
|
||||
| Listener protocol-badge/header-editor UI source contracts | PASS |
|
||||
| ACME startup reporting contract (no speculative banner state; final INFO event) | PASS |
|
||||
| External certificate path / Certbot-symlink / managed-vs-external source contracts | PASS |
|
||||
| Documentation and language-pack checks | PASS |
|
||||
| Web Server, security and dependency source contracts | PASS |
|
||||
| `tools/checks/check_source.py` | PASS (compiler/runtime explicitly not verified) |
|
||||
@@ -33,8 +30,9 @@ Rust/Cargo is not installed in this workspace. Rust-independent release checks c
|
||||
| Browser Web Service UI regression (`node tools/checks/web_service_ui_test.mjs`) | PASS |
|
||||
| Docker Agent (`go test ./...`) | PASS |
|
||||
| `./dev.sh test` static/pre-compiler stages | PASS; command then exits 127 at the Rust/Cargo gate |
|
||||
| Rust `cargo fmt/check/test`, linking and Rust unit tests (including synthetic standard/sapics/custom-MMDB lookup fixtures, GeoIP hot-swap, cache Via and cache-revision tests) | NOT VERIFIED - Cargo/Rust is not installed in this workspace |
|
||||
| Live manual/automatic GeoIP direct-source download and real-world MMDB lookup | NOT VERIFIED - requires network access and a compiled 0.28.0 binary |
|
||||
| Live HTTP/2 + HTTP/3 protocol tests | NOT VERIFIED - requires a compiled 0.28.0 binary and QUIC-capable runtime |
|
||||
| Rust `cargo fmt/check/test`, linking and Rust unit tests (including external-path/Certbot-symlink reload, synthetic standard/sapics/custom-MMDB lookup fixtures, GeoIP hot-swap, cache Via and cache-revision tests) | NOT VERIFIED - Cargo/Rust is not installed in this workspace |
|
||||
| Live manual/automatic GeoIP direct-source download and real-world MMDB lookup | NOT VERIFIED - requires network access and a compiled 0.28.1 binary |
|
||||
| Live external Certbot-path reload through `POST /api/certificates/{certificate_id}/reload` | NOT VERIFIED - requires a compiled 0.28.1 binary plus readable external certificate material |
|
||||
| Live HTTP/2 + HTTP/3 protocol tests | NOT VERIFIED - requires a compiled 0.28.1 binary and QUIC-capable runtime |
|
||||
|
||||
`./dev.sh test` is expected to exit with status 127 only when it reaches the Rust toolchain gate; all earlier release/static checks must complete successfully for this report to remain valid.
|
||||
+9
-4
@@ -96,9 +96,12 @@ Changing listener addresses or enabling a new listener requires a restart.
|
||||
`data/certs/default.pem` and `default.key` are created once when needed. Generated
|
||||
certificates are self-signed development material; they do not provide public trust.
|
||||
Explicit bindings use exact hostname names and already-present certificate/key files.
|
||||
The API stores filenames and names, never private key bytes. CLI generate/import
|
||||
refuses to overwrite named files. Input size, regular-file status, private-key
|
||||
permissions, matching keys, chain parsing and name coverage are validated.
|
||||
Manual entries support two addressing modes: simple filenames are loaded from
|
||||
`<data-dir>/certs`, while two absolute paths are read in place (for example Certbot
|
||||
`/etc/letsencrypt/live/<name>/fullchain.pem` plus `privkey.pem`). Mixed relative/absolute
|
||||
pairs are rejected. The API stores filenames/paths and names, never private key bytes.
|
||||
CLI generate/import refuses to overwrite named files. Input size, regular-file status,
|
||||
private-key permissions, matching keys, chain parsing and name coverage are validated.
|
||||
|
||||
```bash
|
||||
rust-proxy tls generate --name app --hosts app.localhost,alias.localhost
|
||||
@@ -127,7 +130,9 @@ handshakes use the refreshed snapshot, while established sessions/tunnels are no
|
||||
The administration TLS listener remains startup-fixed and reports that a service restart is
|
||||
required when its selected certificate changed.
|
||||
|
||||
For an externally managed/manual certificate whose configured files were replaced in place, `POST /api/certificates/{certificate_id}/reload` provides the same revision-stable hot-publication contract while targeting that certificate ID. The TLS resolver is immutable, so RustProxy validates and republishes a complete snapshot atomically; this avoids partially updated SNI state. Managed ACME certificate IDs are not accepted by this manual reload path.
|
||||
For an externally managed/manual certificate whose configured files were replaced in place, `POST /api/certificates/{certificate_id}/reload` provides the same revision-stable hot-publication contract while targeting that certificate ID. For absolute external paths RustProxy canonicalizes the path again on every reload, intentionally following Certbot `live/` symlinks to the current `archive/` generation before opening the final regular file. The TLS resolver is immutable, so RustProxy validates and republishes a complete snapshot atomically; this avoids partially updated SNI state. Managed ACME certificate IDs are not accepted by this manual reload path.
|
||||
|
||||
External absolute-path files are not copied into RustProxy full backups. Their path strings remain in configuration; restore or mount the certificate material separately on the destination host before starting TLS. The `rustproxy` service identity must be able to traverse every parent directory and read both files. External private keys may be owner-only (`0600`) or read-only for a restricted service group (`0640`); group-write, execute and any world permission are rejected.
|
||||
|
||||
## WebSocket behavior
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# RustProxy 0.28.1 implementation note
|
||||
|
||||
## Scope
|
||||
|
||||
0.28.1 adds first-class support for certificate/key pairs that are managed outside the RustProxy data directory, with Certbot's `/etc/letsencrypt/live/...` layout as the primary operator case. It retains the managed filename mode under `<data-dir>/certs`, all 0.28.0 GeoIP/listener/cache/UI changes, SQLite schema 8 and COLD cache format 3.
|
||||
|
||||
## Certificate file modes
|
||||
|
||||
Manual certificate entries use the existing `cert_file` and `key_file` fields. Their addressing mode is derived without adding a database/configuration schema field:
|
||||
|
||||
- two simple filenames, for example `app.pem` and `app.key`, are resolved inside `<data-dir>/certs`;
|
||||
- two absolute paths, for example `/etc/letsencrypt/live/example.com/fullchain.pem` and `/etc/letsencrypt/live/example.com/privkey.pem`, are read in place;
|
||||
- mixed relative/absolute pairs are rejected.
|
||||
|
||||
Managed filenames retain the existing traversal protection and `O_NOFOLLOW` open behavior. Absolute external paths are bounded to 4096 bytes. Each external path is canonicalized at load/reload time, which intentionally resolves Certbot `live/` symlinks to the current generation under `archive/`; the resolved final file is then opened with `O_NOFOLLOW`, must be a regular file and remains subject to the 128 KiB PEM bound. Managed private keys remain owner-only. External private keys may grant read-only group access (for example `0640`) so the unprivileged `rustproxy` service can use a restricted TLS group, but they may not be executable, group-writable or world-accessible; every parent directory must also be traversable by the service identity.
|
||||
|
||||
The certificate/key pair is still parsed by rustls and must match. Config publication remains atomic: invalid or unreadable replacement material leaves the previous immutable TLS snapshot active.
|
||||
|
||||
## Renewal wrapper workflow
|
||||
|
||||
For an externally managed certificate, renew the files using Certbot/acme.sh/another issuer and then call:
|
||||
|
||||
```text
|
||||
POST /api/certificates/{certificate_id}/reload
|
||||
```
|
||||
|
||||
The endpoint re-reads configured files and republishes the complete immutable TLS/routing snapshot without changing the saved configuration revision. New HTTP/1.1, HTTP/2 and HTTP/3 handshakes use the refreshed certificate; established TLS sessions are not renegotiated. A selected administration certificate remains startup-fixed and reports that process restart is required.
|
||||
|
||||
Scoped Certificate API credentials can call the targeted reload endpoint. `POST /api/certificates/deploy` remains a PEM-body deployment API and deliberately refuses to overwrite an ID that is configured with absolute external file paths; this prevents a renewal owner from silently switching storage models.
|
||||
|
||||
## Backups
|
||||
|
||||
RustProxy full backups contain managed certificate material under `<data-dir>/certs`. Absolute external certificate/key files are not copied into the backup. Their configured paths remain in SQLite/configuration, so the files or mounts must exist separately on the restored host before TLS can be compiled successfully.
|
||||
|
||||
## Administration UI
|
||||
|
||||
Certificates -> Generate/Add -> Existing server files now has an explicit file-location selector:
|
||||
|
||||
- **RustProxy certificate directory (`<data-dir>/certs`)** accepts filenames only;
|
||||
- **External files (absolute paths)** accepts complete server paths and shows a Certbot example.
|
||||
|
||||
English and Polish provider hints also explain that ACME modes generate/manage their own material, Import copies browser-selected PEM files into RustProxy storage, and Existing files references server-side material. Installed-certificate cards mark absolute-path entries as external.
|
||||
|
||||
## Compatibility
|
||||
|
||||
No migration is required from 0.28.0. Existing filename-only certificate entries keep their prior meaning. Existing deploy/upload/ACME flows continue to create RustProxy-managed filenames and never place PEM key bytes in configuration responses.
|
||||
Reference in new issue
Block a user