Skip to content

Certificates

CyberMind-FR edited this page Oct 11, 2026 · 1 revision

Certificates — TLS certificates and their renewal 🟠

EN | FR | 🟠 AUTH | /certs/: what is served, until when, and who renews it

What it is for. The /certs/ page (package secubox-certs) lists the TLS certificates that HAProxy actually serves, shows how many days each has left, and lets an administrator issue, renew or revoke one. This page also explains the renewal chain, because a certificate can be renewed correctly and still never reach the front end.

  • Package: secubox-certs · page /certs/ · API /api/v1/certs/ · config /etc/secubox/certs.toml
  • Related: haproxyctl cert … (WAF describes the HAProxy → sbxwaf → nginx path), the certificate monitor of secubox-metrics.

Where certificates live

Place Content
/data/haproxy/certs/<domain>.pem what HAProxy serves: one concatenated file (chain + private key) per domain
/etc/letsencrypt/live/<domain>/ the certbot store (fullchain.pem, privkey.pem)
acme.sh home (~/.acme.sh/<domain>[_ecc]/) the acme.sh store (fullchain.cer, <domain>.key); EC certificates carry the _ecc suffix

The /certs/ page reads /data/haproxy/certs. Expected permissions on every served file: 0640, owner haproxy:secubox.

Why 0640 haproxy:secubox. The module runs as the secubox user. A .pem left 0600 root is unreadable for it: before the fix, 14 valid certificates showed up as expired.

From renewal to the front end

Renewing in a store does nothing by itself: HAProxy only reads its own directory. Two mechanisms carry the result, whichever tool issued the certificate.

Tool Renewal Carried to HAProxy by
certbot systemd timer, through secubox-certbot (starts certbot even when a pip drift breaks its Python stack) the deploy hook /etc/letsencrypt/renewal-hooks/deploy/secubox-haproxy, which runs haproxyctl cert sync after each successful renewal only
acme.sh its own daily cron (02:00) certsctl deploy --apply, run by a daily timer at 03:15 (Persistent=true, so a missed run is caught up)

Both obey the same safety rules:

  • Idempotent, and HAProxy is reloaded only if a file changed: a reload cuts live connections.
  • Content, not dates, decides: a recent mtime proves nothing.
  • Never shorten a served certificate's life: a .pem that expires later than the source is left alone (it may be a wildcard or a manual deposit).
  • Never replace by an older one, and never deploy when a date is unreadable.
  • Atomic write (temporary file then rename): a truncated .pem would make HAProxy fail at reload, for all vhosts.
  • When a domain exists in both stores, acme.sh wins (it manages most of the fleet).

certsctl deploy without --apply only simulates; certsctl status counts expired / expiring (21 days) / valid. haproxyctl cert sync can be run by hand at any time.

Reading the page

State Rule
healthy more than 30 days left
warning 8 to 30 days
critical 0 to 7 days
expired already past its end date
unknown the file cannot be read (❔, "illisible" in the page)

An unreadable certificate is "unknown", never "expired". Almost always it is a permissions problem (0600 root): run haproxyctl cert sync or fix the mode to 0640 haproxy:secubox. A read failure is not remembered, so the file becomes visible as soon as it is readable, and the bulk renewal skips unknown ones.

Actions (administrator): New Cert (a 3-step wizard with pre-flight checks — DNS resolution, port 80 — then issuance, HTTP or DNS challenge), Renew one certificate, Renew Expiring (all within 30 days), Revoke (revokes through certbot and deletes the .pem, then reloads HAProxy).

Method Path Guard Purpose
GET /list, /status, /details/{domain}, /metrics admin session inventory and detail
POST /check admin session pre-flight checks
POST /issue, /renew/{domain}, /renew-all administrator issue and renew
DELETE /revoke/{domain} administrator revoke and delete

The monitor (portal tile, dashboard)

secubox-metrics runs a certificate aggregator every minute (off the event loop) and publishes valid / expiring_soon / expiring_critical / expired with the next renewal. It reads /etc/letsencrypt/live/*/cert.pem (thresholds 30 and 7 days by default) — the certbot store, not the served .pem. A certificate managed only by acme.sh is therefore seen by the page /certs/, not by this aggregator. The Portal page shows its summary.

A domain is abandoned — what to do

An abandoned domain keeps showing as expiring then expired forever, and the renewal tools keep trying. Do it in this order, without revoking unless a key was compromised:

  1. Remove the vhost: haproxyctl vhost remove <name> (it regenerates the HAProxy configuration, so the certificate is no longer referenced).
  2. Set the .pem aside: move /data/haproxy/certs/<domain>.pem out of the directory (for example into a dated archive folder with 0600 rights) instead of deleting it.
  3. Stop the renewal: acme.sh --remove -d <domain> (add --ecc for an EC certificate). For a certbot-managed one, certbot delete --cert-name <domain>.
  4. Reload HAProxy and check that the page no longer lists the domain.

Skipping step 3 is the classic trap: the domain disappears from the front end and comes back at the next renewal run.


See also

SecuBox | FR | DE | 中文 | v3.0.0-alpha.12


Projet

Soutenir


🔴 BOOT — Démarrer

🟢 ROOT — Configuration

🟣 MIND — Modules

🔵 MESH — Référence

🟠 WALL — Matériel

🤖 Workflow Agents


Liens

Clone this wiki locally