github NeySlim/ultimate-ca-manager v2.184

latest releases: v2.235, v2.235-rc1, v2.234...
3 months ago

What's Changed

Added

  • Configurable RFC 5280 CA profile — the Create CA wizard exposes signature digest (Auto aligns P-384 to SHA-384 and P-521 to SHA-512, or explicit SHA-256/384/512) and an expandable Certificate Profile section with Key Usage and Extended Key Usage. Roots default to keyCertSign + cRLSign (no digitalSignature) and no EKU; issuing CAs add digitalSignature and optional serverAuth, matching common enterprise root/intermediate profiles (Let's Encrypt Root-YR / issuing CA style). Intermediate CA notAfter is clamped to the parent CA expiry, and GET /api/v2/cas/:id now returns the X.509 serial number (colon-separated hex) and SHA-1/SHA-256 thumbprints. The obsolete Create CA help step about optional template selection (templates removed in migration 017) has been removed (#160).
  • ACME external CSR and renewal key reuse — the ACME request form now offers a Key Source selector: Generate new key (default), Reuse key on renewal (preserves the same private key across renewals for DANE/TLSA and key pinning; first issuance generates a key, renewals reload it), or Provide external CSR (paste a PEM CSR; UCM submits it at ACME finalize, the private key never enters UCM). CSR domains are validated against the order identifiers (case-insensitive, RFC 4343). Auto-renewal honours the selected key source. Migration 049 adds key_source, csr_pem and source_certificate_id on acme_client_orders (#161).
  • ACME staging preflight dry-run — a Run Preflight action on the ACME request form validates domains, contact email, ACME account/EAB, CA connectivity and DNS-01 challenge setup against the Let's Encrypt staging directory without consuming production rate limits or changing the global environment. Two modes: Full (staging order + required DNS TXT records preview) and Validate only (config + connectivity). For manual DNS providers it displays the exact _acme-challenge TXT records to add, with optional DNS propagation verification. Emits an acme.preflight webhook event and an acme_preflight audit entry (#162).
  • Typed SAN validation for CSR creation — SAN entries are now validated per type (DNS, IP, Email, URI, UPN) on both backend and frontend, with clear cross-type errors (e.g. an FQDN entered in the IP field is rejected with a hint to use DNS). The frontend validation mirrors the backend rules so the client and server stay consistent (#167).
  • NIST P-521 EC curve option and normalized EC key labels — Generate CSR and Create CA now accept NIST P-256, P-384 and P-521 (ECDSA) key types through normalized labels (EC P-256, NIST P-384, secp256r1, prime256v1, …) that map to the OpenSSL curve names used internally. Previously EC P-256 failed because the UI label was stripped to P-256 and validated against OpenSSL curve names. The secp256k1 (Koblitz) curve remains intentionally unsupported (#167).
  • Hex serial number in certificate technical details — the certificate details view shows the serial as colon-separated uppercase hex (browser/OpenSSL display style) alongside the decimal form, copyable. Conversion handles decimal, 0x-prefixed and compact/colon hex inputs and supports serials larger than 159 bits via arbitrary-precision integer math.

Fixed

  • API key permission escalation — a non-admin user with permission to create API keys (POST /api/v2/account/apikeys) could mint a key carrying permissions their own role did not hold (e.g. a viewer granting admin:system). The create path now rejects any permission the creator does not have, with the same wildcard semantics as the auth checker. Sharing a wildcard key (*) requires the creator to be an admin (#163).
  • Forged mTLS enrollment via spoofed proxy headers — POST /api/v2/mtls/enroll honored X-SSL-Client-Verify, X-SSL-Client-S-DN and X-SSL-Client-Cert headers from any peer, so a caller who could reach gunicorn directly could forge a client certificate and enroll it as another user. The endpoint now gates on is_request_from_trusted_proxy() for those headers, matching the existing protection on the login_mtls() path (#163).

📜 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; includeSubDomains on every HTTPS response. Instances serving self-signed certificates during initial setup can now opt out entirely, drop the includeSubDomains directive, or shorten max-age from Settings → Security. The setting can also be forced via UCM_HSTS_ENABLED, UCM_HSTS_INCLUDE_SUBDOMAINS and UCM_HSTS_MAX_AGE environment 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 AuthCertificate id usable by list/download/export (#156).

Fixed

  • WebSocket handshake fails when if HTTPS_PORT was set to 443 - CORS_ORIGINS always appended :{HTTPS_PORT} to every allowed origin, so on port 443 the list contained entries such as https://ucm.example.com:443. Browsers omit the port number from the Origin header when it is the scheme default so it would never match. As Socket.IO performs server-side checks on Origin, WebSocket connections would be silently rejected. CORS_ORIGINS now omits the port suffix when HTTPS_PORT is 443 (#155).
  • SSH setup script command injection via hostname — the public GET /ssh/setup/<refid> endpoint accepted arbitrary hostname query 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 048 adds acme_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, missing Any import) (#150).

Fixed

  • ACME auto-renewal no longer crashes when refreshing the order expiry — the renewal service read new_cert.not_after to copy the new certificate's expiry onto the order's expires_at, but the Certificate model exposes that date as valid_to. The AttributeError aborted the renewal right after the new certificate had been issued and imported, so expires_at was never updated and the scheduler re-requested the same certificate on every tick. The renewal path now reads valid_to, and expires_at is 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_id foreign key (migration 047) links each order to its external CA account; AcmeClientService.for_issuance(environment, account_id=) resolves explicit account > configured custom directory > Let's Encrypt environment, and for_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

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

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

Debian/Ubuntu

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

Fedora/RHEL

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

Silent/Automated Install

# Skip firewall prompts for CI/automation
sudo UCM_PORT=8443 UCM_FIREWALL=no dpkg -i ucm_2.184_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.