What's Changed
Fixed
- TSA works again for existing installations — 2.200 required the timestamp signer to carry a critical, exclusive
timeStampingEKU, but the signer is the configured CA's own certificate, which carries no EKU at all: every deployment that timestamped before the upgrade got a 503 on all requests, and the product offered no way to mint a compliant signer. A CA certificate is accepted again (with a recommendation to use a dedicated TSA certificate), the newtsa_enabledgate no longer refuses installs that configured a TSA CA before the flag existed, and a client-pinnedreqPolicyis honored by issuing under that policy (RFC 3161 §2.4.1) instead of rejecting. - ACME issuance no longer fails closed on CAA DNS errors — 2.200 turned any CAA lookup failure (SERVFAIL, timeout, unreachable resolver) into a denial and terminally invalidated the order, which broke every renewal on air-gapped and split-horizon networks. DNS errors are non-blocking again by default (a warning is logged); strict fail-closed behaviour is available with the new "fail issuance on DNS lookup errors" toggle, and a transient DNS error never invalidates the order anymore. An RFC 8657
validationmethodsparameter is no longer enforced when the validation method is indeterminable (reused or auto-approved authorizations). - Renewals are no longer blocked by CA name constraints — 2.200 started validating renewals against the chain's NameConstraints, so certificates legitimately issued before enforcement (or before constraints were tightened) became un-renewable and fleets stranded at expiry; scheduled auto-renewal even swallowed the failure silently. Renewal-at-par now graces the names a certificate already carries (single, bulk, scheduled, SCEP and EST renewals) — only names new to the request are enforced — and auto-renewal failures are recorded in the audit trail. A CN that is not hostname-shaped (e.g. a person's name) is no longer treated as a DNS identity, and an unparseable NameConstraints extension no longer blocks all issuance from that CA.
- SCEP accepts legacy clients again — 2.200 required a 16-byte senderNonce and an in-window signingTime, and matched the RecipientInfo byte-for-byte against the CA certificate, which rejected fleets of already-enrolled devices (pre-RFC clients, devices without NTP, embedded stacks that re-encode the issuer). All three checks are tolerant again by default (warnings are logged); signingTime enforcement and its clock-skew window are configurable. GetCACert on an intermediate CA returns the single CA certificate again — the
application/x-x509-ca-ra-certchain response broke Apple/MDM enrollment (error -67731) — with the full-chain response available as an option. A renewal no longer silently strips EKUs the device's current certificate already carries. - EST accepts legacy integrations again — 2.200 returned 415 unless the request carried
Content-Type: application/pkcs10(breaking plain curl scripts) and refused PEM-encoded CSR bodies; both are accepted again with a deprecation warning. Re-enrollment no longer rejects a CSR whose SubjectAltName entries are reordered or differ only in criticality, and a CSR without SAN is issued as before. Clients that need the CA chain in the enroll response (instead of/cacerts) can opt in. - Syslog forwarding keeps line framing on upgrade — 2.200 silently switched TCP+TLS syslog to RFC 6587 octet-counting, corrupting the stream for collectors that only speak newline framing, with no way back from the UI. Existing configurations are pinned to line framing on upgrade (migration 066) and the framing is now an explicit setting.
- Revoked certificates can no longer answer
goodfrom the OCSP cache after upgrade — cache entries written before 2.200 (whose revocation-invalidation was broken) are purged once at upgrade (migration 066). - Wildcard ACME authorizations created before 2.200 are reusable again — they were stored in a format the new reuse lookup never matched, forcing a dns-01 re-validation on the first renewal (fatal with certbot --manual). Existing rows are normalized at upgrade (migration 067).
- UCM-managed ACME renewals recover when the upstream rejects
replaces— the RFC 9773 renewal hint is retried without the field on rejection instead of failing every renewal cycle until expiry. - A failed MS CA rekey submission no longer leaves the certificate paired with the wrong key — the freshly generated key/CSR are rolled back when the submission errors or is denied, so a PKCS#12 export cannot bundle a key that never got signed.
- Admin Sign-CSR can issue delegated OCSP responder and TSA certificates again — 2.200 silently stripped the
OCSPSigning/timeStampingEKUs on every path, including the operator explicitly signing a responder's CSR; the strip now applies only to protocol enrollees (ACME/EST), is logged, and can no longer be smuggled back in through the extra-EKUs merge. - OIDC provider settings are fully configurable in the UI — 2.200 enabled ID-token verification by default but the issuer, JWKS URI and verification toggle it relies on were never exposed, so existing OIDC providers failed closed with no way to fix them without manual API calls (#227).
Added
- Settings previously configurable only by direct database edits now have UI and API coverage: Certificate Transparency SCT embedding and the require-SCTs policy (with an updated default log list), OCSP response validity window, CAA issuer identifiers and enforcement mode, SCEP signingTime enforcement/clock skew/GetCACert chain, EST chain-in-response, syslog TLS verification and TCP framing, and the ACME client's TLS-ALPN-01 port. TLS-ALPN-01 (RFC 8737) is selectable when requesting certificates through the ACME client.
- Templates now govern the issued certificate's Key Usage and Extended Key Usage — issuing from a template previously ignored its configured KU/EKU entirely: the certificate type's built-in profile was applied instead and its EKUs were always merged in, so a template restricted to
OCSPSigningstill produced a certificate carryingserverAuth. When a template is selected itsextensions_templateis now the source of truth (extra EKUs are still added on top), and the certificate type selector in the issue form is disabled since the template defines it. Reported in #226. - Changing a template's type now updates its Key Usage / Extended Key Usage checkboxes — the type selector previously changed nothing in the form, so every new template started from the web-server defaults regardless of the chosen type. Reported in #226.
Added
customcertificate type at issuance — imposes no Extended Key Usage: only the EKUs you pick end up in the certificate, and with none selected the EKU extension is omitted entirely. This makes single-purpose certificates (e.g. an OCSP responder with onlyOCSPSigning) possible from the issue form. Requested in #226.- OCSP Signing system template (
ocsp_signingtype) — delegated OCSP responder profile (RFC 6960):digitalSignature+OCSPSigningonly, 90-day validity. Existing installations receive it automatically on upgrade.OCSPSigningis also selectable in the template editor's EKU checkboxes. Requested in #226.
📜 Recent release history (last 2 versions)
[2.202] - 2026-07-22
Fixed
- Security keys stopped working after upgrading to 2.200 — signature-counter clone detection began being enforced against a stored value that earlier releases had incremented on every login, including for the many authenticators (platform passkeys, and most FIDO2 keys) that always report a counter of zero. Those users could never present a value high enough and were locked out of their own key with
Authentication failed. Clone detection is now only applied when the authenticator actually maintains a counter, and the inflated stored values are reset once on upgrade (migration 065); a genuine counter that fails to increase is still refused. - Test runs no longer write to the production log — the suite inherited the native logging configuration and appended to
/var/log/ucm/ucm.log. On a host that also runs UCM this polluted real logs and, when the suite ran as a different user, took ownership of the file at rotation — after which the service could no longer write its own log at all. - Access log was never written — the WebSocket-capable Gunicorn worker bypassed Gunicorn's access logger entirely, so
/var/log/ucm/access.logstayed empty on every native install no matter how many requests were served. This made it impossible to tell whether a protocol client (SCEP/EST/ACME) had even reached the server when troubleshooting. Requests are logged again. - EST and TSA configuration refusals are now logged — same gap as SCEP: an unknown EST label, an unconfigured EST CA, a disabled TSA, and a TSA CA that is missing or has no certificate/key all returned an error to the client without recording anything. Contributed by @Hemsby (#224).
- SCEP configuration refusals are now logged — a request that reached the server and was turned away by a configuration check (CA offline, no private key, HSM-backed CA, no CA assigned) returned a 500 without recording anything, which was indistinguishable in the log from a request that never arrived. Each refusal now names its reason and the CA involved. Reported by @Hemsby.
- Inbound SCEP requests are now logged — only failures were recorded, so a silent log was ambiguous between "the device never reached us" and "it reached us and succeeded". Each request now logs its operation, HTTP method, source address and client User-Agent.
[2.201] - 2026-07-22
Security
- Group permissions are now enforced —
Group.permissionswas stored and editable but never consulted by authorization, so every grant made through a group was silently inert. A user's effective permissions are now their role's unioned with those of their groups, and the login response advertises the same set the API enforces. Groups can only grant permissions that are actually enforced (the previous list offeredread:certswhile endpoints requireread:certificates) and can never grantadmin:*or the*wildcard, so group membership is not a path to administrator. Permissions outside that set are ignored on read as well as on write, so a value introduced by an old backup or direct SQL stays inert. - SCEP challenge passwords now expire —
scep_challenge_validitywas validated and stored but never applied, leaving a leaked challenge usable indefinitely. The challenge generation time is recorded and enrollment is refused once the configured window has passed. An expired challenge is an explicit refusal rather than a fall-through to the weaker no-challenge path, and renewals keep working since they authenticate with the existing certificate. Challenges created before this release are adopted on first use rather than expired on the spot, so upgrading does not lock out a deployed fleet. The challenge API now reportsexpiredandexpires_at.
Added
- EST CA labels (RFC 7030 §3.2.2) — EST operations are now also served under
/.well-known/est/<label>/…, letting one deployment expose several CAs. Opt-in: map labels to CAs in theest_labelssystem setting (JSON, label → CA reference); with none configured only the existing unlabelled endpoints exist and behaviour is unchanged. An unknown label returns 404 and never falls back to the default CA, so a client is never enrolled against an authority it did not address. - RFC 7807 problem details on API errors — error responses now carry the standard
type,title,status,detailandinstancemembers and are served asapplication/problem+json. Not a breaking change: the historicalerror,messageandcodekeys are still present alongside them, so existing API clients and integrations keep working unchanged. Authentication, permission and unrouted-path errors follow the same shape as handler errors. - ACME certificate profiles (draft-ietf-acme-profiles) — the directory can advertise named issuance profiles under
meta.profiles, and clients select one with theprofilefield in newOrder; the selected profile drives the issued certificate's validity and signature digest, and is echoed on the order. Opt-in: configure theacme_profilessystem setting (a JSON map of name →description/validity_days/digest); with none configured the directory advertises no profiles and behaviour is unchanged. An unknown or malformed profile is rejected withinvalidProfile(migration 064).
Full history: CHANGELOG.md
Installation
Docker (Recommended)
# From Docker Hub
docker pull neyslim/ultimate-ca-manager:2.203
# Or from GitHub Container Registry
docker pull ghcr.io/neyslim/ultimate-ca-manager:2.203
# Run
docker run -d -p 8443:8443 \
-e SECRET_KEY=$(openssl rand -hex 32) \
--name ucm neyslim/ultimate-ca-manager:2.203Debian/Ubuntu
wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.203/ucm_2.203_all.deb
sudo dpkg -i ucm_2.203_all.deb
sudo apt-get install -fFedora/RHEL
wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.203/ucm-2.203-1.fc43.noarch.rpm
sudo dnf install ./ucm-2.203-1.fc43.noarch.rpmSilent/Automated Install
# Skip firewall prompts for CI/automation
sudo UCM_PORT=8443 UCM_FIREWALL=no dpkg -i ucm_2.203_all.debDefault Credentials
- Username:
admin - Password:
changeme123
Change the password immediately after first login!
Documentation
- Installation Guide
- API Documentation