Current Series Release Notes

29.0.0-51

New Features

  • The JWS token signing algorithm is now configurable via two new options in [jwt_tokens]:

    • jws_algorithm selects the algorithm used when signing new tokens (default ES256, preserving backward compatibility). Supported values are ES256, ES384, ES512, and EdDSA.

    • jws_accepted_algorithms lists the algorithms accepted during token validation. This allows operators to accept tokens signed with a previous algorithm during a migration window.

    Changing the signing algorithm requires regenerating the JWS key pair via keystone-manage create_jws_keypair, since each algorithm uses a different key type.

Upgrade Notes

  • [bug 2148398] The identity:create_trust policy rule now uses %(target.trust.trustor_user_id)s instead of %(trust.trustor_user_id)s. The trust data from the request body is now passed explicitly via target_attr rather than relying on the JSON body merge. This aligns create_trust with all other trust policy rules which already use the target.trust.* prefix. Deployments that override the identity:create_trust policy and reference %(trust.trustor_user_id)s must update to %(target.trust.trustor_user_id)s.

  • [bug 2150089] Two new [security_compliance] options control opt-in insecure behaviour for operators with workflows that break after this upgrade:

    allow_insecure_admin_trust_cross_project_credentials_access (default False): set to True if admin-role trusts or application credentials need to access credentials across multiple projects (e.g. Mistral cron triggers syncing EC2 credentials system-wide).

    allow_insecure_application_credential_trust_escalation (default False): set to True if application credentials must create or manage trusts (e.g. Heat stacks authenticated via application credentials). Use OIDC federation flows (v3oidcclientcredentials, v3oidcdeviceauthz) as the proper long-term alternative.

    Both options are intentionally named to signal that enabling them is insecure. Migrate affected workflows away from these options.

  • [bug 2152573] The default behaviour of DELETE /v3/auth/tokens has changed: revoking a token now also revokes all tokens derived from it. To revoke only the specific token without affecting derived sessions, add ?cascade=false to the request.

  • bug 2153453: A token issued via a custom, third-party auth plugin (for example a site-specific SSO integration) is now treated as a delegated credential by default and rejected from managing trusts, application credentials, and OAuth1 access tokens, unless its method name is listed in the new [auth] additional_primary_auth_methods option. Deployments that run a custom auth plugin beyond keystone’s own built-in methods (password, totp, mapped, saml2, openid, external, kerberos, x509, token) must add its method name to this option to avoid regressing self-service workflows for users authenticated through it, such as switching active project.

  • LP#2154645: If you have overridden identity:list_role_assignments or identity:list_role_assignments_for_tree in policy.yaml, add not None:%(target.domain_id)s to any branch that contains domain_id:%(target.domain_id)s, for example:

    identity:list_role_assignments_for_tree: >-
      (rule:admin_required) or
      (role:reader and system_scope:all) or
      (role:reader and domain_id:%(target.domain_id)s and
       not None:%(target.domain_id)s)
    

    Deployments using the default policies are protected automatically.

  • LP#2158538: Authenticating with the token method using an application credential or EC2 credential token is now rejected with HTTP 403 (Forbidden). Previously, application credential tokens without an explicit scope would fall through to the user’s default project, and EC2 credential tokens could be re-scoped to any project. Callers that relied on exchanging an application credential token via the token method must authenticate with the application credential directly instead.

  • [bug 2159643] PATCH /v3/credentials/{credential_id} requests that include type, user_id, or project_id now fail with 400 Bad Request. Update any client or automation that PATCHes credentials to send blob only; delete and re-create the credential for any other field change.

    The existing, opt-in, deprecated [security_compliance] allow_insecure_admin_trust_cross_project_credentials_access option (see bug 2150089) is now scoped to EC2 credentials only, matching its one documented use case (an admin-role trust driving automated tooling that lists or syncs EC2 credentials with a third-party system). Enabling the option no longer grants access to any credential type other than EC2. Operators relying on this option should migrate the underlying automated workflow away from delegated tokens; it will be removed in a future release.

  • [bug 2153453 <https://bugs.launchpad.net/keystone/+bug/2153453>`___] ``ec2credential` has been added to the end of the default [auth] methods list. It is a marker method backed by a dummy plugin that cannot authenticate; attempting to authenticate with the ec2credential method via /v3/auth/tokens is rejected with HTTP 401.

    The new method is appended to the end of the method list on purpose: the fernet method bitmask is positional, and appending preserves the bits of all existing methods, so existing tokens continue to validate. During a rolling upgrade, tokens minted with the new marker decode to an empty method list on not-yet-upgraded nodes (the unknown bit is dropped), which degrades to the pre-upgrade behavior and is still blocked from delegated-token actions by the deny-by-default rule.

    Tokens minted before the upgrade carry no recoverable marker: they continue to be blocked from re-scoping and from managing trusts, application credentials, and OAuth1 access tokens (an empty method list is treated as delegated by default), but they remain usable to authorize requests in Keystone until they expire. Reducing [token] expiration after the upgrade shortens that window.

    The ec2credential method must be present in [auth] methods (it is included in the default list). Operators pinning an explicit methods list must add it after upgrading. While the method is disabled, POST /v3/ec2tokens and POST /v3/s3tokens refuse to issue tokens (HTTP 503): a token minted without the marker would not be recognized as delegated-credential derived on a token payload round-trip, so the endpoints fail closed rather than re-opening the vulnerability described above.

  • Two new configuration options have been added to the [jwt_tokens] section: jws_algorithm and jws_accepted_algorithms. The defaults (ES256 and ['ES256']) preserve the previous behavior, so no action is required for existing deployments.

    To migrate to a new algorithm, operators should:

    1. Add the new algorithm to jws_accepted_algorithms alongside the current one.

    2. Generate a new key pair with keystone-manage create_jws_keypair.

    3. Set jws_algorithm to the new algorithm.

    4. After all tokens signed with the old algorithm have expired, remove the old algorithm from jws_accepted_algorithms.

  • Support for Python 3.10 has been removed. Now Python 3.11 is the minimum version supported.

Critical Issues

  • [bug 2148398] The RBAC enforcer unconditionally merged the raw JSON request body into the policy enforcement dictionary after trusted target data had been set from the database. An attacker could include a target key in the JSON body to overwrite database-sourced RBAC target attributes, causing all %(target.*)s policy substitutions to evaluate against attacker-controlled values. This affected 88 endpoint/method combinations across all Keystone API resource areas. Any authenticated user could exploit this to read every credential secret in the deployment, create EC2 credentials for arbitrary users, or revoke other users’ tokens. A domain administrator could escalate to full cloud admin by creating inherited role grants on other domains. The vulnerability has been present since the Rocky release (14.0.0).

Security Issues

  • [bug 2148398] The RBAC policy enforcer now namespaces JSON request body data under a request_body key in the policy dictionary instead of merging it at the top level. This prevents user-controlled input from overwriting security-critical keys such as target (populated from the database by build_target or target_attr) and URL path parameters like user_id. All upstream policy rules are unaffected by this change. Deployments with custom policy rules that reference JSON body fields directly via %(field_name)s substitutions (not under target.) will need to update those references to %(request_body.field_name)s.

  • [bug 2150089] Delegated tokens (trusts, application credentials, OAuth1 access tokens) are now restricted to credentials whose project_id matches the token’s project scope. This closes a cross-project lateral movement vector where a delegated token could read, modify, or delete credentials belonging to a different project, including EC2 keys and TOTP/MFA seed bindings.

    Application credential tokens are now blocked from all trust operations (create, delete, list, get). Allowing an application credential to bootstrap a trust creates a new delegation context whose token can access authentication material outside the delegation chain, breaking the audit trail. The unrestricted flag governs credential management, not trust management.

  • [bug 2152573] DELETE /v3/auth/tokens now revokes the entire token chain (all tokens derived via rescoping) in addition to the specific token. This closes an attack where an attacker who obtained a project-scoped token could maintain access indefinitely by rescoping before expiry, even after the victim revoked the parent token. Pass ?cascade=false to revert to the previous single-token revocation behaviour.

  • [bug 2152573] A new endpoint DELETE /v3/users/{user_id}/tokens revokes all tokens for a user. Users may call it for their own account; administrators may call it for any user. This provides a self-service session-invalidation path for LDAP, federated, and OIDC users who cannot change their Keystone password.

  • [bug 2152573] When token validation detects that a user has been disabled in an external read-only backend (e.g. LDAP), Keystone now persists a durable revocation event. Previously, re-enabling the account in the external directory would silently revive all tokens that were valid before the account was disabled, including tokens held by an attacker.

  • [bug 2152932] Fixed a database connection leak that could affect deployments using a MySQL backend for federation service provider or identity mapping data. Under sustained token validation or federation-related API activity, keystone workers could retain database connections longer than intended and eventually exhaust their local connection pools. Operators may have observed intermittent API failures, request timeouts, or MySQL connection exhaustion until affected worker processes were restarted.

  • [bug 2153453] EC2-derived tokens (methods: ['ec2credential']) could still create, list, read, or delete trusts (/v3/OS-TRUST/trusts), create, list, read, or delete application credentials (/v3/users/{user_id}/application_credentials), and authorize OAuth1 request tokens (PUT /v3/OS-OAUTH1/authorize/{request_token_id}), because none of the delegation guards on those endpoints recognized ec2credential as a delegated method. OAuth1 access-token-scoped tokens had the same gap for trust management. A stolen EC2 access/secret key pair or OAuth1 access token could therefore bootstrap a trust, application credential, or OAuth1 access token delegation that outlives revocation of the original credential.

    These endpoints now use the same primary-auth-method allowlist already used by /v3/credentials and the OS-EC2 compat endpoints: any token whose methods aren’t entirely primary (interactive) auth methods, or that carries a trust_id, is rejected. Application credential tokens keep their existing, documented behavior on each endpoint (the opt-in trust-escalation escape hatch, and the unrestricted/restricted distinction for creating further application credentials); OAuth1 and EC2 credentials have no such use case and are blocked unconditionally.

  • LP#2154645: Any user holding role:reader on any project could list all role assignments under any domain by passing a domain ID as scope.project.id to GET /v3/role_assignments?include_subtree. Domain projects store domain_id=null, which matched the null domain_id of any project-scoped token in the oslo.policy string comparison, bypassing the domain-reader restriction. An explicit not None:%(target.domain_id)s guard has been added to the affected policy rules.

  • LP#2158538: Tokens issued via delegated credentials (application credentials and EC2 credentials) could be exchanged for a broader-scoped token using the token authentication method.

    An application credential token could omit the scope parameter during token-method reauthentication, causing the new token to fall through to the user’s default project, escaping the application credential’s project binding. EC2 credential tokens had no such guard and could be re-scoped to any project the underlying user has a role on, not just the project the EC2 credential was bound to.

  • [bug 2159643] Delegated tokens (trusts, application credentials, OAuth1 access tokens, and EC2 credentials, which were not previously recognized as delegated by this check) can no longer access /v3/credentials or the /v3/users/{user_id}/credentials/OS-EC2 compat endpoints at all, superseding the project-scope comparison introduced by bug 2150089. Trust-scoped and OAuth1 access-token-scoped tokens creating EC2 credentials is also removed; this was never a deliberate feature (see bug 1242597 / OSSA-2013-032).

    PATCH /v3/credentials/{credential_id} now only accepts blob. type, user_id, and project_id are immutable after creation. Delete and re-create the credential to change any of those fields. This also fixes bug 2158931: the previous guard only validated a PATCH request’s stored (pre-image) project_id, never the new project_id in the request body, so a delegated token scoped to one project could rewrite a credential it already owned there to point at a different project.

    This closes the API-level creation path only. It does not retroactively affect type=ec2 credentials that were already bound to a trust or OAuth1 access token before this fix (for example, created directly through the provider API or keystone-manage rather than through this guarded endpoint). To fully mitigate the risk this change addresses, operators should also audit their existing EC2 credentials for a trust_id or access_token_id in their stored blob and delete and re-create any that have one, so that no EC2 credential remains tied to a delegation that can independently expire, be revoked, or otherwise change state out from under it.

  • [bug 2153453] Tokens minted from an EC2 or S3 credential exchange (POST /v3/ec2tokens or POST /v3/s3tokens) have always recorded the ec2credential auth method, which was never a registered auth method. The fernet token provider encodes methods as a bitmask of the configured [auth] methods, so the marker was silently dropped on the token payload round-trip: on any validation that re-read the payload (cache miss, different process), the token looked like it had been issued with no auth methods at all.

    As a result, the guard that rejects EC2/S3-credential tokens from being used to authorize requests in Keystone could not recognize such tokens, and the guards that reject delegated tokens from re-scoping or managing trusts, application credentials, and OAuth1 access tokens only blocked them incidentally (an empty method list is treated as delegated by default). A stolen EC2 or S3 access/secret key pair could therefore be exchanged for a token and then used to authorize requests in Keystone.

    The ec2credential method is now a registered auth method backed by a non-functional (dummy) plugin, so the marker survives the token payload round-trip and tokens minted from an EC2 or S3 credential exchange are recognized as delegated-credential derived by all of the above guards. The plugin cannot authenticate: EC2 and S3 credentials must still be exchanged at their respective endpoints. The method can never be classified as a primary auth method, so listing it in [auth] additional_primary_auth_methods has no effect.

Bug Fixes

  • [bug 2134925] Fixed a crash (HTTP 500) when listing sub-resource endpoints with a limit query parameter (e.g. GET /v3/users/{user_id}/projects?limit=1). The wrap_collection method now includes URL path parameters from flask.request.view_args when building the next pagination link, so that flask.url_for() can correctly resolve sub-resource routes.

  • [bug 2161103] LDAP-backed domains do not support marker-based (keyset) pagination. LDAP has no native keyset pagination, and the session cookie required for native LDAP paging cannot survive across stateless HTTP requests in a multi-node deployment. Attempting client-side emulation (fetching all entries to find the marker position) would incur a full directory scan on every page request, which is unacceptable for large directories.

    The list_limit setting continues to cap the number of entries returned per request. Callers should not rely on next link markers for LDAP-backed domains.

  • Fixed token validation to correctly handle mixed key types in the public key repository. Previously, jwt.decode() could raise InvalidKeyError or InvalidAlgorithmError when encountering public keys of a different type than expected, causing token validation to fail instead of trying the next available key. These exceptions are now handled gracefully during the key iteration loop.

  • Fixed jwt.decode() call in _decode_token_from_id to pass algorithms as a list instead of a string. While PyJWT accepted the string form, it was inconsistent with the documented API.