π Recent release history (last 2 versions)
[2.183] - 2026-07-03
Added
- Operator-configurable HSTS (Strict-Transport-Security) header β the HSTS policy was previously hardcoded to
max-age=31536000; includeSubDomainson every HTTPS response. Instances serving self-signed certificates during initial setup can now opt out entirely, drop theincludeSubDomainsdirective, or shortenmax-agefrom Settings β Security. The setting can also be forced viaUCM_HSTS_ENABLED,UCM_HSTS_INCLUDE_SUBDOMAINSandUCM_HSTS_MAX_AGEenvironment variables in/etc/ucm/ucm.env(takes precedence over the database); when set, the corresponding toggle in the UI is locked with a badge (#154). - mTLS PKCS#12 export from Account β after generating an mTLS certificate from the Account page, both PEM and PKCS#12 (.p12) download are now offered client-side, with password-protected PKCS#12 for browser or OS keychain import. The download endpoint enforces POST with a JSON body for PKCS#12 (password never sent via query string, avoiding leakage in proxy logs), minimum 8-character password, and returns the
AuthCertificateid usable by list/download/export (#156).
Fixed
- WebSocket handshake fails when if HTTPS_PORT was set to 443 -
CORS_ORIGINSalways appended:{HTTPS_PORT}to every allowed origin, so on port 443 the list contained entries such ashttps://ucm.example.com:443. Browsers omit the port number from theOriginheader when it is the scheme default so it would never match. As Socket.IO performs server-side checks onOrigin, WebSocket connections would be silently rejected.CORS_ORIGINSnow omits the port suffix whenHTTPS_PORTis 443 (#155). - SSH setup script command injection via hostname β the public
GET /ssh/setup/<refid>endpoint accepted arbitraryhostnamequery values that were embedded into generated shell/PowerShell scripts without validation. A payload such as$(id)or";id;"would execute when an operator piped the script to bash. Hostnames are now validated against^[a-zA-Z0-9._-]+$via a shared helper on both the public and authenticated setup routes, and the Windows script generator escapes single quotes (#157).
[2.182] - 2026-07-02
Added
- Per-CA ACME timing settings and robust DNS-01 TXT verification β each external ACME CA account now carries its own order poll timeout, poll interval and HTTP timeout (migration
048addsacme_client_accounts.order_poll_timeout_sec,order_poll_interval_sec,http_timeout_sec), so a slow authority no longer inherits the global hardcoded values. The DNS-01 challenge self-check now resolves the expected TXT record through the authoritative nameservers first, then public resolvers, with per-resolver diagnostic logging, and invalid ACME authorizations are detected during polling instead of stalling until timeout. The Gandi DNS provider was also hardened (URL-encoding of record values, post-create verification, missingAnyimport) (#150).
Fixed
- ACME auto-renewal no longer crashes when refreshing the order expiry β the renewal service read
new_cert.not_afterto copy the new certificate's expiry onto the order'sexpires_at, but theCertificatemodel exposes that date asvalid_to. TheAttributeErroraborted the renewal right after the new certificate had been issued and imported, soexpires_atwas never updated and the scheduler re-requested the same certificate on every tick. The renewal path now readsvalid_to, andexpires_atis updated correctly so a renewed certificate is not renewed again until its next due window.
Added
- Multi-CA management for the ACME client β UCM can now issue certificates from several external ACME authorities (Let's Encrypt, Actalis, ZeroSSL, Google Trust Services, HARICAβ¦) instead of a single one. ACME CA accounts are managed from the UI (CRUD, per-account External Account Binding, default selection, registration status), each certificate request picks its issuing CA, and the order is pinned to that account so renewals stay on the same authority. The
AcmeClientOrder.acme_client_account_idforeign key (migration047) links each order to its external CA account;AcmeClientService.for_issuance(environment, account_id=)resolves explicit account > configured custom directory > Let's Encrypt environment, andfor_order(order)reuses the pinned account on verify/finalize/status/renewal (#149).
Full history: CHANGELOG.md
Installation
Docker (Recommended)
# From Docker Hub
docker pull neyslim/ultimate-ca-manager:2.184-rc1
# Or from GitHub Container Registry
docker pull ghcr.io/neyslim/ultimate-ca-manager:2.184-rc1
# Run
docker run -d -p 8443:8443 \
-e SECRET_KEY=$(openssl rand -hex 32) \
--name ucm neyslim/ultimate-ca-manager:2.184-rc1Debian/Ubuntu
wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.184-rc1/ucm_2.184.rc1_all.deb
sudo dpkg -i ucm_2.184.rc1_all.deb
sudo apt-get install -fFedora/RHEL
wget https://github.com/NeySlim/ultimate-ca-manager/releases/download/v2.184-rc1/ucm-2.184.rc1-1.fc43.noarch.rpm
sudo dnf install ./ucm-2.184.rc1-1.fc43.noarch.rpmSilent/Automated Install
# Skip firewall prompts for CI/automation
sudo UCM_PORT=8443 UCM_FIREWALL=no dpkg -i ucm_2.184.rc1_all.debDefault Credentials
- Username:
admin - Password:
changeme123
Change the password immediately after first login!
Documentation
- Installation Guide
- API Documentation