github NeySlim/ultimate-ca-manager v2.188-rc1

latest releases: v2.237, v2.237-rc2, v2.237-rc1...
pre-release3 months ago
๐Ÿ“œ Recent release history (last 2 versions)

[2.187] - 2026-07-08

Added

  • Configurable public vhost for ACME directory URLs โ€” a dedicated public hostname/port (acme_public_vhost, acme_public_port in Settings โ†’ General) can now be advertised in the local ACME server and ACME proxy directory URLs (/acme/*, /acme/proxy/*), independent of the admin UI URL โ€” the split admin/ACME reverse-proxy topology (e.g. admin.ucm.example.com vs acme.ucm.example.com behind one wildcard certificate). JWS verification accepts both the advertised public origin and the inbound URL; without a configured vhost, behavior is unchanged (request host). Wildcard hostnames are rejected for the vhost value (TLS SAN concept, not an advertised URL), and an optional acme_public_tls_cert_id records which managed certificate to deploy on the ACME vhost (metadata only). Settings APIs expose acme_public_base_url / acme_proxy_public_base_url and the UI directory URLs follow the configured origin (#173, thanks @fredlubrano).

Changed

  • SQLAlchemy 2.0 Session.get โ€” all 353 Model.query.get() / query.get_or_404() call sites migrated to db.session.get() / db.get_or_404(), eliminating ~4500 LegacyAPIWarning per test run ahead of a future SQLAlchemy major bump.

Security

  • CSR extension policy on certificate issuance โ€” the shared CSR signer copied every requested extension into the issued certificate verbatim, so a crafted CSR carrying BasicConstraints CA:true (and keyCertSign) submitted through EST, SCEP or ACME enrollment could obtain a working subordinate CA โ€” a trust escalation from an enrollment endpoint. Leaf certificate types now force BasicConstraints(ca=False) and strip the CA-only key-usage bits; only the explicit intermediate-CA signing flow may assert CA powers. Regression tested.
  • SCEP unauthenticated auto-enrollment when no challenge is set โ€” per RFC 8894 ยง2.4 an omitted challengePassword allows unauthenticated authorisation, so with auto-approve enabled and no challenge configured any anonymous client on the public SCEP endpoint received a CA-signed certificate. Initial PKCSReq auto-issuance is now refused unless a challenge is configured (manual-approval mode and renewals are unaffected); UCM_SCEP_ALLOW_NO_CHALLENGE=1 opts back in for isolated deployments.
  • Key-strength floor on EST and SCEP enrollment โ€” both protocols now reject CSRs whose public key is below policy (RSA < 2048, non-NIST EC curves) instead of signing weak/exotic keys, matching the UI/API issuance floor (EST RFC 7030 ยง3.7 and SCEP defer key policy to the local CA).
  • EST request-body cap bypass via chunked encoding โ€” the body-size limit only inspected Content-Length, so a Transfer-Encoding: chunked request could stream an unbounded body into memory. The body is now read with a hard cap on the request stream regardless of framing.

Fixed

  • Broken settings restore endpoint โ€” POST /api/v2/settings/backup/restore passed a temp-file path where the service expects raw bytes, so it returned 500 on every call and left the uploaded (encrypted) backup in /tmp on each attempt. It now reads the upload as size-capped bytes in memory (no temp file) and restores correctly.
  • mTLS certificate import always failed โ€” POST /api/v2/mtls/enroll-import crashed on every valid PEM (undefined variable, then a str stored into the binary cert_pem column). The endpoint now imports and enrolls correctly; covered by a regression test.
  • Public ACME vhost hardening (post-#173 review) โ€” the local ACME server now accepts the JWS url on both the advertised public origin and the inbound request origin (the tolerance moved into verify_jws itself, shared with the proxy, so in-flight orders survive an acme_public_vhost change on /acme/* too); CAA enforcement and caaIdentities follow the configured public hostname instead of the inbound Host; the vhost is validated as a real FQDN (rejects .., leading/trailing hyphens, single labels); non-string vhost values and out-of-band garbage port rows return 400/defaults instead of 500; the public origin is memoized per request (one combined SystemConfig read instead of up to 10 per proxy request); the TLS certificate is picked from a dropdown of key-bearing certificates instead of a raw database id, and clearing it removes the config row; the CA Accounts panel shows the same public directory URLs as the other tabs.

[2.186] - 2026-07-06

Fixed

  • ACME proxy /directory on fresh install โ€” the legacy /acme/proxy/directory and /new-nonce endpoints returned a 500 (No external ACME CA account configured for the proxy) on a brand-new instance before any external CA account was added, because the proxy service resolved the upstream AcmeClientAccount eagerly in its constructor. Resolution is now lazy: only the upstream directory URL (config-derived, defaults to Let's Encrypt staging) is needed for /directory and /new-nonce, and the account row is looked up on the first key-bearing operation (new-account, new-order, signing), preserving the helpful configuration error for actual signing paths. This had broken the release smoke gate on a fresh Docker container.
  • ACME manual verify DNS wait and authorization poll cadence โ€” the synchronous manual-verify endpoint ran the DNS self-check up to the full configured dns_propagation_timeout (max 3600 s), risking client/proxy timeouts; it is now capped at 30 s (background auto-poll still honors the full timeout). The auto-poll loop also queried per-domain authorization status on every iteration, doubling CA traffic; it is now checked every max(poll_interval ร— 5, 15) s, with the order-status poll still catching terminal invalid states each interval.
  • Contact-less upstream account registration โ€” AcmeClientService.register_account now accepts email=None and omits the contact field (RFC 8555 makes it optional; Let's Encrypt accepts contact-less registrations), so the proxy no longer blocks issuance when the only available email has a non-public TLD (.lan/.local).
  • ACME preflight staging order cleanup on failure โ€” an ephemeral staging order created during a full preflight is now removed from the database even when a later stage of the preflight raises, instead of leaking an orphan row.
  • Key-reuse renewal fallback โ€” key_source=reuse now falls back to the original source_certificate_id when the order's own certificate_id is missing (e.g. an order whose import failed), so the key-reuse chain survives across renewals.

Full history: CHANGELOG.md


Installation

Docker (Recommended)

# From Docker Hub
docker pull neyslim/ultimate-ca-manager:2.188-rc1

# Or from GitHub Container Registry
docker pull ghcr.io/neyslim/ultimate-ca-manager:2.188-rc1

# Run
docker run -d -p 8443:8443 \
  -e SECRET_KEY=$(openssl rand -hex 32) \
  --name ucm neyslim/ultimate-ca-manager:2.188-rc1

Debian/Ubuntu

wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.188-rc1/ucm_2.188.rc1_all.deb
sudo dpkg -i ucm_2.188.rc1_all.deb
sudo apt-get install -f

Fedora/RHEL

wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.188-rc1/ucm-2.188.rc1-1.fc43.noarch.rpm
sudo dnf install ./ucm-2.188.rc1-1.fc43.noarch.rpm

Silent/Automated Install

# Skip firewall prompts for CI/automation
sudo UCM_PORT=8443 UCM_FIREWALL=no dpkg -i ucm_2.188.rc1_all.deb

Default Credentials

  • Username: admin
  • Password: changeme123

Change the password immediately after first login!

Documentation

Don't miss a new ultimate-ca-manager release

NewReleases is sending notifications on new releases.