Files
RustProxy/docs/V0.28.2_IMPLEMENTATION.md
T
2026-10-08 11:27:18 +02:00

46 lines
3.7 KiB
Markdown

# RustProxy 0.28.2 implementation note
## External certificate path hardening
0.28.2 keeps the 0.28.1 direct-file workflow for Certbot/acme.sh certificates but changes validation from an exact-mode expectation to a security-policy check. POSIX ACLs are supported: a private key may be owner-readable or group/ACL-readable by the `rustproxy` service identity. RustProxy rejects a private key that is world-readable and rejects group/world write access on every external certificate/key file. Execute bits alone are not treated as key disclosure.
For an absolute external path RustProxy validates the configured parent chain before resolving the final file, resolves the final Certbot-style `live/*.pem` symlink, validates the resolved parent chain again, and opens the final regular file with `O_NOFOLLOW`. On Unix, parent directories must be owned by root or the running RustProxy service user and must not be group/world-writable. Root-owned sticky ancestors such as `/tmp` remain accepted for controlled test/custom trees.
This closes the writable-parent/path-redirection gap while preserving Certbot renewal semantics: after `certbot renew`, a targeted reload follows the current `live/ -> archive/` generation and publishes a new immutable TLS snapshot only after the pair validates.
## ACL example
The packaged systemd unit keeps RustProxy writable only in its data/log directories and explicitly marks `/etc/letsencrypt` and `/etc/ssl` read-only when those paths exist:
```ini
ReadWritePaths=/var/lib/rustproxy /var/log/rustproxy
ReadOnlyPaths=-/etc/letsencrypt -/etc/ssl
```
`ReadOnlyPaths` does not grant Unix access. For traversal-only access to protected directories and read access to the actual PEM files, use narrow POSIX ACLs. Example:
```bash
setfacl -m u:rustproxy:--x /etc/ssl/
setfacl -m u:rustproxy:--x /etc/letsencrypt/ /etc/letsencrypt/live/ /etc/letsencrypt/archive/
setfacl -m u:rustproxy:--x /etc/letsencrypt/live/example.com/ /etc/letsencrypt/archive/example.com/
setfacl -m u:rustproxy:r-- /etc/letsencrypt/archive/example.com/fullchain*.pem /etc/letsencrypt/archive/example.com/privkey*.pem
setfacl -m d:u:rustproxy:r-- /etc/letsencrypt/archive/example.com/
```
A default ACL on the archive lineage is useful, but a renewer may recreate a private key as `0600` and mask inherited named-user access. A renewal hook should therefore reapply `u:rustproxy:r--` to the current resolved `fullchain.pem` and `privkey.pem` targets before calling the reload API. Do not grant `w` to the `rustproxy` identity on the Certbot tree.
## Least-privilege Certificate API
SQLite schema 9 adds `certificate_api_grants`. New Certificate API credentials require 1-32 explicit certificate IDs and are reload-only unless **Also allow deploy** is enabled. Token authentication is accepted only for targeted `POST /api/certificates/{certificate_id}/reload`; the global `POST /api/certificates/reload` remains administrator-session only. Deploy tokens must have both explicit deploy permission and an allowlisted request certificate ID.
Credentials created before 0.28.2 migrate to reload-only compatibility grants covering all certificate IDs (`*`). Rotate those credentials in Settings -> Certificate API to replace the broad compatibility grant with explicit IDs.
Normal administrator session + CSRF access remains unchanged. Certificate API tokens still cannot read configuration or call unrelated administration APIs, and non-loopback token use still requires HTTPS.
## Compatibility
- SQLite schema: 9 (additive migration from schema 8).
- Saved proxy configuration: unchanged from 0.28.1.
- COLD cache format: 3, unchanged.
- External certificate paths/backups: same semantics as 0.28.1; external PEM bytes are not copied into RustProxy backups.