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 / ::1 and 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 via ip -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

  1. In Settings → DNS Providers, add your provider’s API credentials once (they’re stored obfuscated at rest and never returned to the browser).
  2. Open Certificates, click Request Certificate, and enter the domain — use *.example.com for a wildcard, or a plain hostname for a single site.
  3. 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.
  4. The certificate appears in the inventory with its expiry date. Auto-renewal is on by default; nothing else to do.

Supported providers

Cloudflare
AWS Route53
Google Cloud DNS
DigitalOcean
Linode
OVH
Gandi
GoDaddy
Hetzner DNS
Namecheap
Porkbun
Vultr
Njalla
DNSimple
Dynu
RFC2136 (BIND)

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 / Ubuntuapt-getsudo apt-get install -y python3-certbot-dns-cloudflare
Fedora / RHEL / Rocky / Almadnfsudo dnf install -y python3-certbot-dns-cloudflare
Arch / Manjaro / CachyOSpacmansudo pacman -S --noconfirm --needed certbot-dns-cloudflare
openSUSE Leapzyppersudo zypper install -y python3-certbot-dns-cloudflare
openSUSE Tumbleweedzyppersudo zypper install -y python313-certbot-dns-cloudflare (auto-detected Python version)
Alpine Linuxapksudo apk add --no-cache certbot-dns-cloudflare
Any (community plugins)pipxpipx 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_SERVICE for HTTP-01 and the Proxmox pct set <CTID> -features keyctl=1 incantation; 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 0600 into a 0700 directory. 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 verify CLI 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 renew iterates renewal configs rather than the live/ 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.

Esc