|
| 1 | +--- |
| 2 | +sidebar_label: OSSA-2026-037 |
| 3 | +--- |
| 4 | + |
| 5 | +# OSSA-2026-037: Inconsistent scope enforcement for delegated tokens in Keystone |
| 6 | + |
| 7 | +| Property | Value | |
| 8 | +|:-----------------|:---------------------------------------------------| |
| 9 | +| Date | 2026-08-25 | |
| 10 | +| CVE | CVE pending (two ids requested from MITRE) | |
| 11 | +| Severity | High | |
| 12 | +| Affected Project | Keystone | |
| 13 | +| Reporter | Grzegorz Grasza (Red Hat), Tim Shephard (roiai.ca) | |
| 14 | + |
| 15 | +## Summary |
| 16 | + |
| 17 | +Two related vulnerabilities were discovered in OpenStack Keystone concerning tokens obtained through |
| 18 | +delegated credentials — application credentials, EC2 credentials, and OAuth1 access tokens. |
| 19 | + |
| 20 | +Grzegorz Grasza of Red Hat reported that Keystone did not consistently block delegated tokens from |
| 21 | +creating new long-lived credentials or authorizing new delegations (Bug #2153453). A token derived |
| 22 | +from an EC2 credential or an OAuth1 access token could create, list, read, and delete trusts, and an |
| 23 | +EC2-derived token could create application credentials and authorize OAuth1 request tokens. Each of |
| 24 | +these operations mints a new, independent grant that persists after the original credential is |
| 25 | +revoked. Because trust creation validates the requested roles against the trustor's full role |
| 26 | +assignments rather than against the roles of the delegated token, a narrowly scoped OAuth1 or EC2 |
| 27 | +token could additionally delegate roles it was never authorized for. |
| 28 | + |
| 29 | +Tim Shephard of roiai.ca separately reported that delegated tokens could be submitted to the token |
| 30 | +authentication method of `POST /v3/auth/tokens` to escape their project scope (Bug #2158538). An |
| 31 | +application credential token presented without an explicit scope was rescoped to the credential |
| 32 | +owner's default project instead of the project the credential was issued for, and an EC2-derived |
| 33 | +token could be rescoped to any project the underlying user holds a role in. |
| 34 | + |
| 35 | +**Every OSISM deployment running an unpatched Keystone version is affected.** Application |
| 36 | +credentials, EC2 credentials, trusts, and the token authentication method are available in every |
| 37 | +Keystone deployment, `[token] allow_rescope_scoped_token` is enabled by default, and there is no |
| 38 | +configuration that makes a deployment immune. Exploitation requires possession of a delegated |
| 39 | +credential, for example a leaked application credential or EC2 key pair. |
| 40 | + |
| 41 | +Upstream has not published a CVSS score for this issue, and the two CVE ids requested from MITRE are |
| 42 | +still pending. The severity rating above is OSISM's assessment: an attacker holding a delegated |
| 43 | +credential can turn it into persistent access that survives revocation of that credential, obtain |
| 44 | +roles beyond the delegation, and reach other projects of the credential owner. |
| 45 | + |
| 46 | +## Affected Versions |
| 47 | + |
| 48 | +The upstream advisory states the affected ranges as `>=13.0.0 <27.0.3`, `>=28.0.0 <28.0.3` and |
| 49 | +`>=29.0.0 <29.0.3`, so the fixes are expected in Keystone 27.0.3 (Epoxy), 28.0.3 (Flamingo) and |
| 50 | +29.0.3 (Gazpacho). |
| 51 | + |
| 52 | +**At the time of this advisory there is no fixed Keystone release available upstream for any |
| 53 | +series.** The upstream fixes are still under review on OpenDev and have not merged yet. |
| 54 | + |
| 55 | +| OpenStack Release | Upstream Status | Fix in OSISM Images | |
| 56 | +|:-----------------------|:---------------------------------------------------|:---------------------------------------------------------------------------------------------------------------| |
| 57 | +| Caracal (2024.1) | Unmaintained, no upstream fix | Community-curated backport in preparation | |
| 58 | +| Dalmatian (2024.2) | End of life since 2026-04-29, no fix | Community-curated backport in preparation | |
| 59 | +| Epoxy (2025.1) | Vulnerable — fix proposed (27.0.3), not yet merged | Proposed upstream patch ([PR #776](https://github.com/osism/container-images-kolla/pull/776)) | |
| 60 | +| Flamingo (2025.2) | Vulnerable — fix proposed (28.0.3), not yet merged | Proposed upstream patch ([PR #776](https://github.com/osism/container-images-kolla/pull/776)) | |
| 61 | +| Gazpacho (2026.1) | Vulnerable — fix proposed (29.0.3), not yet merged | Patch included ([PR #776](https://github.com/osism/container-images-kolla/pull/776)), images not published yet | |
| 62 | +| Hibiscus (2026.2, dev) | Fix proposed on master, not yet merged | Not shipped by OSISM yet | |
| 63 | + |
| 64 | +Caracal (2024.1) is unmaintained and Dalmatian (2024.2) reached end of life on 2026-04-29 upstream; |
| 65 | +neither will receive an official fixed release. For these two releases OSISM is preparing the fix as |
| 66 | +community-curated backports, shipped as downstream patches in the Keystone container images. This |
| 67 | +advisory will be updated as soon as the patched images are available. |
| 68 | + |
| 69 | +For Epoxy (2025.1) and Flamingo (2025.2) OSISM already ships container images that carry the |
| 70 | +proposed upstream patches ahead of their official upstream release. The same patches are included |
| 71 | +for Gazpacho (2026.1), whose images will be published with the upcoming OSISM 11 release. |
| 72 | + |
| 73 | +## Impact on OSISM |
| 74 | + |
| 75 | +Keystone is a core service of every OSISM deployment, and all delegation mechanisms involved are |
| 76 | +available by default: |
| 77 | + |
| 78 | +- **Application credentials** can be created by every user for their own projects. They are the |
| 79 | + recommended way to give automation access to the cloud and are widely used. |
| 80 | +- **EC2 credentials** are used to access the S3 API of Ceph RGW or Swift with Keystone identities. |
| 81 | + Every user can create them. |
| 82 | +- **OAuth1** is part of Keystone's default `[auth] methods` |
| 83 | + (`external,password,token,oauth1,mapped,application_credential`), which OSISM does not change. |
| 84 | + Kolla only replaces the list when Keystone federation is enabled |
| 85 | + (`password,token,openid,application_credential`), which removes OAuth1 but leaves the other |
| 86 | + paths open. |
| 87 | +- **Trusts** are created on behalf of users by services such as Heat and Magnum, and every user can |
| 88 | + create them. |
| 89 | +- The token authentication method and `[token] allow_rescope_scoped_token = True` are Keystone |
| 90 | + defaults that OSISM does not change. |
| 91 | + |
| 92 | +Exploitation requires an attacker to hold a delegated credential of a victim — a leaked application |
| 93 | +credential, an EC2 access/secret key pair, or an OAuth1 access token — or a user to deliberately |
| 94 | +abuse their own narrow delegation. In both cases the attacker gains more than the delegation was |
| 95 | +meant to grant: persistent credentials that survive revocation of the original one, trusts carrying |
| 96 | +roles the delegation never had, and tokens for other projects of the credential owner. |
| 97 | + |
| 98 | +### How to Check if You Are Affected |
| 99 | + |
| 100 | +#### Check the Running Keystone Version |
| 101 | + |
| 102 | +```bash |
| 103 | +docker exec keystone pip show keystone | grep ^Version |
| 104 | +``` |
| 105 | + |
| 106 | +Because no fixed Keystone release exists upstream yet, your deployment is **affected** unless you |
| 107 | +have deployed an OSISM container image that includes the patches (see Remediation). |
| 108 | + |
| 109 | +#### Check Whether the Patched Image Is Deployed |
| 110 | + |
| 111 | +The fix introduces the module `keystone.api._shared.delegation`, which only exists in patched |
| 112 | +images: |
| 113 | + |
| 114 | +```bash |
| 115 | +docker exec keystone /var/lib/kolla/venv/bin/python3 -c "import keystone.api._shared.delegation" \ |
| 116 | + 2>/dev/null && echo patched || echo vulnerable |
| 117 | +``` |
| 118 | + |
| 119 | +`vulnerable` means the running image does not contain the fix. |
| 120 | + |
| 121 | +## Vulnerability Details |
| 122 | + |
| 123 | +Keystone distinguishes *primary* authentication methods, which authenticate a user directly |
| 124 | +(`password`, `token`, `totp`, `mapped`, `saml2`, `openid`, `external`, `kerberos`, `x509`), from |
| 125 | +*delegated* credentials, which are deliberately narrow grants derived from a user |
| 126 | +(`application_credential`, `ec2credential`, `oauth1`, and trust-scoped tokens). API endpoints that |
| 127 | +mint a new, independent grant must reject delegated tokens, otherwise a narrow grant can bootstrap a |
| 128 | +broader or longer-lived one. Before the fix these guards were implemented separately per endpoint, |
| 129 | +and each of them recognized only some of the delegated methods. |
| 130 | + |
| 131 | +### Bug #2153453 — Delegated tokens can create persistent delegations |
| 132 | + |
| 133 | +- `_check_application_credential()` in `keystone/api/trusts.py` only blocked tokens whose methods |
| 134 | + included `application_credential`. Tokens derived from an OAuth1 access token or an EC2 credential |
| 135 | + could create, list, read, and delete trusts. Since trust creation validates the requested roles |
| 136 | + against the trustor's full role assignments rather than against the roles of the requesting |
| 137 | + token, such a token could also delegate roles the delegation was never scoped to. |
| 138 | +- `_block_delegated_token_app_creds()` in `keystone/api/users.py` only checked for a `trust_id` or |
| 139 | + an OAuth1 `access_token_id` on the token, so an EC2-derived token — which carries neither — could |
| 140 | + create and manage application credentials and access rules. |
| 141 | +- `PUT /v3/OS-OAUTH1/authorize/{request_token_id}` in `keystone/api/os_oauth1.py` only rejected |
| 142 | + trust-scoped, OAuth1-scoped, and application credential tokens, so an EC2-derived token could |
| 143 | + authorize OAuth1 request tokens and thereby mint new OAuth1 access tokens. |
| 144 | + |
| 145 | +All three grants outlive the credential used to create them: revoking a leaked EC2 key pair or |
| 146 | +OAuth1 access token does not remove the trusts, application credentials, or access tokens it |
| 147 | +bootstrapped. |
| 148 | + |
| 149 | +The fix introduces `keystone/api/_shared/delegation.py` with a deny-by-default allowlist of primary |
| 150 | +authentication methods. `is_delegated_method()` treats every token whose methods are not entirely |
| 151 | +primary — including an empty method list, which occurs for `ec2credential` tokens after a fernet |
| 152 | +round-trip — as delegated, and all three endpoints now use this shared check. Application credential |
| 153 | +tokens keep their documented behavior (the opt-in `[security_compliance] |
| 154 | +allow_insecure_application_credential_trust_escalation` for trust management and the |
| 155 | +unrestricted/restricted distinction for creating further application credentials); OAuth1 and EC2 |
| 156 | +credentials are blocked unconditionally. |
| 157 | + |
| 158 | +### Bug #2158538 — Delegated tokens escape their project scope via token reauthentication |
| 159 | + |
| 160 | +`token_authenticate()` in `keystone/auth/plugins/token.py` implements the token authentication |
| 161 | +method, i.e. exchanging an existing token for a new one. Trust-scoped and OAuth1-scoped tokens were |
| 162 | +rejected unconditionally, but application credential tokens were only rejected when an explicit |
| 163 | +scope was requested: with the scope omitted, the request fell through to Keystone's default-project |
| 164 | +scoping and returned a token for the credential owner's default project — a different project than |
| 165 | +the one the application credential was bound to. EC2-derived tokens matched none of the checks and, |
| 166 | +with the default `[token] allow_rescope_scoped_token = True`, could be rescoped to any project the |
| 167 | +underlying user has a role in. |
| 168 | + |
| 169 | +The fix rejects every delegated token from the token authentication method using the same shared |
| 170 | +classification and returns HTTP 403. |
| 171 | + |
| 172 | +### Prerequisite Changes |
| 173 | + |
| 174 | +The upstream fix consists of two changes that are interdependent and must be applied together; OSISM |
| 175 | +ships both in the patched images: |
| 176 | + |
| 177 | +1. **trusts, oauth1, app-creds: reject delegated tokens across all endpoints** |
| 178 | + ([Bug #2153453](https://bugs.launchpad.net/keystone/+bug/2153453)) — introduces the shared |
| 179 | + delegation classification and the new `[auth] additional_primary_auth_methods` option and applies |
| 180 | + the check to trusts, application credentials, and OAuth1 authorization. |
| 181 | +2. **auth: reject delegated tokens from token-method reauthentication** |
| 182 | + ([Bug #2158538](https://bugs.launchpad.net/keystone/+bug/2158538)) — applies the classification |
| 183 | + to the token authentication method and depends on the first change. |
| 184 | + |
| 185 | +:::warning |
| 186 | + |
| 187 | +Behavior changes after the fix: |
| 188 | + |
| 189 | +- Any authentication method that is not built into Keystone is treated as a delegated credential and |
| 190 | + rejected from managing trusts, application credentials, and OAuth1 access tokens, and from token |
| 191 | + reauthentication. Deployments running a custom or third-party authentication plugin must list its |
| 192 | + method name in `[auth] additional_primary_auth_methods`, otherwise these workflows fail for users |
| 193 | + authenticated through it. |
| 194 | +- Exchanging an application credential token or an EC2-derived token for a new token via the token |
| 195 | + authentication method is rejected with HTTP 403. Clients must authenticate with the application |
| 196 | + credential directly instead. |
| 197 | + |
| 198 | +::: |
| 199 | + |
| 200 | +### Additional Hardening in the OSISM Images |
| 201 | + |
| 202 | +Besides the two upstream patches, the patched OSISM images for 2025.1, 2025.2, and 2026.1 contain |
| 203 | +the upstream change *Ban ec2credential tokens from Keystone API*, which is merged on master but not |
| 204 | +backported to the stable branches upstream. It rejects tokens derived from EC2 credentials for every |
| 205 | +Keystone API call except token validation and re-authentication, closing the whole class of EC2 |
| 206 | +token issues at the API boundary. The behavior is controlled by the new option `[auth] |
| 207 | +ban_ec2credential_tokens` (default `True`). Deployments that use EC2-derived tokens directly against |
| 208 | +the Keystone API must set it to `False`. |
| 209 | + |
| 210 | +## Remediation |
| 211 | + |
| 212 | +### For OSISM Releases |
| 213 | + |
| 214 | +Because no fixed Keystone release is available upstream, OSISM provides the fix as downstream |
| 215 | +patches for the Keystone container images via the following change: |
| 216 | + |
| 217 | +- [container-images-kolla commit 01ae42c](https://github.com/osism/container-images-kolla/commit/01ae42cdce2dc6c86927116fcce10c90a930122b) |
| 218 | + ([PR #776](https://github.com/osism/container-images-kolla/pull/776)) |
| 219 | + |
| 220 | +This change adds the patches for 2025.1 (Epoxy), 2025.2 (Flamingo), and 2026.1 (Gazpacho). The |
| 221 | +community-curated backports for 2024.1 (Caracal) and 2024.2 (Dalmatian) are in preparation and will |
| 222 | +be announced in an update of this advisory. |
| 223 | + |
| 224 | +A fix will be included in upcoming OSISM releases that ship the patched Keystone container images. |
| 225 | +Consult the [OSISM Release Notes](../../release-notes/) for version information and availability. |
| 226 | + |
| 227 | +The affected code runs in the Keystone API service (`keystone` container). Using rolling tags, |
| 228 | +override the Keystone container images in `environments/kolla/images.yml`. With a stable OSISM |
| 229 | +release the Kolla images are pulled from the `kolla/release/<openstack_version>` namespace with |
| 230 | +pinned version tags (`docker_namespace` in `environments/kolla/configuration.yml`), whereas the |
| 231 | +rolling images live in the `kolla` namespace. The image parameters therefore have to be overridden |
| 232 | +together with the tag; the tag override alone would point at an image that does not exist: |
| 233 | + |
| 234 | +```yaml title="environments/kolla/images.yml" |
| 235 | +keystone_tag: "2025.1" # or "2025.2", depending on your OpenStack release |
| 236 | +keystone_image: "registry.osism.tech/kolla/keystone" |
| 237 | +keystone_fernet_image: "registry.osism.tech/kolla/keystone-fernet" |
| 238 | +keystone_ssh_image: "registry.osism.tech/kolla/keystone-ssh" |
| 239 | +``` |
| 240 | +
|
| 241 | +Replace `registry.osism.tech` if you pull the images from a mirror. From 2025.2 on the Keystone |
| 242 | +role additionally runs an `httpd` container (`keystone_httpd_image`, `keystone_httpd_tag`) that |
| 243 | +contains no Keystone code; its tag follows `keystone_tag` by default, so pin `keystone_httpd_tag` |
| 244 | +to the currently deployed tag when overriding `keystone_tag`. |
| 245 | + |
| 246 | +### Mitigation |
| 247 | + |
| 248 | +Because all deployments are affected and the vulnerabilities require only a delegated credential, |
| 249 | +applying the patched container image is the only complete remediation. Until the fix is deployed — |
| 250 | +in particular on 2024.1 and 2024.2 — consider the following measures: |
| 251 | + |
| 252 | +1. Disabling the rescoping of scoped tokens closes the token reauthentication path (Bug #2158538). |
| 253 | + Add the following to the configuration repository and redeploy Keystone: |
| 254 | + |
| 255 | + ```ini title="environments/kolla/files/overlays/keystone.conf" |
| 256 | + [token] |
| 257 | + allow_rescope_scoped_token = False |
| 258 | + ``` |
| 259 | + |
| 260 | + This also prevents legitimate clients from exchanging a scoped token for another scoped token; |
| 261 | + authenticating with an unscoped token or with credentials directly is not affected. It does not |
| 262 | + mitigate Bug #2153453. |
| 263 | +2. Auditing existing delegations. Trusts, application credentials, EC2 credentials, and OAuth1 |
| 264 | + access tokens that were created through a delegated token before the fix remain valid after it is |
| 265 | + applied and have to be revoked manually: |
| 266 | + |
| 267 | + ```bash |
| 268 | + openstack --os-cloud admin trust list |
| 269 | + openstack --os-cloud admin credential list --type ec2 |
| 270 | + openstack --os-cloud admin consumer list |
| 271 | + openstack --os-cloud admin application credential list --user <user> |
| 272 | + ``` |
| 273 | + |
| 274 | + Application credentials can only be listed per user. Trusts whose trustee is not a service user, |
| 275 | + and credentials or consumers nobody can account for, are candidates for removal. |
| 276 | +3. Rotating EC2 credentials and OAuth1 access tokens that may have leaked, since revoking them does |
| 277 | + not remove grants they may already have bootstrapped. |
| 278 | +4. Monitoring the Keystone API logs for `POST /v3/OS-TRUST/trusts`, |
| 279 | + `POST /v3/users/{user_id}/application_credentials`, and `PUT /v3/OS-OAUTH1/authorize` requests |
| 280 | + from unexpected sources. |
| 281 | + |
| 282 | +## References |
| 283 | + |
| 284 | +- [OSSA-2026-037 Advisory](https://security.openstack.org/ossa/OSSA-2026-037.html) |
| 285 | +- [OSISM Fix (container-images-kolla commit 01ae42c)](https://github.com/osism/container-images-kolla/commit/01ae42cdce2dc6c86927116fcce10c90a930122b) |
| 286 | +- [OSISM Fix (container-images-kolla PR #776)](https://github.com/osism/container-images-kolla/pull/776) |
| 287 | +- [OpenDev Review (Proposed Fix 1/2 - Hibiscus)](https://review.opendev.org/1002301) |
| 288 | +- [OpenDev Review (Proposed Fix 2/2 - Hibiscus)](https://review.opendev.org/1002302) |
| 289 | +- [OpenDev Review (Proposed Fix 1/2 - Gazpacho)](https://review.opendev.org/1002303) |
| 290 | +- [OpenDev Review (Proposed Fix 2/2 - Gazpacho)](https://review.opendev.org/1002304) |
| 291 | +- [OpenDev Review (Proposed Fix 1/2 - Flamingo)](https://review.opendev.org/1002305) |
| 292 | +- [OpenDev Review (Proposed Fix 2/2 - Flamingo)](https://review.opendev.org/1002306) |
| 293 | +- [OpenDev Review (Proposed Fix 1/2 - Epoxy)](https://review.opendev.org/1002307) |
| 294 | +- [OpenDev Review (Proposed Fix 2/2 - Epoxy)](https://review.opendev.org/1002308) |
| 295 | +- [OpenDev Review (Ban ec2credential tokens from Keystone API)](https://review.opendev.org/997369) |
| 296 | +- [Launchpad Bug #2153453](https://bugs.launchpad.net/keystone/+bug/2153453) |
| 297 | +- [Launchpad Bug #2158538](https://bugs.launchpad.net/keystone/+bug/2158538) |
0 commit comments