Skip to content

Releases: bpmforge/kryptkeeper

v0.9.4

Choose a tag to compare

@github-actions github-actions released this 15 Oct 18:25

Kryptkeeper v0.9.4

This release packages the agent for Windows and macOS. It also fixes the
agent and ACME problems that live validation of v0.9.3 found. The Windows
agent and the macOS install (Homebrew formula and LaunchDaemon) were
validated live on the release candidate assets before release.

Packaging

  • Windows agent zip and MSI. Each release now includes
    kryptkeeper-agent_<version>_windows_amd64.zip and a .msi. The MSI
    installs per machine and registers the KryptkeeperAgent service. Both
    files are listed in the signed checksums.txt. The MSI is not
    Authenticode-signed, so SmartScreen warns. Runbook:
    docs/ops/WINDOWS_AGENT.md.
  • Homebrew formula. Each release includes kryptkeeper.rb, with its own
    keyless cosign signature (.sig / .pem). The formula is generated from
    the signed checksums. It supports Apple Silicon only. There is no tap
    repository; packaging/README.md gives the local-tap install steps that
    Homebrew 7 requires.
  • macOS agent as a LaunchDaemon. See
    packaging/macos/com.kryptkeeper.agent.plist and the runbook
    docs/ops/MACOS_AGENT.md.

Agent fixes

  • Renewals run one at a time. The agent used to re-dispatch a pending
    renewal on every poll, so a second ACME order competed with the first for
    port 80. A failing renewal now backs off from 1 minute to 1 hour, with
    jitter, and the agent reports the error to the hub.
  • Port 80 is checked before ordering. The agent checks IPv4 and IPv6
    separately, so a listener on 0.0.0.0:80 is no longer missed on Windows.
    If the port is in use or not permitted, the agent says so instead of
    failing later with an unclear ACME error. The challenge server listens on
    both IPv4 and IPv6.
  • The Windows agent refuses install tasks up front. It no longer tries
    sudo; it reports the error to the hub. Use agentless WinRM/IIS on Windows
    in v0.9.

ACME fixes

  • dns-01 works where outbound port 53 is blocked. The propagation check
    is now configurable per connector with dns_propagation: auto
    (default), authoritative, resolver, doh or none. auto keeps the
    authoritative check and falls back to DNS-over-HTTPS, then to the
    resolver, when no nameserver answers. See docs/ops/ACME_UPSTREAM.md.
  • POST /api/v1/renewal/execute always answers. On a slow dns-01
    renewal it could return an empty reply. Its deadline now covers the
    longest propagation wait, and the response is always JSON.
  • Failed challenges say why. The hub's ACME server now includes the
    reason in a failed http-01 or dns-01 challenge's error, for example the
    URL and status. Before, clients showed authorization error for X: with
    nothing after the colon.

Upgrade notes

There are no database migrations and no breaking API changes. ACME
connector config_json gains the optional dns_* fields described above.
Existing ACME connectors use dns_propagation: auto. On networks where
port 53 works, auto behaves as before.

Install and upgrade

Replace the binary or image with 0.9.4 and restart. Docker Compose: set the
image tag to 0.9.4 (or pull 0.9), then run docker compose up -d.
Agents: install the new agent binary, zip or MSI, then restart the agent.

Verify the release

checksums.txt, kryptkeeper.rb and the image manifests are signed
keyless with cosign through the release workflow's GitHub OIDC identity.

V=0.9.4
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt.sig
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt.pem

cosign verify-blob \
  --certificate-identity "https://github.com/bpmforge/kryptkeeper/.github/workflows/release.yml@refs/tags/v$V" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --signature checksums.txt.sig --certificate checksums.txt.pem checksums.txt

sha256sum --ignore-missing -c checksums.txt   # after downloading an archive

cosign verify \
  --certificate-identity "https://github.com/bpmforge/kryptkeeper/.github/workflows/release.yml@refs/tags/v$V" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/bpmforge/kryptkeeper:$V

Full list of changes: CHANGELOG.

v0.9.4-rc.2

v0.9.4-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Oct 16:18

Kryptkeeper v0.9.4-rc.2

Release candidate for v0.9.4. It exists to prove the release pipeline's new
assets before the final tag: the Windows agent zip and MSI, and the signed
Homebrew formula asset. See CHANGELOG.md "[Unreleased]" for the changes.

Not for production use; v0.9.4 follows once these assets are validated.

v0.9.3

Choose a tag to compare

@github-actions github-actions released this 15 Oct 02:41

Kryptkeeper v0.9.3

Network discovery now does what the product promises: it finds every TLS
endpoint on the networks you scan, puts the certificates it finds into the
inventory, and tells you whose certificate each one is and what is wrong with
it. This release also includes the fixes prepared as v0.9.2, which was not
published separately.

Validated on a 17-host lab with targeted, full-range (/24) and scheduled
scans, on the default configuration, before release.

Discovery

  • Found certificates reach the inventory. Certificates discovered on the
    network appear in the certificate list without a restart, marked unmanaged
    (or managed, if this hub issued them). Before, they were counted on the
    dashboard but never listed.
  • Whose certificate is it. Every certificate a server presents is checked
    against this hub's CAs and the system root store and labelled managed,
    public, private_other (another private CA), self_signed or unknown.
    The dashboard shows a "Seen on the network by issuer" breakdown, and the
    Certificates and Discovery pages filter by issuer.
  • Chain problems. UNTRUSTED_ROOT when a chain ends in a root no one
    trusts; INCOMPLETE_CHAIN only when a server provably leaves out an
    intermediate (for example, a hub certificate served without its hub
    intermediate). Leaving out the root is normal and is not flagged. A missing
    intermediate from someone else's private CA cannot be proven without
    fetching it, so it is reported as unknown rather than guessed.
  • Per-endpoint results. Hostname mismatch and chain completeness are
    stored for each certificate on each endpoint, so a certificate served under
    two names keeps the right result for each, whatever order the scans run in.
  • Self-signed certificates made the usual way (openssl req -x509, which
    marks them as CAs) are now flagged when a server presents them.
  • Range scans no longer flag every certificate as a hostname mismatch
    because the target was an IP address.
  • Servers that answer but are not usable TLS are recorded instead of
    disappearing: NOT_TLS, TLS_HANDSHAKE_FAILED with the reason, and
    POSTGRES_TLS_DISABLED for a PostgreSQL port that declines TLS. A scan
    where some hosts failed ends completed_with_errors and says which.
  • Old protocols are detected. Servers that only offer TLS 1.0, TLS 1.1 or
    SSLv3 get DEPRECATED_TLS_VERSION; their certificate is captured where
    possible.
  • One rule for "risky". An endpoint is risky when its certificate scores
    70 or more, or is expired, self-signed, weak (key or signature) or chains to
    an untrusted root, or when the endpoint has a hostname mismatch, is not
    TLS, fails the TLS handshake, has PostgreSQL TLS disabled, or only offers
    deprecated TLS. The dashboard, the "Risky only" filter and the endpoint list
    all use it, and the Findings panel counts real findings.
  • The Discovery list shows posture inline: issuer badge, the top findings
    and a risky marker, and the protocol that was actually observed.

Other fixes (from v0.9.2)

  • The audit log shows what each record is about. GET /api/v1/audit adds
    resource_name (certificate common name, CA, template, approval, rule or
    scan target), resolved only for resources the caller may read. Stored
    records are unchanged. The CSV export gains a trailing resource_name
    column.
  • One rule for counting certificates. A certificate is active unless it
    is revoked or superseded by a renewal. Every page that shows a certificate
    total uses this rule; revoked certificates have their own dashboard figure.
  • Issue and renew responses include key_algorithm_display (for example
    ML-DSA-65).
  • UI: the certificate table scrolls instead of clipping; the PQC
    readiness card shows the parts that make up its percentage; expired
    certificates are no longer described as "expiring".

Behaviour changes to check before upgrading

  • Discovery defaults to the db provider (persisted, full-featured) when
    APP_DISCOVERY_PROVIDER is unset, and in the shipped env.example,
    docker-compose.yml and Helm values. scanner remains available as a
    minimal in-memory mode. Docker Compose users who kept the old default now
    get persisted discovery.
  • More endpoints are reported as risky: hostname mismatches, failed TLS
    handshakes and TLS-disabled PostgreSQL ports now count. Plain SMTP/IMAP
    ports that offer no STARTTLS are reported too.
  • Certificate totals exclude revoked and superseded certificates, so
    numbers may drop. The dashboard summary adds revoked. Per-CA counts now
    include certificates marked expired or reissue_required.
  • chain_complete / chain_trusted are omitted on discovered
    certificates in the certificate API: chain status is a per-endpoint fact
    and is reported on the endpoint's mapped certificates.
  • Scan jobs can end completed_with_errors; failed now means nothing
    was reachable. GET /api/v1/discovery/scan-jobs returns error.
  • The audit CSV export has one extra trailing column.

Database migrations 000077–000079 are added (trust class, scan status,
per-endpoint facts). They run automatically on start; take a backup first as
usual.

Install and upgrade

Replace the binary or image with 0.9.3 and restart. Docker Compose: set the
image tag to 0.9.3 (or pull 0.9), then docker compose up -d.

Verify the release

checksums.txt and the image manifests are signed keyless with cosign
through the release workflow's GitHub OIDC identity.

V=0.9.3
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt.sig
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt.pem

cosign verify-blob \
  --certificate-identity "https://github.com/bpmforge/kryptkeeper/.github/workflows/release.yml@refs/tags/v$V" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --signature checksums.txt.sig --certificate checksums.txt.pem checksums.txt

sha256sum --ignore-missing -c checksums.txt   # after downloading an archive

cosign verify \
  --certificate-identity "https://github.com/bpmforge/kryptkeeper/.github/workflows/release.yml@refs/tags/v$V" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/bpmforge/kryptkeeper:$V

Full list of changes: CHANGELOG.

v0.9.1

Choose a tag to compare

@github-actions github-actions released this 14 Oct 18:45

Kryptkeeper v0.9.1

A fix release for v0.9.0. Upgrade if you issue post-quantum (ML-DSA)
certificates or use the PQC reports: in v0.9.0, ML-DSA certificates were
signed but could not be stored on SQLite or PostgreSQL.

Fixed

  • ML-DSA certificates are stored. v0.9.0 signed the certificate and then
    answered 503 certificate was signed but could not be recorded, because
    Go reports an ML-DSA key as unknown, which the database rejects. Issue,
    renew, import, ACME and discovery now record ML-DSA.
  • Standard OpenSSL ML-DSA requests work. OpenSSL 3.5+ writes ML-DSA CSRs
    under the usual CERTIFICATE REQUEST label. The hub now picks the parser
    from the key's algorithm OID, so a plain openssl req CSR issues; the
    request's self-signature is still verified. See the README section
    "Issuing post-quantum (ML-DSA) certificates".
  • ML-DSA CAs can be created through the API and the New CA form
    (ML-DSA-44, ML-DSA-65, ML-DSA-87; software key backend only).
  • PQC numbers agree across pages. Crypto Posture, the PQC countdown and
    the PQC migration roadmap now share one definition.
  • Clearer errors on issue and renew. An invalid or unsupported CSR returns
    400, and an inactive or keyless issuing CA returns 409, instead of 503.
  • Templates show how many certificates they issued, and
    GET /api/v1/certificates?template_id= filters again.
  • KRYPTKEEPER_ORG_NAME sets the organization of a newly bootstrapped
    root CA.
  • A missing ./var directory is created at startup instead of causing
    503 on file-backed templates, CAs and certificates.
  • UI: Approvals, Revocation Activity, certificate detail and SPIFFE show
    CA and template names instead of IDs; certificate detail shows the ML-DSA
    parameter set (for example ML-DSA-65); internal ticket references are
    gone from on-screen text. The Audit page has a "Hide read-only events"
    toggle (off by default). Denied or failed reads are never hidden, and the
    CSV export always contains every event.

Behaviour changes to check before upgrading

  • quantum_safe_percent (Crypto Posture, GET /api/v1/reports/crypto-posture) now
    counts only certificates whose whole chain is post-quantum. An ML-DSA leaf
    under a classical CA is reported separately as quantum_safe_leaf_only.
    Dashboards that read quantum_safe_percent may show a lower number. The
    PQC migration roadmap response adds quantum_safe_leaf_only,
    not_applicable, pqc_score_percent and quantum_safe_percent.
  • Deleting a template that has issued certificates now returns 409. This
    guard was always intended but never fired.
  • Clients that retried on 503 from POST /certificates or .../renew
    should treat 400 and 409 as final.

No database migration is added; v0.9.0 schema 76 is unchanged.

Install and upgrade

Replace the binary or image with 0.9.1 and restart. Docker Compose:
set the image tag to 0.9.1 (or pull 0.9), then docker compose up -d.

Verify the release

checksums.txt and the image manifests are signed keyless with cosign
through the release workflow's GitHub OIDC identity.

V=0.9.1
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt.sig
curl -fsSLO https://github.com/bpmforge/kryptkeeper/releases/download/v$V/checksums.txt.pem

cosign verify-blob \
  --certificate-identity "https://github.com/bpmforge/kryptkeeper/.github/workflows/release.yml@refs/tags/v$V" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --signature checksums.txt.sig --certificate checksums.txt.pem checksums.txt

sha256sum --ignore-missing -c checksums.txt   # after downloading an archive

cosign verify \
  --certificate-identity "https://github.com/bpmforge/kryptkeeper/.github/workflows/release.yml@refs/tags/v$V" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/bpmforge/kryptkeeper:$V

Full list of changes: CHANGELOG.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 12 Oct 20:58

Kryptkeeper v0.9.0

Kryptkeeper is a self-hosted machine-identity and certificate-lifecycle
platform: a Go hub that runs your own CAs, issues certificates over ACME, EST,
SCEP, CMP and Microsoft autoenrollment (WSTEP/XCEP), publishes CRL and OCSP,
issues SSH certificates, discovers and inventories certificates across a
fleet, and renews and installs them without manual steps. A React dashboard
sits on top of the API. It runs from Docker Compose, Helm, Terraform or a
plain binary on SQLite or Postgres.

This is the first public release. Earlier versions (v1.0 to v2.2) were
internal milestones and were never published. Treat v0.9.0 as a release
candidate for 1.0: the core is validated against real clients (below), several
areas are labelled beta or experimental, and the version number says so.

Highlights

  • Issuance and CAs: root and intermediate CAs, templates with name,
    key-strength and SSH policy, CSR-in issuance, renew, revoke, import, fullchain
    download, PQC (ML-DSA, hybrid) certificates with software keys.
  • Protocols: ACME (http-01, dns-01 for wildcards), EST, SCEP with one-time
    challenges, CMP, WSTEP/XCEP for Windows GPO autoenrollment, CRL and OCSP.
  • Unattended renewal: the scheduler renews and installs on nginx, Apache or
    HAProxy over SSH, on hosts running the agent, and on F5, Citrix, Kubernetes
    and IIS targets. It verifies the served certificate, rolls back on failure,
    retries with backoff, and tells the owner when it gives up.
  • Delivery plugins: sandboxed WebAssembly plugins with signed manifests and
    trust tiers; F5 and Citrix ship as first-party plugins.
  • Discovery and inventory: network TLS scan (including ML-KEM detection),
    Kubernetes and service-mesh discovery, WinRM agentless discovery (SSH agentless certificate discovery is not wired in v0.9),
    read-only Cloud Keys inventory page.
  • Governance: append-only audit log with optional SIEM forwarding, approval
    queue with proposer and approver separated, key dual control on by default,
    custom roles, SSO (OIDC, SAML), SCIM, MCP server whose writes are
    proposal-only.
  • Postgres, fully tested: every database-backed store has a round-trip test
    on PostgreSQL (users and API keys, notifications, CA lifecycle and more), and
    any purely additive migration is tolerated automatically on upgrade.
  • Key ceremonies: root-CA ceremonies work on SQLite and PostgreSQL; a root
    stays pending until approved. A hub whose active CA has no key now refuses to
    start (KRYPTKEEPER_ALLOW_MISSING_CA_KEYS=true for recovery).
  • Notification rules: rules persist and fire under the default delivery
    provider, and are validated.
  • Complete audit trail: approval holds, dual-control proposals, skipped
    alerts and workload attestations are all stored (migration 000076), and
    the Audit page and GET /api/v1/audit?outcome= filter by every outcome.
    Notification providers that only record (file, db) skip alerts and audit
    them as skipped instead of reporting them sent.
  • Template validity: validity_period (P30D, P1Y, 72h, 90d) is
    honoured on issue and renew.
  • Security: a deep audit run on 2026-10-09 and the fixes for it (below).

What's validated

Results from 2026-10-11 on v0.9.0-rc.7 (main at 2f9e766) against real clients (full table
in docs/release/LIVE_RESULTS_v0.9.md).
Per-feature status for all 77 inventoried features is in
docs/release/FEATURE_VALIDATION_v0.9.md.

Suite Result Real peer
Smoke (tests/smoke) 331/331 hub binary, curl, local OIDC JWKS stub
ACME (tests/live/acme) 28/28 certbot, lego
Protocols (tests/live/protocols) all steps passed openssl EST, SCEP, CMP, OCSP, CRL
TLS (tests/live/tls) all steps passed globalsign/est over the hub's own TLS
SSH CA (tests/live/sshca) all steps passed OpenSSH sshd and client
Plugins (tests/live/plugins) all steps passed (27 checks) example plugin, mock appliance, cosign
Autopilot (tests/live/autopilot) all pass (29 checks) nginx hosts and the agent, unattended
Agent (tests/live/agent) 18 passed, 0 failed Linux container agent
SSH install (tests/live/ssh-install) all pass (nginx, apache, rollback) OpenSSH, nginx, apache

go test ./..., the same with a PostgreSQL 16 test database (-p 1), and make test-race each pass
on 50 packages. The upgrade test was re-run on rc.5 (nine scenarios on SQLite and PostgreSQL) and
spot-checked on rc.7 (SQLite rc.1 and rc.2 and PostgreSQL rc.4 to rc.7, schema 76, including audit rows with
the new outcomes being stored); see
UPGRADE_TEST_v0.9.md.

Older dated runs not repeated on 2026-10-11 (Kubernetes, service mesh, notifications,
PKCS#11 on SoftHSM2, Docker Compose, WinRM/IIS, WSTEP/XCEP on a Windows lab
with Kerberos) are recorded in docs/UAT_PLAN.md.

Beta, experimental, and not in v0.9

  • Beta (works; unit and integration tested only): reports, compliance
    evidence, CBOM and PQC readiness; rollout groups and PQC migration waves;
    SAML, OIDC and SCIM (tested against in-process IdPs, not Keycloak, Okta or
    Entra); CT monitoring; cloud inventory; ADCS sync; Terraform provider;
    air-gap bundle; MCP server; Helm chart (never install-tested on a cluster);
    the UI has no browser end-to-end tests.
  • Experimental: multi-tenancy (see Security); HashiCorp Vault and GCP Cloud
    KMS key stores (no tests).
  • Not verified on real hardware or accounts: the F5 and Citrix plugins
    (mock devices only), cloud KMS (emulators only), SCEP from network devices,
    the ACME upstream connector against Let's Encrypt staging (dns-01 via
    Cloudflare; the live run is still pending).
  • Not in v0.9: finding exceptions and risk acceptance (v0.10); issuer
    connectors other than internal and ACME (DigiCert, EST, SCEP and WSTEP
    upstream connectors).

Upgrading from internal builds

v0.9.0 turns on several secure defaults. Before starting a v0.9.0 hub, read
"Upgrade notes (breaking or behaviour changes)" in the
CHANGELOG. The ones that stop a hub or
lock out an operator most often:

  • Key export, backup and escrow need a second human to approve
    (KRYPTKEEPER_KEY_DUAL_CONTROL=false opts out).
  • AUTH_MODE=jwt and oidc bearer tokens are read-only until
    APP_AUTH_ROLE_CLAIM and APP_AUTH_ROLE_MAP_JSON map a role.
  • Secrets under 32 bytes, and OIDC mode without issuer and audience, are
    startup errors.
  • Docker Compose needs KRYPTKEEPER_KEYSTORE_PASSPHRASE, different from the
    bootstrap token.
  • SSH certificates need a template with an ssh_policy.
  • Behind a reverse proxy, set APP_TRUSTED_PROXY_CIDRS.
  • API keys minted through POST /api/v1/api-keys on earlier builds were lost at
    every restart; re-mint them once (CHANGELOG upgrade notes 56 and 57).
  • An active CA with no key in the keystore stops the hub (note 58); existing
    notification rules start alerting (note 59); templates with P1Y now issue
    1-year certificates (note 60).

Security

A deep audit of the whole repository (SAST, secrets in code and history,
dependencies, OWASP Web Top 10, OWASP LLM Top 10 for the MCP server, cloud,
IaC and CI, STRIDE threat model, 19 attack chains) ran on 2026-10-09 and
found 3 CRITICAL and 9 HIGH issues. A challenger review re-checked all 13
(none was wrong; three had their severity lowered). No live
secrets were found in code or git history, and no dependency is on the CISA KEV
list.

Everything CRITICAL and HIGH is fixed except tenant isolation. Fix groups FG-1
to FG-5 and FG-7 to FG-14 are merged (ACME issuance, key egress and separation
of duties, RBAC escalation, SSH CA policy, token hardening, login and session,
egress policy, issuance policy, plugins and MCP, cloud KMS, IaC and CI,
dependencies, documentation). Fixes ship with regression tests, and the live
smoke, ACME and SSH CA suites were re-run (results above).

Known limits you should plan around:

  • Multi-tenancy is experimental (FG-6, due in v0.10). Tenant isolation is
    not a security boundary: a tenant admin can act across tenants. The hub
    refuses MULTI_TENANCY unless KRYPTKEEPER_MULTI_TENANCY_EXPERIMENTAL=accept-risk
    is set. Do not use it to separate mutually untrusting parties. See
    docs/design/TENANCY_POSTURE.md.
  • Residual risks per OWASP category, such as in-memory refresh tokens and
    replay caches (a restart forgets them), a denylist rather than a sandbox for
    plugin remote_exec, and CMP using one shared secret per server, are listed
    in docs/security/OWASP_TRACKER.md and
    docs/security/final-report.md.

Reporting a vulnerability: do not open a public issue. Use GitHub's
private "Report a vulnerability" advisory on this repository, or email
security@kryptkeeper.io. Targets: acknowledgement in 48 hours, initial
assessment in 5 business days, coordinated disclosure within 90 days. Full
policy: SECURITY.md.

Install

git clone https://github.com/bpmforge/kryptkeeper.git
cd kryptkeeper
cp env.example .env     # set APP_BOOTSTRAP_TOKEN and KRYPTKEEPER_KEYSTORE_PASSPHRASE
docker compose up -d

Both values must differ; generate each with openssl rand -hex 32. The README
Quick Start conti...

Read more

v0.9.0-rc.9

v0.9.0-rc.9 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 12 Oct 20:04

Kryptkeeper v0.9.0-rc.9

Release-candidate verification run of the public release pipeline (build,
keyless signing, image push). The code is identical to v0.9.0; see
RELEASE_NOTES_v0.9.0.md for the full notes.