v0.34.0_fina;

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-18 12:00:59 +02:00
1 parent 1ca019d849
commit c207e8a03f
69 files changed
+1496 -13838

No files matched your search

+41 -116
View File
@@ -1,139 +1,64 @@
> 0.7 update: configured managed certificates now have automatic renewal and
> validated hot deployment. Initial issuance/import remains explicit. Read
> [RENEWAL.md](RENEWAL.md) for defaults, retained profiles and opt-out.
# Certificates, ACME providers and renewal
# Certificate providers
Certificates are managed under **Certificates**. You can import a PEM pair, reference accessible external certificate files or request a managed ACME certificate. Issuing a certificate does **not** automatically assign it to a Vhost; choose the certificate in the intended frontend/Vhost afterwards.
Source implementation; real provider/DNS/CA issuance and Rust runtime have not been
verified in the packaging environment. Offline tests substitute Certbot, so they
cannot establish that a DNS token, zone policy or CA deployment works.
## Supported certificate methods
## Workflow
Open Certificates -> Add certificate. Choose one provider or import mode, enter
the domains and metadata, then request issuance. Production is selected by default
in the panel and still requires the separate explicit production confirmation. Staging
remains available for testing and its certificates are not browser-trusted.
After a successful job, import its validated pair into the library, then assign the
certificate to a vhost. No CA operation occurs on startup or merely opening a form.
| Selection | Mechanism and required configuration |
| Mode | Requirements |
|---|---|
| HTTP-01 / domain validation | Certbot webroot; domain names and a publicly reachable plaintext HTTP listener |
| Cloudflare | DNS-01; scoped API token; `certbot-dns-cloudflare` installed |
| OVH | DNS-01; `ovh-eu` or `ovh-ca`, application key/secret, consumer key; `certbot-dns-ovh` installed |
| RFC2136 | DNS-01; server/port, TSIG name/secret/algorithm; `certbot-dns-rfc2136` installed |
| PEM import | Upload fullchain/certificate and corresponding private key, then validate |
| Existing server files | Advanced: reference a PEM pair already in the certificate directory |
| **HTTP-01 (Let's Encrypt)** | DNS for every name and a public TCP port **80** reaching a plaintext RustProxy listener; HTTP challenge endpoint is handled by RustProxy. |
| **Cloudflare DNS-01** | A zone-limited `Zone:DNS:Edit` API token and functioning Certbot Cloudflare plugin. |
| **OVH DNS-01** | Correct regional API endpoint plus application key/secret and consumer key scoped to the DNS zone. |
| **RFC2136 DNS-01** | DNS server, TSIG name/secret/algorithm and an update policy restricted to the requested records. |
| **PEM import** | A matching certificate chain and private key, validated before activation. |
| **Existing local files** | Trusted absolute certificate/key paths accessible to the RustProxy service account. Suitable for a separately managed Certbot lineage. |
RustProxy now owns its preferred Certbot environment under `<data-dir>/acme-python`.
When it is missing, RustProxy creates a virtual environment and installs pinned Certbot
5.8.0 plus `certbot-dns-cloudflare`, `certbot-dns-ovh` and
`certbot-dns-rfc2136` 5.8.0 with pip. No distribution Python files are modified and
RustProxy never executes sudo. If the stdlib `venv` module is unavailable but system
pip works, `virtualenv` is bootstrapped only inside the RustProxy data directory.
HTTP-01 cannot create wildcard certificates; use DNS-01. A staging certificate is for testing and is not generally trusted by browsers. Production issuance requires explicit confirmation of the production operation and provider terms.
If local setup fails, a runnable system `certbot` from `PATH` is used as fallback. The
Certificates panel reports whether the managed environment, the system fallback, or no
ACME runtime is active. Systems without Python (for example some appliance layouts)
retain PEM import and existing-file certificate modes even though ACME issuance/renewal
is unavailable. `bash dev.sh acme --doctor` and `certbot plugins` inspect only the
current shell/system fallback; they do not validate provider credentials or DNS reachability.
## First issuance and assignment
The shipped OCI image contains Python, venv/pip and distribution Certbot packages as an
offline fallback. Its preferred managed environment is still created in the persisted
`/var/lib/rustproxy` data volume.
1. Open **Certificates → Add certificate** and choose the provider/import method.
2. Enter DNS names, a unique certificate ID, contact email where requested and provider-specific credentials or files.
3. For HTTP-01, confirm public DNS and HTTP port 80 reach RustProxy even when a separate public HTTPS port is active. For DNS-01, validate authoritative DNS provider and propagation permissions.
4. Run staging first when testing new credentials/DNS, then confirm production issuance deliberately.
5. Wait for the job result; inspect errors under Certificates and the service logs.
6. Assign the issued/imported certificate to the intended Vhost/listener and verify a real TLS handshake with the public hostname.
## HTTP-01 requirements and scope
The reserved HTTP-01 challenge path is available only during eligible issuance/renewal work. It is not a generic public file server and does not bypass authentication for unrelated URLs.
Public TCP port **80** for every requested name must reach an active plaintext
RustProxy listener. NAT from public 80 to a private/custom HTTP port is allowed;
setting only port 8080 in public DNS is not sufficient. Check all published A/AAAA
addresses and multi-node routing. TLS-only listeners are not enough. There is no
TLS-ALPN-01 mode. This implementation accepts DNS names, not IP certificates.
Wildcard certificates require one of the DNS-01 providers, not HTTP-01.
## Managed ACME runtime
During a running HTTP-01 job, RustProxy serves only
`/.well-known/acme-challenge/<token>` for that job's exact normalized hostnames.
Only GET/HEAD and bounded regular files from `certs/acme-http01` are accepted;
queries, traversal, unrelated names, HTTPS requests and inactive jobs cannot read
these files. The reserved path precedes application auth/cache/static pages so a
CA can validate ownership without logging in. This exception does not bypass
application authentication for any other path. Responses are no-store.
RustProxy prefers a **private Certbot/Python environment** under the application data directory (typically `/var/lib/rustproxy/acme-python`), with support for the Cloudflare, OVH and RFC2136 plugins. A working system Certbot may be used as fallback; systems without a usable runtime can still import or reference PEM files. The application does not run system-wide `sudo` to install plugins or modify distribution-owned Python packages.
A CDN or upstream proxy must forward this path and the correct Host without
rewriting it. There is no automatic forwarding configuration at your DNS/CDN vendor.
Expired challenge jobs do not leave an open generic file server.
Provider tokens and TSIG secrets are security credentials: use least privilege, protect files and the data directory, rotate compromised values and restrict backups. ACME DNS credentials may be stored in private provider/Certbot files; do not assume they are stored in the same way as RustProxy administration Secret Store records.
## DNS credentials and least privilege
## Automatic renewal
Cloudflare: use an API token with `Zone:DNS:Edit` restricted to the zones used for
issuance. The adapter intentionally does not accept the account-wide Global API Key.
Managed certificates have configurable automatic renewal. RustProxy runs the scheduler while the service is active; this is **not** a separate cron service. Certificate status and next checks are visible in Certificates. The scheduler uses spaced checks and retry/backoff on failure, without automatically forcing a CA reissue on every check.
OVH: use a consumer key allowing `GET /domain/zone/` and GET/PUT/POST/DELETE for
`/domain/zone/<YOUR_ZONE>/*`. Repeat only for required zones, not the whole account.
Choose the region matching the account. Consult the official plugin docs if API
policies change. A domain registered at OVH but DNS hosted elsewhere must use the
actual authoritative DNS provider; registration alone is not sufficient.
After successful renewal, RustProxy validates the new pair and publishes it to new TLS handshakes without interrupting existing connections. Invalid/rejected material should leave the previous active pair in place. Keep room for archived ACME generations and monitor expiry; this is not an independent external expiry-alert service. **Do not run an overlapping system Certbot timer** against the RustProxy-managed ACME profiles.
RFC2136: retain the TSIG-limited update policy described in ACME_RFC2136.md. DNS
provider propagation delay is configurable. The helper rejects malformed tokens,
unknown/cross-provider fields and INI injection. It does not test your DNS policy
in advance. Wildcard DNS-01 issuance does not add wildcard HOST routing: vhost
hostnames remain exact matches.
## Externally managed Certbot / PEM files
## API input
When another service owns the certificate, add a manual certificate with the actual certificate and key paths, for example:
Common fields: `provider`, `certificate_id`, `domains` (one or more DNS names), `email`,
`environment` (`staging`/`production`), `agree_tos`, `confirm_production`. RustProxy
does not impose its own domain-count ceiling; the selected CA/provider may still
apply its external certificate/SAN policy.
DNS providers accept `propagation_seconds`.
```text
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem
```
Provider-specific fields:
Keep the original Certbot renewal system and use the RustProxy certificate reload/deploy API or local CLI after rotation. RustProxy resolves relevant symlinks again during reload. The service account must have directory traverse and narrow read permissions to the **current** key file; `ReadOnlyPaths` alone does not grant filesystem permission. Renewal hooks may need to reapply ACLs to replaced target files.
- `http01`: none; wildcard domains are rejected.
- `cloudflare`: `api_token`.
- `ovh`: `endpoint`, `application_key`, `application_secret`, `consumer_key`.
- `rfc2136`: `dns_server`, `dns_port`, `key_name`, `key_secret`, `key_algorithm`, `sign_query`.
### Restricted certificate API
`POST /api/acme/issue` is authenticated and CSRF-protected; one job runs at a time.
`GET /api/acme` is safe status metadata only. Job status is process-local; account
and credential files persist. A completed certificate has optional issuer metadata
with provider/environment, not a copy of request credentials.
Create an appropriately scoped credential in **Settings → Certificate API**. Its allowed certificate IDs and action permissions control **reload** or **deploy**; it is not a general administration token. A caller supplies both a Bearer credential and its configured `X-RustProxy-Certificate-Key` ID. Use HTTPS outside local/trusted transport and never store the clear token in the repository. See [Administration API](API.md).
## Secret storage, renewal and limitations
## Problems to check
Provider secrets are sent to the private worker on stdin, not command arguments.
They are not stored in exported JSON, SQLite, localStorage or audit records.
Files are private (0600 credentials, 0700 directories); these permissions do NOT
encrypt them. The service user/root can read them. Certbot account/lineage state
and provider credential files persist for managed renewal under
`certs/acme/<environment>-<id>-<provider>`; RFC2136 keeps its old path without a
provider suffix. Issued PEM outputs use new immutable names before library import.
- **HTTP-01 validation fails:** public TCP 80, all A/AAAA DNS answers, Host forwarding and challenge path handling through any CDN/router.
- **DNS-01 fails:** authoritative DNS provider, correct zone, TSIG/API scope and propagation interval.
- **Certificate available but wrong site certificate displayed:** Vhost assignment, listener binding, hostname and restart/reload state.
- **Renewal stops:** Certbot runtime, private profile files, DNS secrets, clock skew, permissions and CA limits.
- **External file reload fails:** rotated symlink targets, certificate/key match, SAN coverage and read permissions.
Back up these private profiles separately while issuance is idle: the standard
SQLite/configured-PEM backup does not include every account/credential profile.
Do not put real credentials into shell history or source-control examples.
Restrict the admin interface and service environment. Rotate exposed API credentials.
Configured managed certificates in 0.7 renew through the shared RustProxy worker;
validated changed material is published automatically while failed checks preserve
the active generation. Use the certificate's toggle to disable scheduled renewal.
PEM-only imports remain manual. A separate system Certbot timer may own a **different,
manual RustProxy certificate id** and publish it with `tls deploy --id ...`; it must not
also own a RustProxy-managed ACME profile. There is no external expiry alert, revocation
or certificate garbage collector. Cancellation
kills the worker group; interrupted DNS cleanup can leave TXT records an operator
must remove. See [renewal and recovery](RENEWAL.md) and test CA staging first.
## Primary references
- HTTP-01/DNS-01 constraints: https://letsencrypt.org/docs/challenge-types/
- Certbot webroot and operational use: https://eff-certbot.readthedocs.io/en/latest/using.html
- Cloudflare plugin/API scope: https://certbot-dns-cloudflare.readthedocs.io/en/stable/
- OVH plugin/API scopes: https://certbot-dns-ovh.readthedocs.io/en/latest/
- RFC2136 plugin: https://certbot-dns-rfc2136.readthedocs.io/en/stable/
These document upstream requirements, not evidence that this RustProxy build has
passed live provider or CA acceptance.
Keep a private encrypted application backup and a separate copy of any external certificate/key material. See [Backup and recovery](BACKUP.md) and [Security](SECURITY.md).