Skip to main content

v26.2.13

v26.2.13

Detect mismatched database dialect at startup

Kratos, Hydra, Keto, and backoffice now verify at startup that the configured database dialect matches the actual database server. The migrate sql and serve commands run a single SELECT version() query and refuse to start if the DSN scheme does not match the server.

Previously, pointing a service at a CockroachDB cluster with a postgres:// DSN (or the reverse) silently applied the wrong migrations and produced broken schemas. This is easy to do because CockroachDB speaks the PostgreSQL wire protocol and advertises a postgres:// DSN itself.

If your DSN scheme is wrong, you will now see a clear error such as:

DSN scheme postgres:// declares a PostgreSQL database but the server is
CockroachDB. Replace the scheme with cockroach:// so that the service
picks the correct migrations and SQL dialect.

To fix it, change the DSN scheme from postgres:// to cockroach:// (or the reverse) and restart. MySQL and SQLite are unaffected.

Extend Landlock sandbox to migrate and courier subcommands

Kratos now applies the Landlock filesystem sandbox to the migrate sql and courier subcommands in addition to serve. After startup, these processes can only read and write the paths already allowlisted for serve: watched config files, TLS certificates, the courier template directory, SMTP client certificates, every file:// URI in the loaded configuration, the SQLite data directory (if SQLite is used), and any paths listed under security.landlock.allowed_paths.

This tightens the security posture of the migrate jobs and the courier worker pod without requiring any configuration changes. The existing security.landlock.disabled and security.landlock.allowed_paths settings continue to apply to all sandboxed subcommands. The sandbox is a no-op on kernels without Landlock support and is automatically skipped under go test.

Reject identity PATCH that desynchronizes a credential's type from its map key

PATCH /admin/identities/{id} now returns 400 Bad Request when a JSON patch leaves a credential whose type field does not equal its map key — for example, replacing /credentials/password with a value that omits "type", replacing /credentials/password/type with an empty string, or adding /credentials/<newkey> with a value that omits "type".

Previously these patches were accepted with two different broken outcomes:

  • Replacing an existing credential subtree returned 200 OK but silently overwrote the credential's config with the patch value. For a password credential this destroyed hashed_password and prevented the user from logging in.
  • Adding a new credential key produced a misleading 500 Internal Server Error (No identity credential type "" could be found, this is a code bug.). The surrounding transaction rolled back, so the database itself was left intact in this case.

The new validation runs before any persistence, so neither outcome can occur. The check is enforced at two layers — the PATCH handler rejects the request directly, and IdentityManager.ValidateIdentity enforces the same invariant as a safeguard against any caller that bypasses the handler and writes to Identity.Credentials directly. The error response identifies the offending map key:

credentials.<key>.type must equal "<key>", got "<value>"

Valid PATCH operations on credential subtrees, such as /credentials/password/config/hashed_password, continue to work unchanged.

Reject invisible Unicode characters in identifiers to prevent duplicate accounts

Identity identifier normalization now applies NFKC compatibility decomposition and strips Unicode format characters (category Cf) — zero-width spaces, soft hyphens, the byte-order mark, bidi controls, and other invisible code points. Previously, an attacker could register a second account with an email or username that was visually identical to an existing user's identifier but stored as a distinct string, bypassing the duplicate-account check.

After this change, [email protected] and alice​@example.com collapse to the same identifier and the second registration fails with a duplicate- credential error. The same fix covers the password, code, and webauthn login strategies, as well as recovery- and verification-address lookups.

B2B organization domain matching (MatchEmailDomain and the errorIfEmailAddressesAreClaimedByOrg hook) now canonicalizes both sides of the domain comparison the same way, so a Cf-poisoned domain in an organization's configuration no longer silently disables auto-enrollment for that domain's legitimate users.