Skip to content

Releases

rIDM 0.4.1

Choose a tag to compare

@github-actions github-actions released this 19 Oct 21:47
696ae98

Changed

  • Every Rust dependency is on its latest stable release. Four RustSec advisories against rustls 0.23.45 (RUSTSEC-2026-0346 to 0349) are closed by 0.23.46; x509-parser 0.18, picky-krb 0.13, OpenTelemetry 0.33 (tracing-opentelemetry 0.34), utoipa 6 (utoipa-axum 0.3, utoipa-swagger-ui 10) and uuid 1.28 are the notable moves. testcontainers stays at 0.27 until testcontainers-modules follows. utoipa 6 no longer writes an empty description on a response, so every success response in the OpenAPI document now says OK, Created, Accepted or No content, and single-element oneOf wrappers around referenced schemas are gone; the document is otherwise the same. The three zip advisories (RUSTSEC-2026-0367 to 0369) are on the ignore list with a reason: zip is a build-time dependency of utoipa-swagger-ui that unpacks the crate's own vendored archive, never attacker-controlled input, and utoipa-swagger-ui 10 does not yet accept zip 9.
  • The ridm-auth crate is published to crates.io from this release on: ridm-auth = "0.4" replaces the git dependency in the quickstart, the crate's README and its Cargo.toml point at the docs and docs.rs, and the packaged crate carries the licence.

Artifacts

  • Image: ghcr.io/zerosandonesllc/ridm:0.4.1 (sha256:70276d965ddad151bd89e2a3dd8a6889da5ba5e3a6efe3ae87c5c92b0824ca25), linux/amd64 and linux/arm64
  • FIPS 140-3 image: ghcr.io/zerosandonesllc/ridm:0.4.1-fips (sha256:e39c42b5a691f73c4ab7a726c5d5448f656e8c37169382c254464106b5ee000c), linux/amd64 and linux/arm64 (what it is)
  • Helm chart: helm install ridm oci://ghcr.io/zerosandonesllc/charts/ridm --version 0.4.1
  • Static binaries for linux amd64 and arm64 below; verify them with SHA256SUMS and its Sigstore bundle.

Verifying signatures: https://zerosandonesllc.github.io/rIDM/docs/deploy/releases.html#verifying-a-release

rIDM 0.4.0

Choose a tag to compare

@github-actions github-actions released this 14 Oct 13:55
499f409

Added

  • The consoles restyled (docs). The admin console has a dark command bar (tenant pill, search with ⌘K, theme switch), a sidebar with live counts and a needs-attention bell, the page as a sheet with breadcrumbs, and user and client details in a floating panel that deep links open. The overview shows four tiles with sparklines and deltas against the previous window, a needs-attention list (locked users, open invitations, keys expiring within 14 days, dead webhook deliveries) and a recent-activity feed. The account console has a dark header with the tenant mark, a security score with the next step to take, every section as a card and parsed user-agent labels on sessions and trusted devices. Every list is on one data table (windowed where long) with one footer for Load more / Newer / Older, skeletons while loading and empty states, and key, client-secret and certificate actions go through a confirmation dialog with a toast on success. Three typefaces are self-hosted (Bricolage Grotesque, Hanken Grotesk, JetBrains Mono); the fonts and the token sheet are documented in design/README.md.
  • Tenant appearance (settings.branding, Settings → Appearance in the console): skin (signal-v2 default, signal, vault, registry: four looks as token sets, chosen per tenant), contrast_color next to primary_color (six presets or custom; the console shows the readability ratios), mode (system, light, dark) and allow_theme_switch, plus headline and tagline for the sign-in page. The API validates the colours as CSS colour literals (hex for the contrast colour) and refuses unknown skins and modes. The administered tenant's appearance is worn by the console; the login page preview on the Appearance page renders the real shell.
  • Sign-in pages: with the Signal v2 skin and a headline the page splits into a brand panel and the form; otherwise a single card. The footer carries the tenant links, Help, a language select and the theme switch (hidden when the tenant forbids switching; the tenant policy wins over the person's choice).
  • Server-side sort for users and clients: ?sort= and ?dir= on GET /admin/tenants/{slug}/users (created, username, email, status, last_login) and /clients (created, name, type), with a sort-aware keyset cursor, so a sorted table pages without offsets. Four migrations add the supporting indexes with CREATE INDEX CONCURRENTLY.
  • Bulk actions: enable, disable or delete a selection of users, and delete a selection of IP rules, with one confirmation and one summary toast.
  • Filters in the URL on the users, clients and audit pages, so a filtered view can be bookmarked and shared; the audit filters apply as you type.
  • GET /admin/tenants/{slug}/stats returns previous (the window before, for deltas), counts (users, groups, clients, webhooks, keys, identity providers), attention with attention_total, and recent (the eight newest audit events with actor and subject). The users list returns each row's second_step (passkey, totp, otp or none).
  • npm run screenshots in ui/ regenerates the site's screenshots with Playwright.
  • Valkey with a private CA and client-certificate authentication (#23): REDIS_TLS_CA_FILE verifies a rediss:// server against your own roots (outbound HTTPS keeps the system's), and REDIS_TLS_CERT_FILE + REDIS_TLS_KEY_FILE present a client certificate, which Valkey's tls-auth-clients-user CN maps onto an ACL user with no password. Single-server form only; per region as REDIS_TLS_*_<NAME>. The files are re-read whenever they change, so a renewal needs no restart. The Helm chart's redis.tls mounts the Secrets. The FIPS build asserts the connection's TLS configuration like every other.

Fixed

  • Card headers wrap instead of overflowing; the keys page fits a phone; a held modifier no longer starts a g shortcut sequence, and Escape cancels one; skip links on the auth and account shells; the console sign-in card no longer prefills the tenant with quotes and prefers the tenant named in the address.
  • Hints, rails and status colours meet 4.5:1 in every skin, light and dark; the a11y sweep runs every console page in each skin.

Upgrade notes

  • The four migrations only add indexes, so a 0.3.0 server keeps running beside 0.4.0 and a rollback needs no database change. The new settings.branding fields have defaults; an exported tenant document from 0.3.0 imports unchanged.

Artifacts

  • Image: ghcr.io/zerosandonesllc/ridm:0.4.0 (sha256:a84b3281980464c8f7f577f3a4e5bdaecc4d0b8a66420d127e2f91874cf5799e), linux/amd64 and linux/arm64
  • FIPS 140-3 image: ghcr.io/zerosandonesllc/ridm:0.4.0-fips (sha256:0daefd15bcb215474b578cb574b98acb4739b0212def919afeae37ab9fdac05c), linux/amd64 and linux/arm64 (what it is)
  • Helm chart: helm install ridm oci://ghcr.io/zerosandonesllc/charts/ridm --version 0.4.0
  • Static binaries for linux amd64 and arm64 below; verify them with SHA256SUMS and its Sigstore bundle.

Verifying signatures: https://zerosandonesllc.github.io/rIDM/docs/deploy/releases.html#verifying-a-release

rIDM 0.3.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 15:45
6a78345

Added

  • An optional FIPS 140-3 build (docs, #7). The fips cargo feature (off by default) and a new image, ghcr.io/zerosandonesllc/ridm:<version>-fips, built on Red Hat UBI 9 from api/Dockerfile.fips (no RHEL subscription needed). It runs every TLS connection on rustls' FIPS provider and the AWS-LC FIPS module. It refuses to start unless the module passes its self-test and the host is in FIPS mode; FIPS_ALLOW_NON_FIPS_HOST=true overrides the host check for development and CI only. Every feature of the standard build is included. The standard build and image are unchanged.
  • FIPS_TRANSITION (FIPS build only): reads the secrets at rest written before the move to AES-256-GCM, logging and counting each read (ridm_fips_non_approved_total), so an existing deployment can switch to the FIPS build and re-encrypt with rotate-master-key.
  • PBKDF2-HMAC-SHA512 passwords in the FIPS build (PBKDF2_ITERATIONS, default 210,000), with a 128-bit salt. Other formats are re-hashed at sign-in. Outside FIPS_TRANSITION, the FIPS build verifies and bulk-imports only PBKDF2 hashes. The standard build keeps argon2id and reads PBKDF2-SHA512 hashes, so a deployment can move between builds either way.
  • Kerberos: the RFC 8009 encryption types (aes128-cts-hmac-sha256-128, aes256-cts-hmac-sha384-192), MIT's and RHEL IdM's default, next to RFC 3962's AES types.
  • GET /admin/tenants/{slug}/users?password_algo=… lists the users whose password hash is in a given format.
  • Helm chart: FIPS and OpenShift deployments (#21). image.variant: fips selects the -fips image. database.tls and migrations.database.tls mount a client certificate (a kubernetes.io/tls Secret, one per database role) and a CA, and add sslmode, sslcert, sslkey and sslrootcert to an inline URL, so the pods and the migration Job authenticate to Postgres by certificate with no password. extraVolumes/extraVolumeMounts on the pods and migrations.extraVolumes/migrations.extraVolumeMounts on the Job. route.enabled renders an OpenShift Route in place of the Ingress. ci/fips-openshift-values.yaml is the worked example (UIDs left to the restricted SCC), and the chart smoke test now installs against a Postgres that accepts only hostssl … cert, with the FIPS image in CI.
  • Secrets at rest in AES-256-GCM, under a key derived per value with HKDF-SHA-256. Every server reads it. The FIPS build writes it now. The standard build keeps writing XChaCha20-Poly1305 for this release, so a rolling update or a rollback to 0.2.0 never meets a value it can't read, and switches in the next release. rotate-master-key --status counts the remaining XChaCha20 rows (legacy_cipher_rows).

Changed

  • Hashes, HMAC, random numbers, signing-key generation and TOTP all run on aws-lc-rs, in both builds. The algorithms and stored formats are unchanged: existing keys, TOTP enrolments and tokens keep working. rsa, p256, ed25519-dalek and totp-rs are no longer dependencies, and the RUSTSEC-2023-0071 (Marvin) exception is gone with rsa.
  • Postgres TLS uses aws-lc-rs instead of ring.
  • Imported PBKDF2 password hashes are verified with aws-lc-rs.
  • Passkeys are verified by rIDM's own WebAuthn relying party on aws-lc-rs, which replaces webauthn-rs. The options, the checks and the stored format are the same, so existing passkeys keep working and a release that still uses webauthn-rs reads the ones registered now. Challenges come from the module's DRBG. Attestation statements are no longer re-verified (rIDM requests and trusts none), so authenticators that send TPM attestation now register. rIDM no longer links OpenSSL, in either build, and the vendored-openssl feature is gone.
  • Kerberos crypto runs on aws-lc-rs, written in-house and checked against the RFC 3961, 3962 and 8009 vectors and a real MIT KDC. picky-krb is no longer a dependency of the server.
  • The time spent for an unknown user or a user without a password now matches a real verification under the configured parameters, in either build. It used to verify a fixed, cheaper argon2id hash.
  • Temporary passwords are drawn without modulo bias.

Fixed

  • The console overview's two-step adoption card described second factors as still to come; it now says what the number counts.

Artifacts

  • Image: ghcr.io/zerosandonesllc/ridm:0.3.0 (sha256:14400d2a9575dc48bbd4c5b3355a33cbea3ff08363657e0cd141ffffe83e9b3c), linux/amd64 and linux/arm64
  • FIPS 140-3 image: ghcr.io/zerosandonesllc/ridm:0.3.0-fips (sha256:dc436161b58e102bed4d34215e6d444ef536d888eb010649dc00f701ab1a01d4), linux/amd64 and linux/arm64 (what it is)
  • Helm chart: helm install ridm oci://ghcr.io/zerosandonesllc/charts/ridm --version 0.3.0
  • Static binaries for linux amd64 and arm64 below; verify them with SHA256SUMS and its Sigstore bundle.

Verifying signatures: https://zerosandonesllc.github.io/rIDM/docs/deploy/releases.html#verifying-a-release

Images

Multi-arch (linux/amd64, linux/arm64), signed with cosign 3 by this release workflow; see Releases and verification.

Image Digest
ghcr.io/zerosandonesllc/ridm:0.3.0 (also 0.3, latest) sha256:14400d2a9575dc48bbd4c5b3355a33cbea3ff08363657e0cd141ffffe83e9b3c
ghcr.io/zerosandonesllc/ridm:0.3.0-fips (FIPS 140-3 build) sha256:dc436161b58e102bed4d34215e6d444ef536d888eb010649dc00f701ab1a01d4
oci://ghcr.io/zerosandonesllc/charts/ridm:0.3.0 sha256:ed9a82c12bd9b268cc6ec374491448f1ebb675de1199b5ace93dd489d2413bf3

rIDM 0.2.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 21:53
ff72c80

The first published release. 0.1.0 was cut but never tagged or published, so this
release also contains everything its section of
CHANGELOG.md
lists; the notes below are what came after it.

Added

  • SAML 2.0 identity provider. Every tenant publishes IdP metadata at
    {issuer}/saml/metadata (entity ID: the issuer) and answers AuthnRequests
    over HTTP-Redirect and HTTP-POST at /saml/sso with a signed, optionally
    encrypted (AES-GCM or AES-CBC, RSA-OAEP) Response. A SAML application is
    registered as a service provider — a client of the new type saml, with
    saml_service_providers holding its entity ID, consumer URLs, logout URL,
    NameID format (pairwise persistent, transient, email or user id), attribute
    names, certificates and signing options — from its metadata or by hand, in
    the console's new SAML page or at /admin/tenants/{slug}/saml/....
    Requests are admitted only from registered SPs, to registered consumer URLs,
    once, within ten minutes, with signatures checked against the SP's
    certificates (require_signed_requests to insist). The sign-in itself is
    the OIDC one: second factors (REFEDS MFA and Microsoft multipleauthn
    context classes are step-up requests), risk policy, consent and
    organizations apply unchanged. IdP-initiated sign-in (/saml/init?sp=) for
    SPs that opt in. Front-channel Single Logout: an SP's LogoutRequest ends
    the session and walks the browser through the session's other SAML SPs
    before answering, and RP-initiated logouts walk them too. The SAML signing
    keys are separate from the JWT keys and rotate only by hand (pending,
    published first → active → delete). SPs travel in the tenant document under
    saml_service_providers. New events saml_key.created and
    saml_key.status_changed. The XML signature, canonicalization and
    encryption code is rIDM's own (new dependencies roxmltree, rcgen,
    x509-parser, flate2; no C XML library); the parser refuses DTDs,
    signatures are accepted only over the element read, and it is checked
    against xmlsec1 in both directions (CI installs it) and fuzzed
    (saml_message). Both SAML roles are tested against Keycloak 26 on every
    build, each side configured from the other's metadata: rIDM as the IdP of a
    Keycloak realm and a Keycloak realm as rIDM's upstream (sign-in, encrypted
    assertions, tampered and replayed messages, Single Logout from either side,
    a key rollover picked up by a metadata refresh).
  • Organizations within a tenant: membership (a user may belong to several, one
    of them primary), role grants scoped to an organization, email domains
    verified by DNS TXT record with auto-join, an organization step in the login
    flow, and an org_id claim in access and ID tokens taken from the session, so
    one user can act in different organizations in different sessions. New
    ridm:orgs:read and ridm:orgs:write permissions (user managers hold both),
    a console page, and a read-only list in the account console. A tenant with no
    organizations is unaffected, and no token gains a claim.
  • Organization administrators: a role granted inside an organization now
    reaches the admin API for that organization alone — its record, members,
    domains, invitations and internal role grants — and never satisfies a
    tenant-wide permission check. A sixth built-in role, ridm:org-admin, is
    seeded in every tenant for it. Creating, deleting and listing organizations,
    changing a slug or status, and adding an existing user as a member stay
    tenant-wide; an organization's own administrator adds people through new
    org-scoped invitation routes, and GET …/{org}/grantable-roles says which
    roles they may grant there. GET /admin/me gained organization and
    organization_permissions, the console opens such an administrator on their
    own organization, and the MFA-for-administrators policy counts them.
  • Risk-based adaptive authentication (settings.risk, off by default): four
    signals — new device, new country, impossible travel and failure velocity —
    each with a tenant-set weight, scored against two thresholds. step_up_at
    demands a second factor for that sign-in whatever the MFA policy says, and
    not even a trusted device waives it; block_at refuses the sign-in, opening
    no session, discarding the flow, denying a waiting device code and returning
    access_denied to the client. Either threshold at 0 switches that outcome
    off. Sign-ins are scored when a first factor passes and when a live session
    is reused at /authorize or a device approval; an allowed one records the
    country it came from as history. New risk.step_up and risk.blocked audit
    events (with the score, the signals and the country), a
    ridm_risk_decisions_total counter, and an Adaptive auth section in the
    console. Location comes from trusted-proxy headers
    (GEOIP_COUNTRY_HEADERS, GEOIP_LATITUDE_HEADERS,
    GEOIP_LONGITUDE_HEADERS) or a MaxMind DB file the deployment supplies
    (GEOIP_DB); with neither, the device and velocity signals still work.
  • Admin impersonation (settings.impersonation, off by default): an
    administrator holding the new ridm:users:impersonate permission (built-in
    ridm:owner only) asks POST /admin/tenants/{slug}/users/{user}/impersonate
    with a reason for a one-time, 60-second link. Opening it gives that browser an
    SSO session as the user that lasts at most max_minutes. Every access and ID token
    minted from the session names the administrator in act ({sub, iss}) and
    expires with it, and the admin API refuses any token carrying act. The
    session owes none of the user's MFA, password-change or risk steps. The
    account API refuses everything that needs a recent sign-in, plus consent
    withdrawal, with the new urn:ridm:error:impersonation-forbidden, and no consent
    can be given. Users holding any admin permission can't be impersonated.
    The account console shows a banner with End impersonation, which ends
    the session and puts back the browser's own. New impersonation.requested,
    impersonation.started and impersonation.ended events. Every other event
    raised during the session carries the administrator as impersonator,
    stored in a new audit column impersonator_id. The column is covered by the hash
    chain only when set, so existing rows still verify. The audit API can filter on
    it, and the CSV export has it as a new last column.
  • Audit chain verification you can run yourself: ridm audit verify --file
    checks a JSON export offline with the chain algorithm (now in ridm-core),
    streaming it. --head pins the hash the export must end on, and --after
    the hash it must follow, so consecutive exports prove one chain. ridm audit verify asks the server and ridm audit export streams a chain to disk. The
    verify endpoint gained last_hash and scheduled.
  • A daily audit_verify job checks every chain that grew, from its last
    verified checkpoint. A break is stored, logged, counted
    (ridm_audit_chain_breaks_total, ridm_audit_chains_broken) and recorded
    once as the new audit.chain_broken event in that tenant's chain.
  • Feature flags have their own console page (key, description, on/off, and
    per-organization values by slug), and applications can read them: the new
    standard features scope adds a features claim (the flags on for the
    sign-in's organization) to access and ID tokens, and GET /t/{slug}/features
    answers live for any access token of the tenant. settings.features values
    are now {enabled, description, organizations}; a bare boolean still loads
    and is still accepted.
  • Backchannel sign-in (OpenID CIBA Core 1.0): POST /t/{slug}/bc-authorize
    takes a login_hint (username or email) or an id_token_hint, an optional
    binding_message and requested_expiry, and answers an auth_req_id; the
    client collects the tokens with the new urn:openid:params:grant-type:ciba
    grant, polling or after rIDM pings its notification endpoint
    (backchannel_token_delivery_mode poll or ping, per client; push is not
    offered). The user is emailed or texted a link to the account console's new
    Requests page, where they approve or deny; an approval is remembered as
    consent. At most five requests wait on one user. New
    backchannel_request message template, backchannel.requested and
    backchannel.denied events, unknown_user_id and invalid_binding_message
    errors, and the CIBA discovery metadata.
  • The FAPI 2.0 Security Profile per client (security_profile: fapi2):
    registration refuses anything outside the profile, /authorize accepts only
    pushed requests, PKCE and DPoP-bound tokens are compulsory, client
    assertions must be PS256/ES256/EdDSA with the issuer as a string aud,
    request objects and DPoP proofs PS256/ES256/EdDSA, tokens and JARM responses
    are signed with ES256 or EdDSA even when the tenant defaults to RSA, and
    refresh tokens are not rotated. mTLS is not offered yet, so DPoP is the only
    sender constraint.
  • require_pushed_authorization_requests per client, for PAR without the rest
    of the profile.
  • SAML 2.0 identity providers upstream: an identity provider of the new kind
    saml (settings in saml_identity_providers) makes rIDM the service provider
    of a SAML IdP. It is set up from the IdP's metadata URL or document
    (POST /admin/tenants/{slug}/identity-providers/saml-metadata), and publishes
    its own SP metadata at {issuer}/broker/{alias}/saml/metadata (the SP entity
    ID), with signing and encryption certificates from the tenant's SAML keys.
    AuthnRequests are signed and sent by HTTP-Redirect or HTTP-POST; the
    assertion consumer service accepts only responses signed with a registered
    certificate (the assertion itself unless want_assertions_signed is off),
    answering rIDM's request, for this SP and consumer URL, within their window,
    once...
Read more