feat: built-in ACME certificate management (DNS-01) - #205
Draft
nd4y wants to merge 1 commit into
Draft
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
This was referenced Aug 9, 2026
nd4y
force-pushed
the
feature/acme
branch
2 times, most recently
from
August 9, 2026 04:19
163c0e2 to
19c4e80
Compare
The panel issues TLS certificates for node inbounds itself, renews them on schedule and injects the material into a node's config as it is rendered, so a private key only ever reaches the nodes that serve its names - config profiles are shared, and writing PEM into one hands every key to every node using it. - credentials for eight native DNS providers (Cloudflare, deSEC, DigitalOcean, Gandi, Hetzner, Porkbun, PowerDNS, Vultr), CUSTOM for any HTTP DNS broker and MANUAL for dns-persist-01; the provider registry lives in the contract, so backend validation and the UI form come from one source of truth - orders against any RFC 8555 CA: staging-first defaults, EAB, wildcards, ECDSA and RSA key types - per-node delivery with the certificate fingerprint mixed into the config hash, so a renewal restarts exactly the affected nodes and profiles keep empty certificate arrays - renewal scheduler with backoff, parallel issuance, and a per-certificate event journal that surfaces failures in the UI - import for certificates the panel did not issue, replaceable in place - secrets encrypted at rest under ACME_SECRET_KEY, deliberately separate from APP_SECRET so rotating the login secret cannot orphan stored certificates Migrations only add acme_* tables; installations that never open the page are unaffected. Operator documentation is in docs/acme.md.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Discussion issue per CONTRIBUTING: #206
What this adds
Built-in ACME certificate management: the panel issues TLS certificates for node inbounds over DNS-01, renews them on schedule, and delivers the material to nodes without storing PEM in config profiles. Companion UI PR: remnawave/frontend (linked below).
CUSTOM(any HTTP DNS broker over an open four-endpoint protocol) andMANUAL(pairs with dns-persist-01). The provider registry lives in the contract, so the backend validates and the UI renders each provider's fields from one source of truth.tlsSettings.certificateswhen the node's config is rendered. Only nodes the certificate is bound to ever see the private key; profiles keep emptycertificatesarrays. The certificate fingerprint participates in the config hash, so a renewal restarts exactly the affected nodes.The problem it solves
Serving TLS on nodes today means an external agent (acme.sh/certbot plus glue scripts) that writes PEM into config profiles. That has three structural problems:
With this PR the panel owns the lifecycle end to end: issuance, renewal, delivery, restart of affected nodes, and an audit trail — with private keys scoped to the nodes that need them.
When it applies
CUSTOM: the real credential moves to a small broker with a domain allow-list, so an internet-facing panel holds a token that can only create ACME TXT records for permitted names, not edit whole zones.Configuration
Deployment: one new environment variable, generated by the bundled CLI. Everything else is runtime, via the UI. Migrations only add
acme_*tables.cli generate-acme-key # -> ACME_SECRET_KEY=<32 bytes, base64>It encrypts DNS credentials, ACME account keys and certificate private keys at rest, and is deliberately separate from
APP_SECRET: rotating the login secret must not make stored certificates unreadable.Cloudflare credential (UI: Credentials → Add credential): an API token with
Zone:Read+DNS:Editon the target zones. The Test action verifies the token and lists visible zones before the first order.CUSTOM credential: base URL + bearer token of any service implementing the broker protocol (
POST /v1/dns-01/present,POST /v1/dns-01/cleanup, optionalPUT /v1/persist,GET /v1/policy) — specified indocs/acme.md. A reference broker implementation is acme-proxy; its config for the typical "panel may only touch its own names" setup:Full operator documentation, including failure semantics and what the feature refuses to do, is in
docs/acme.md.Screenshots
Certificates, nodes-style rows — status, domains, remaining lifetime, DNS credential, node bindings:
Edit (staging-first defaults, EAB under the fold):
Details drawer with the event journal of a failing order:
Import of an externally issued certificate:
Credentials and the registry-driven provider form:
Notes for review
acme_*tables inert.