8.0 KiB
0.7 update: configured managed certificates now have automatic renewal and validated hot deployment. Initial issuance/import remains explicit. Read 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 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.