Community contribution from @rocogamer (PR #156). Every cert issued by CertMate was previously hardcoded to RSA-2048 because CAManager.build_certbot_command never passed --key-type or --rsa-key-size to certbot.
What landed
- Global default under Settings -> General -> "Default Certificate Key":
default_key_type(rsa/ecdsa),default_key_size(2048/3072/4096),default_elliptic_curve(secp256r1/secp384r1). Mutually exclusive selectors in the UI. - Per-cert override under the create-cert form's "Advanced Options". Leaving it on "Use global default" sends no key fields and the backend inherits the configured default.
- Renewals preserve the original shape automatically - certbot persists the
--key-type/--rsa-key-size/--elliptic-curveflags into its ownrenewal/<domain>.confat create time, so no new bookkeeping in CertMate. - Backwards-compatible default: settings without these fields migrate to
rsa/2048/secp256r1at load time, so existing installs see no behaviour change after upgrade. validate_key_options()helper rejects contradictory shapes up-front with a 400: RSA + curve, ECDSA + size, unsupported sizes, unsupported curves.- Side-fix: the GET
/api/settingsmasking regex matched thekeysubstring indefault_key_type, returning'********'which matched no<option>in the UI dropdown. Fixed via an explicit_NON_SECRET_KEYSallowlist. - 24 unit tests added (
tests/test_key_options.py,tests/test_settings_masking_allowlist.py).
Verification
Pre-merge smoke gate against certmate.org with random subdomains:
- ECDSA +
secp384r1override: cert emitted,openssl x509 -textconfirmsid-ecPublicKey,NIST CURVE: P-384,ecdsa-with-SHA384. Force-renew preserves the shape end-to-end. - RSA +
key_size=3072override: cert emitted,Public-Key: (3072 bit),sha256WithRSAEncryption. Force-renew preserves the shape end-to-end. - Full test suite (docker + Playwright + Cloudflare real-cert lifecycle): 97 passed, 1 skipped, 2 pre-existing xfail.
Closes #156.