github NeySlim/ultimate-ca-manager v2.233-rc2

pre-release3 hours ago
📜 Recent release history (last 2 versions)

[2.232] - 2026-09-21

Added

  • The server's own application log can be read from the interface, under System Logs: the source (application log, gunicorn access and error streams, the systemd journal where it exists), a subsystem, a level floor, a time window, a search and an exclusion, all applied on the server, with secrets redacted before anything leaves the process and a traceback kept as one entry. Reading is restricted to administrators and deliberately not audited, since the audit trail is written to that same log and the page polls (#361, by @stefanelul2000).
  • A guide to enrolling devices through Microsoft Intune's SCEP challenge validation, from the Entra app registration to the per-platform Intune profile, in docs/integrations/intune-scep.md (#360, by @stefanelul2000).

Changed

  • The CLA check counts a commit whose email is linked to no GitHub account as the pull request author's, who ticks the attestation line the new pull request template carries instead of signing under the commit's name.
  • The service runs with the systemd-journal supplementary group, granted in the unit file on DEB and RPM, so the diagnostic bundle and the log viewer can read the unit's journal (#361, by @stefanelul2000).
  • The Attach target dialog names the certificate it is attaching, so two certificates opened one after the other no longer look alike (#359, by @B0F1B0).
  • Attaching a deployment target now offers the Include Root CA box of the export dialog for the full chain file. New bindings ship the leaf and the intermediates only, which is what a TLS server should send; bindings created before this release keep the root as they always did, and the binding row says so (#357, by @B0F1B0).

Fixed

  • Redacting a private key from the logs could take tens of seconds on a log seeded with unclosed BEGIN markers, which anyone could plant through the SCEP User-Agent. The block is now found in one pass and the User-Agent is logged bounded.
  • Restoring a backup written before 2.232 gave every deployment binding the new default and dropped the root from its full chain. A column the archive does not name takes the value its version implied, so those bindings keep the root as they did.
  • In the log viewer, an end bound given to the minute excluded that minute but for its first second, and a copied line of the access, error or journal source was rewritten in the application log's shape. The end bound now closes its minute or day, and a copy is the line as the log wrote it.
  • In the log viewer, a slow answer could land over the filters chosen since it was asked, and ticked rows kept their tick across a change of data. Only the newest request fills the page now, and the tick is cleared with the data.
  • The CLA check failed on any pull request without a co-author trailer, accepted a signature line added by someone other than its signatory, and let an existing line be reworded; the v1.1 agreement also called itself v1.0 and pointed at the v1 signature file. All four corrected before the check runs for the first time.
  • The application log was written to a file nothing could read back: a container logged to standard output only, and a native install whose /var/log/ucm could not be opened fell back to stderr, so the diagnostic bundle shipped without it. One file is now chosen at startup, /var/log/ucm/ucm.log where it opens and DATA_DIR/ucm.log otherwise, with UCM_LOG_MAX_BYTES and UCM_LOG_BACKUPS for its rotation (#361, by @stefanelul2000).
  • Most of UCM's own secrets were not redacted from the diagnostic bundle: the pattern wanted a word boundary, which an underscore does not give, so client_secret=, challenge_password= and their kind went out in full, as did JSON passwords, cookies and the password in a URL. The names UCM's settings and provider credentials actually use are covered now, in query-string, JSON and quoted forms, and the log viewer shares the pass (#361, by @stefanelul2000).
  • A Windows device enrolling through Intune was refused at the certificate build once its request decrypted: the NDES client writes a BasicConstraints of CA:FALSE with a path length, a shape the parser rejects as a whole. The extensions of such a request are now read with that constraint taken as a plain end-entity one, which UCM never copied anyway; the same reading serves WSTEP and renewals (#228, reported by @stefanelul2000).
  • Deleting a CA that had served SCEP enrollments failed on PostgreSQL with a foreign-key error after its files were already removed, and left orphaned rows on SQLite. The enrollment history now goes with the authority, a CA still named by a SCEP profile, an ACME domain or an issuance policy is refused with 409 like one with certificates, and the files are removed only once the deletion is committed.
  • An iPhone enrolling over SCEP refused every certificate from a CA whose own certificate is shorter than the leaf: the reply carried the CA certificate next to the issued one, DER ordering put the CA first, and Apple's client pairs the first certificate with its key. The reply now carries the issued certificate only, which is all RFC 8894 requires (#228, reported by @stefanelul2000).
  • A certificate issued over SCEP through a profile's template was not counted against that template (#228).
  • The SCEP endpoint kept advertising its capabilities with SCEP switched off, and a switched-off or unconfigured endpoint answered 500 as if the server had crashed. Every operation now honours the switch, and a refusal comes back as 503 with its reason, or 404 for an unknown profile.
  • A Windows device enrolling through Intune was refused at the very last step with badMessageCheck: the Windows SCEP client wraps the session key with RSAES-OAEP, and UCM only unwrapped PKCS#1 v1.5. Both are accepted now, and the reply is wrapped the way the request was (#228, reported by @stefanelul2000).
  • A SCEP request reusing an already-issued transactionID with a different key was answered with the certificate of the first key, which the device cannot pair with its own. It is refused with badRequest now, and the log names the transaction (#228).
  • The Create CA wizard pre-ticked the serverAuth Extended Key Usage on every intermediate CA, so a CA created from the interface still restricted every chain beneath it to TLS servers and FreeRADIUS rejected the client certificates issued under it; v2.196 had removed that default on the API path only. The box now starts unticked (#228, reported by @stefanelul2000).
  • Three counters sat on zero whatever the data said: the number of users on the audit page, the certificates of each SSH CA, and the orders of each ACME account on the dashboard. Each tile read a field the API never sent, and each now reads a field it answers.
  • The buttons on an ACME domain row were cut off on anything but a very wide window, and on a tablet they were not drawn at all. They were an ordinary column, so the table sized them from a guess made on the name of the key and hid what did not fit, while the card view drops plain columns entirely. They are row actions now: a column of their own, sized by the buttons it holds, and a menu on the card. The card shows the approval state too, which it used to leave out (#355, reported by @dogarethebest).
  • That column was one pixel wide on every list, not just this one, because the data columns shared the whole width and left nothing for it. The buttons were drawn over the last column. Lists now reserve it, and the ACME domain columns, sized by the same guesswork, no longer cut their status badges either.
  • A list sits flush against the edges and a card view keeps its margin, rather than each tab deciding for itself.
  • The packages declared a licence the project does not have: the RPM named only BSD-3-Clause without the Commons Clause that goes with it, and the frontend package declared ISC, which it never was. The LICENSE file also called the result "open source", which a clause restricting sale is not.

[2.231] - 2026-09-15

Changed

  • A request the server honours on other terms than the ones asked for now says so. Shortening a validity was the silent case: a policy ceiling or the issuing CA's own expiry cut the requested duration, the answer was still a success, and the only trace was an expiry date nobody reads until it matters. Issuance, CSR signing and renewal return what was granted, what was asked for and which policy or which CA expiry decided, and the interface shows it next to the result.
  • Deleting a certificate that is still valid explains, before anything is deleted, that it has to be revoked first so the CRL and the OCSP responder carry the withdrawal, and offers to revoke and delete in one step. The refusal used to arrive after the fact, as a failure message, from both the list page and the detail window.

Fixed

  • Delivery history grew without end. Every webhook delivery, deployment delivery and notification log entry was written once and deleted by nothing: no retention rule, no scheduled task, and not even deleting the webhook it belonged to, which left its whole history behind pointing at an identifier that no longer named anything. On one instance three months of ordinary use had left 47 MiB of webhook payloads, about two thirds of the database, because a payload carries the certificate it describes. Finished rows are now removed past a window that an administrator can change, thirty days for the ones that were delivered and ninety for the ones that failed, since a failure is what gets opened when someone asks why a notification never arrived. A delivery still owed is never removed, whatever its age, and a window set to zero keeps that kind for ever.
  • An upgrade logged Found 1 pending migration(s) and then went quiet, and the lines naming the migration and confirming it appeared only at the next shutdown, attributed to the process that had already finished. The migration had run; the progress line carries no newline of its own, so an unflushed stream left the journal holding it. The runner now writes each line through (#350, reported by @JoseGoncalves).
  • A certificate asked for three years came back valid for ninety days. Five example issuance policies were seeded active and without a scope on installations upgraded from an older release, and their ceilings contradict each other, so once the rules started being applied at issuance the lowest of them capped every request. The examples an administrator never adapted are switched off; anything renamed, given a scope, rewritten or created by hand is left as it is.

Full history: CHANGELOG.md


Installation

Docker (Recommended)

# From Docker Hub
docker pull neyslim/ultimate-ca-manager:2.233-rc2

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

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

Debian/Ubuntu

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

Fedora/RHEL

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

Silent/Automated Install

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