Watch: issuing a certificate
A wildcard certificate over DNS validation, auto-renewal, and the local CA. The written steps are below.
Overview
WolfStack ships TLS that works out of the box on every install — no manual cert generation, no exposed plain-HTTP dashboards, and a one-click path to real Let’s Encrypt certificates including wildcards via DNS-01.
Three input modes:
- Auto self-signed (default). Generated on first start if you haven’t supplied anything. RSA-2048, 10-year validity, SAN covers your hostname plus
localhost/127.0.0.1/::1and every non-loopback IP the host has. Lives at/etc/wolfstack/tls/cert.pem. - Let’s Encrypt via certbot. HTTP-01 (webroot) or DNS-01 with 15 supported providers. Auto-renewed daily.
- Bring your own. Upload existing cert/key pairs or point WolfStack at any path on disk (cerbot, ACME.sh, internal CA, whatever).
Secure by default. Real certs in one click.
- HTTPS on every fresh install. Auto-generates a 10-year self-signed cert with SAN covering hostname, localhost, and every host IP. The dashboard is encrypted from boot.
- Guided HTTPS onboarding. Browser-by-browser instructions for accepting the self-signed cert (Chrome / Edge / Firefox / Safari) plus a one-click “Continue to HTTPS login” redirect after upgrades.
- Certbot DNS plugin install across every distro. Six package managers (apt / dnf / pacman / zypper / apk / pipx) plus snap as an EFF-recommended alternative where supported, with smart pre-flight, LXC/Docker awareness, and per-environment guidance.
Self-signed cert (default)
The first time you start WolfStack with no --tls-cert, no --tls-domain, and no Let’s Encrypt cert in a standard location, it generates one:
- Algorithm: RSA-2048 (universal browser compatibility).
- Validity: 10 years — no renewal cron needed for the lifetime of a typical deployment.
- Subject Alternative Names (SAN): includes your hostname,
*.<hostname>for FQDNs,localhost,127.0.0.1,::1, and every non-loopback IP on the host (auto-detected viaip -j addr show). - Storage: cert at
/etc/wolfstack/tls/cert.pem(0644), private key at/etc/wolfstack/tls/key.pem(0600). - Reuse logic: if the cert already exists, parses cleanly, and isn’t within 30 days of expiry, it’s used as-is. Delete the file to force regeneration.
Your browser will warn once per node about the self-signed CA — accept it (Advanced → Proceed) or import the cert into your trust store. Once you have a real domain pointed at the node, request a Let’s Encrypt cert via the Certificates page and the self-signed one is replaced automatically.
Let’s Encrypt via DNS-01 (wildcard-friendly)
Wildcard certs (*.example.com) require DNS-01 — the ACME challenge that certbot proves by writing a TXT record. WolfStack supports 15 DNS providers out of the box.
Issuing a certificate — step by step
- In Settings → DNS Providers, add your provider’s API credentials once (they’re stored obfuscated at rest and never returned to the browser).
- Open Certificates, click Request Certificate, and enter the domain — use
*.example.comfor a wildcard, or a plain hostname for a single site. - Pick the matching DNS provider and click Issue. WolfStack writes the TXT record, waits for it to propagate, completes the ACME challenge and installs the certificate — all without touching port 80.
- The certificate appears in the inventory with its expiry date. Auto-renewal is on by default; nothing else to do.
Supported providers
Credentials are XOR-obfuscated at rest in /etc/wolfstack/dns-providers.json and materialised to a 0600 INI file under /run/wolfstack/dns-creds/ only for the duration of the certbot call (RAII-cleaned via Rust’s Drop — even on error or panic).
Every provider has a “Test” button that runs a Let’s Encrypt staging-CA dry-run so you can validate credentials without burning your production rate limit.
One-click plugin install across every distro
The plain certbot package on most distros doesn’t bundle DNS plugins — each provider is a separate package. WolfStack’s Settings → DNS Providers page detects what’s missing on your node and offers a one-click install with the correct command for your distro.
| Distro | Package manager | Example (Cloudflare plugin) |
|---|---|---|
| Debian / Ubuntu | apt-get | sudo apt-get install -y python3-certbot-dns-cloudflare |
| Fedora / RHEL / Rocky / Alma | dnf | sudo dnf install -y python3-certbot-dns-cloudflare |
| Arch / Manjaro / CachyOS | pacman | sudo pacman -S --noconfirm --needed certbot-dns-cloudflare |
| openSUSE Leap | zypper | sudo zypper install -y python3-certbot-dns-cloudflare |
| openSUSE Tumbleweed | zypper | sudo zypper install -y python313-certbot-dns-cloudflare (auto-detected Python version) |
| Alpine Linux | apk | sudo apk add --no-cache certbot-dns-cloudflare |
| Any (community plugins) | pipx | pipx install certbot && pipx inject certbot certbot-dns-cloudflare |
Smart pre-flight
Before suggesting an install command, WolfStack pre-checks that the package actually exists in your host’s repos — one batched apt-cache show or dnf info covering all 15 plugins. If your distro dropped a package (Debian 13 trixie cut several DNS plugins; EPEL 9/10 omit google and gandi), WolfStack silently falls through to a pipx pipeline that includes the right uninstall-then-reinstall steps so the inject reaches the right venv.
The page also surfaces:
- Runtime badge — host / LXC / Docker / VM, so you see your environment at a glance.
- Cache staleness warning — if your apt/dnf cache is > 24h old, an amber banner tells you which command to run to refresh (so a stale “not available” doesn’t mislead you).
- Snap as an EFF-recommended alternative — offered only when snap is actually usable (snapd running, not in LXC/Docker where it’s broken).
- Container-specific notes — LXC operators get a heads-up about
CAP_NET_BIND_SERVICEfor HTTP-01 and the Proxmoxpct set <CTID> -features keyctl=1incantation; Docker operators get a reminder about Dockerfile persistence.
Discovery and inventory
The Certificates page scans every configured cert source on every node:
- Certbot live directory (
/etc/letsencrypt/live/) — renewals, expiry dates, included domains. - WolfStack’s own
/etc/wolfstack/tls/. - Proxmox
pveproxy-ssl.pem+ key pairs across all PVE nodes. - Any path you’ve uploaded a custom pair to.
Each cert shows expiry countdown, SAN list, signing chain, and origin (which node, which path). Sort by “expires soonest” to find your next renewal action.
Auto-renewal
A daily background task runs certbot renew --quiet with a deploy hook that reloads WolfProxy (and anything else you’ve told it to reload via reload_cmd in /etc/wolfstack/certbot.json). Renewals happen quietly in the background; you get an alert only if one fails.
Container & environment compatibility
WolfStack is tested and supported across:
- Bare-metal hosts — Debian, Ubuntu, Fedora, RHEL, Rocky, Alma, Arch, Manjaro, CachyOS, openSUSE Leap, openSUSE Tumbleweed, Alpine.
- LXC containers (including unprivileged Proxmox CT) — DNS-01 always works; HTTP-01 needs
CAP_NET_BIND_SERVICE. - Proxmox VMs and libvirt/KVM VMs — behave as normal hosts.
- Docker containers — install persists for container lifetime; bake into Dockerfile for permanence.
- systemd-nspawn — same guidance as LXC.
WolfStack auto-detects the container runtime via /proc/1/environ and surfaces environment-specific guidance in the install UI. snap suggestions are auto-disabled in LXC, Docker, and nspawn where snapd is broken.
Built-in local CA — TLS for internal domains
Public ACME cannot issue for a domain the internet cannot reach. If you run *.lab.lan, *.ai.home, or any other internal-only namespace, Let’s Encrypt is not an option — and the usual answer is standing up your own CA with openssl by hand, or bolting on a separate tool to do it.
WolfStack has one built in. Generate a root CA once, install it on your devices once, and every internal service can have a trusted certificate from then on.
- One root CA — 4096-bit, generated on the node, with the private key written atomically at
0600into a0700directory. The CA private key never leaves the host and is never returned by the API. - Leaf certificates signed by that CA — 825-day validity, SAN covering both the domain and its wildcard, marked for server authentication and chained to the root.
- Download the public root certificate from the Certificates page, with per-platform instructions for installing it into your device trust stores. Only the public certificate is ever downloadable.
- Verified two independent ways before shipping — both a programmatic X.509 store verification and the
openssl verifyCLI confirm the issued leaf chains correctly.
One-click publish
Issuing a certificate is only half the job — something still has to serve it. One-click publish does both: give it an internal domain and a backend address, and WolfStack issues the certificate and writes a managed reverse-proxy vhost through the WolfProxy nginx configurator — HTTPS, websocket-ready, with an HTTP→HTTPS redirect — then enables and reloads it so the service is actually live.
Backends are checked against a strict allowlist so a malformed or hostile value cannot inject nginx directives, and operator-supplied certificate and key paths are guarded against path traversal.
Certificate replication across the cluster
By default a certificate lives on the node that issued it. That is deliberate and it is the right default — key material should not be sprayed across a cluster without you asking for it. But it means a WolfProxy on node B looking for /etc/letsencrypt/live/<name>/fullchain.pem finds nothing.
Replication is the opt-in for when you do want it, enabled per certificate rather than by a global switch.
- One owner, many replicas. The node holding the certbot renewal configuration owns the certificate. Peers receive replicas written into the same
live/<name>/layout, so certificate listing, WolfProxy and WolfHost all work on peers with no changes. - Replicas cannot race the owner. A replica is delivered with no renewal configuration, and
certbot renewiterates renewal configs rather than thelive/directory — so a replica is inert by construction and two nodes can never both try to renew the same certificate. - Reconciliation catches every renewal path. A certificate might be renewed by WolfStack’s API, by its daily task, by the distribution’s own certbot timer, or by an operator at the shell — and most of those never tell WolfStack anything. Rather than listening for an event that may never fire, the owner reconciles every 15 minutes: fingerprint each replicated certificate, ask each peer which digest it holds, and push only what differs. Issue, renew and delete also trigger an immediate reconcile.
- Self-healing. Because reconciliation compares digests rather than tracking events, a rebuilt or restored peer simply receives what it is missing on the next pass.
- Peer endpoints are cluster-secret authenticated, and certificate names arriving over the network are validated against a strict allowlist plus a canonical-containment check before they are ever used as paths.
Steady state is cheap: one small request per peer that moves no key material unless a digest has actually changed.