v2.11.3 (Patch — reliability audit fixes)
Fixes from a findings-verification pass over DNS provider wiring, storage retry behaviour, notification delivery and backup security.
Fixes
- Unified backup restore silently skipped every certificate file — an off-by-one in the
certificates/prefix strip ([12:]instead of 13 chars) left a leading/on every entry path, which the ZIP-slip guard then rejected. Restores have been settings-only since v2.7.0; certificates now actually restore. Found by the new backup roundtrip tests. - Phantom
desecDNS provider removed; provider list unified —desecwas advertised byGET /api/web/certificates/dns-providersbut had no issuance strategy, no credential schema and was rejected by settings validation.DNSManager.SUPPORTED_PROVIDERSnow matches the canonical 25-provider set used by the strategy factory and settings validation (previously it listed only 13), and the sync is pinned by a wiring-consistency test. POST /api/web/certificates/test-provideralways returned HTTP 500 — it called aDNSManager.test_providermethod that was never implemented (AttributeError). Now performs an offline credential-shape validation against the provider's required fields.- Storage backend retries never actually retried —
@_with_retry()decorated methods that swallowed their own exceptions, so the decorator never saw a transient error (Azure Key Vault, AWS Secrets Manager, HashiCorp Vault), and Infisical had no retry wiring at all. The catch now lives outside the retry boundary; transient errors (429/5xx/timeouts) get up to 3 attempts on all four cloud backends, with Azure retrying per surface inbothmode. - SMTP notifications now retry and are logged — email sends get the same 3-attempt exponential backoff as webhooks (static config errors are not retried) and land in the delivery log (
/api/webhooks/deliveries) alongside webhook deliveries.
Security
- Backup encryption at rest (opt-in) — set
CERTMATE_BACKUP_PASSPHRASEand unified backups are written as.zip.enc(PBKDF2-SHA256, 600k iterations → Fernet) instead of cleartext zips that embed every certificate private key. Listing reads metadata from a cleartext header (no KDF cost); restore — including the corrupt-settings auto-recovery path — transparently handles both formats. Without the variable, behaviour is unchanged.
UI
- Setup wizard re-appeared on every login after "Skip wizard" — the skip is now persisted per browser; completing the wizard still clears it everywhere via
setup_completed.
Testing
- New suites: storage retry contract, backup encryption roundtrip, DNSManager↔factory wiring pin, SMTP retry/delivery-log coverage.
tests/conftest.pyacceptsCERTMATE_E2E_BASE_URLto run the e2e suite against an already-running instance when Docker isn't available.