📜 Recent release history (last 2 versions)
[2.191] - 2026-07-10
⚠️ Upgrade note — LDAP/SSO certificate verification is now enforced. Before this
release, an LDAP/SSO provider with "verify SSL" enabled but no CA bundle uploaded
silently performed no certificate validation. It now validates the LDAP server
certificate against the system trust store. Action required: if your LDAP server
uses a private or self-signed certificate that is not in the host's system trust store,
upload its CA as the provider's CA bundle before upgrading, or those logins will start
failing with a TLS validation error. Providers with "verify SSL = off" are unaffected.
See #181.
Security
- LDAP TLS was not validated when "verify SSL" was on but no CA bundle was set — with SSL verification enabled but no CA bundle uploaded, the LDAP/SSO TLS builder fell back to ldap3's default TLS, which does not validate the server certificate, so a "verify SSL = on" provider silently performed no verification (MITM exposure on LDAP auth). It now validates against the system trust store (
CERT_REQUIRED). Note: verification is on by default — an LDAP server using a private/self-signed certificate that is not in the system trust store must now have its CA uploaded as the provider CA bundle (previously such setups connected without validation). Explicit "verify SSL = off" is unchanged. Contributed by @heidrickla (#181). - SSRF guard could be bypassed via unspecified and IPv4-mapped IPv6 addresses — the outbound-URL guard (webhooks, SSO discovery, ACME proxy) checked loopback but not the unspecified address (
0.0.0.0/::, which route to loopback on most systems), and matched the cloud-metadata deny-list by string, so an IPv4-mapped IPv6 encoding (e.g.::ffff:169.254.169.254) slipped past. The guard now collapses IPv4-mapped IPv6 to IPv4, compares against a parsed-IP deny-set, and treats unspecified addresses as forbidden. Contributed by @heidrickla (#182). - Outbound request hardening: default timeout + DNS-rebinding fix — the SSRF-pinned request helpers sent no default
timeout, so a stuck upstream could hang a worker indefinitely; they now default to 30s (overridable). The ACME proxy connection test validated the host and then fetched viaurllib.urlopen, which re-resolved the hostname independently (DNS-rebinding window); it now fetches through the pinned SSRF-safe helper so resolution, deny-list re-validation, and the pinned connection all use the same IP. Contributed by @heidrickla (#183).
Changed
- SCEP crypto migrated off the unmaintained pyCrypto to pyca/cryptography — the SCEP crypto helpers and message parser now use
cryptography's ciphers (AES-256-CBC, 3DES-CBC) instead ofpycryptodome, which is dropped as a dependency. The algorithms are standard so ciphertext is byte-identical and existing SCEP clients' messages keep decrypting unchanged. Contributed by @heidrickla (#184).
Fixed
- SCEP auto-approve enrollment crashed on a naive/aware datetime comparison — when clamping an issued certificate's validity to the CA's expiry, the code compared
utc_now()(naive-UTC) against the CA certificate's timezone-aware expiry, raisingcan't compare offset-naive and offset-aware datetimes. The SCEP handler masked it as a generic failure, so every auto-approved enrollment failed with "Internal SCEP processing error" (the renewal validity-window check had the same flaw). Both comparisons are now done consistently, with a regression test that issues against an online CA. Verified end-to-end with a real SCEP client (enrollment now returns SUCCESS). api.v2failed to import on non-POSIX platforms —api/v2/system/https.pydid an unconditional top-levelimport pwd(a Unix-only stdlib module), and sinceapi/v2/__init__.pyimports every submodule eagerly, the wholeapi.v2package (and the test suite) failed to import on Windows withModuleNotFoundError: No module named 'pwd'.pwdis now imported lazily inside the twochownsites and skipped gracefully off POSIX; Linux behaviour is unchanged. Contributed by @heidrickla (#179).- Removed unreachable dead code in the backup service —
BackupService._encrypt_private_keyhad areturnafter its realreturn, dead code that would have leaked the plaintext key if ever reached. Contributed by @heidrickla (#180).
[2.190] - 2026-07-10
Fixed
- API-key creation rejected valid permission resources — the permission validator in
POST /api/v2/account/apikeysused a hardcoded resource list that had drifted from the scopes actually enforced by the API: keys could not be scoped tocsrs,user_certificates,templates,truststore,est,hsm,ssh,policies,approvals,key_recovery,audit,groupsorsso(e.g.write:csrs, required to sign a CSR, was rejected — leaving CSR signing reachable only with a full-access*key). The valid set is now derived fromROLE_PERMISSIONSplus the admin-only resources (users,system,sso), with a regression test that scans every@require_authscope so the validator can no longer drift. Contributed by @heidrickla (#178).
Full history: CHANGELOG.md
Installation
Docker (Recommended)
# From Docker Hub
docker pull neyslim/ultimate-ca-manager:2.192-rc1
# Or from GitHub Container Registry
docker pull ghcr.io/neyslim/ultimate-ca-manager:2.192-rc1
# Run
docker run -d -p 8443:8443 \
-e SECRET_KEY=$(openssl rand -hex 32) \
--name ucm neyslim/ultimate-ca-manager:2.192-rc1Debian/Ubuntu
wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.192-rc1/ucm_2.192.rc1_all.deb
sudo dpkg -i ucm_2.192.rc1_all.deb
sudo apt-get install -fFedora/RHEL
wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.192-rc1/ucm-2.192.rc1-1.fc43.noarch.rpm
sudo dnf install ./ucm-2.192.rc1-1.fc43.noarch.rpmSilent/Automated Install
# Skip firewall prompts for CI/automation
sudo UCM_PORT=8443 UCM_FIREWALL=no dpkg -i ucm_2.192.rc1_all.debDefault Credentials
- Username:
admin - Password:
changeme123
Change the password immediately after first login!
Documentation
- Installation Guide
- API Documentation