Files
RustProxy/docs/ACME_PROVIDERS.md
T
2026-10-07 13:40:21 +02:00

140 lines
8.0 KiB
Markdown

> 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.
# Certificate providers
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.
## 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 |
|---|---|
| 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 |
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.
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.
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.
## HTTP-01 requirements and scope
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.
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.
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.
## DNS credentials and least privilege
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.
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.
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.
## API input
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`.
Provider-specific fields:
- `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`.
`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.
## Secret storage, renewal and limitations
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.
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.