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 OKbut silently overwrote the credential'sconfigwith the patch value. For a password credential this destroyedhashed_passwordand 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.
