OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

keystone — Scenarios

End-to-end scenarios — keystone — branch stable/2026.2

Branch stable/2026.2 @ e6dc5ae079e6 — repository https://opendev.org/openstack/keystone — analysed on 2026-10-28

Statuses refer to errors. The behaviour of keystonemiddleware, in the other services, is marked [inferred]: it lives outside this repository.

1. Authentication, then token validation by a service

sequenceDiagram
  autonumber
  participant U as Client (openstack CLI)
  participant K as keystone-api
  participant DB as SQL
  participant C as memcached
  participant N as nova-api + keystonemiddleware
  U->>K: POST /v3/auth/tokens {password, scope project}
  K->>DB: SELECT user/local_user/password (+ failure counter)
  alt bad password
    K->>DB: UPDATE local_user SET failed_auth_count+1
    K-->>U: 401 "The request you have made requires authentication."
  else account locked / disabled / password expired
    K-->>U: 401 AccountLocked / UserDisabled / PasswordExpired
  else
    K->>DB: roles (effective assignments), catalog
    K->>K: Fernet encrypt(msgpack payload v2) with the primary key
    K-->>U: 201 X-Subject-Token + token body + catalog
  end
  U->>N: GET /servers  X-Auth-Token
  N->>K: GET /v3/auth/tokens  X-Auth-Token=<service token>  X-Subject-Token=<user token>  [inferred: middleware]
  K->>C: tokens region (cached validation?)
  alt not cached
    K->>K: decrypt with the active keys, expiry check
    K->>DB: SELECT revocation_event … (check_token)
    K->>DB: SELECT user (enabled?)
  end
  alt revoked / expired / unreadable key
    K-->>N: 404 TokenNotFound
    N-->>U: 401 [inferred]
  else
    K-->>N: 200 token (roles, project, catalog)
    N->>N: middleware cache (token_cache_time) [inferred]
  end

Diagnostics:

  • 401 for every user after a key rotation: the key repository is not synchronised between nodes. A token encrypted on node A with its new primary key cannot be decrypted on node B (ValidationError "Could not recognize Fernet token", then TokenNotFound).
  • 500 on every authentication: KeysNotFound, an empty key repository or wrong permissions.
  • "Authorization failed … from <ip>" in the logs: the WARNING emitted by the 401 handler (keystone/server/flask/application.py:137-141).

2. Disabling a user: instant revocation

sequenceDiagram
  autonumber
  participant A as Administrator
  participant K as keystone-api
  participant DB as SQL
  participant C as memcached
  participant MQ as RabbitMQ
  participant N as other services
  A->>K: PATCH /v3/users/{id} {"enabled": false}
  K->>DB: UPDATE user SET enabled=false
  K->>K: callbacks "disabled user" (same request)
  K->>DB: INSERT revocation_event(user_id, issued_before=now) + purge of expired events
  K->>DB: DELETE application_credential … WHERE user_id
  K->>C: invalidate tokens and receipts regions (new region identifier)
  K->>MQ: identity.user.updated (CADF)
  K-->>A: 200
  N->>K: validation of an old token of the user [inferred]
  K-->>N: 404 TokenNotFound (revocation event matched)

Diagnostics: tokens still accepted after disabling the user. There are two possible causes:

  • the services' middleware cache (token_cache_time, 300 s by default on the keystonemiddleware side [inferred]);
  • a non-shared Keystone cache (dogpile.cache.memory), where the invalidation stays in the one worker that handled it.

3. Federated login (OIDC through mod_auth_openidc, WebSSO)

sequenceDiagram
  autonumber
  participant B as Browser (Horizon)
  participant H as Apache + mod_auth_openidc
  participant K as keystone-api
  participant I as External IdP (OIDC)
  participant DB as SQL
  B->>H: GET /v3/auth/OS-FEDERATION/websso/openid?origin=https://horizon/…
  H->>I: OIDC redirect, authentication
  I-->>H: code → id_token (validated by the module)
  H->>K: same request + OIDC-* attributes in the WSGI environment
  K->>DB: identity_provider (by remote_id), federation_protocol, mapping
  K->>K: RuleProcessor.process(assertion) → user, groups, projects
  alt no rule matches / IdP disabled
    K-->>B: 401 / 403
  else
    K->>DB: shadow user (federated_user, nonlocal_user), expiring_user_group_membership (TTL authorization_ttl)
    K->>K: federated unscoped token (payload v4)
    K-->>B: 200 HTML page that POSTs the token to "origin" (trusted_dashboard)
  end
  B->>K: POST /v3/auth/tokens {token, scope project}
  K-->>B: 201 federated project-scoped token (v5)

Sources: keystone/api/auth.py:360-470, keystone/auth/plugins/mapped.py:41-500, keystone/federation/utils.py:469-1130. The origin must be in [federation]/trusted_dashboard, or the request is refused.

4. Application credential: create, then use

sequenceDiagram
  autonumber
  participant U as User
  participant K as keystone-api
  participant DB as SQL
  U->>K: POST /v3/users/{me}/application_credentials {name, roles?, expires_at?, access_rules?}
  K->>DB: effective roles of the user on the token's project
  alt requested role not held
    K-->>U: 404 RoleAssignmentNotFound
  else quota reached
    K-->>U: 403 ApplicationCredentialLimitExceeded
  else
    K->>DB: INSERT application_credential (secret_hash), application_credential_role, access_rule
    K-->>U: 201 {id, secret (shown once only), …}
  end
  Note over U: later, from a script or a service
  U->>K: POST /v3/auth/tokens {methods:[application_credential], id, secret}
  K->>DB: application_credential + hash check + expiry + user enabled
  K-->>U: 201 token v9 (credential's roles only, expiry ≤ the credential's)

5. Rotating the Fernet keys (operation)

sequenceDiagram
  autonumber
  participant O as Operator / cron
  participant M as keystone-manage (node 1)
  participant FS1 as fernet-keys node 1
  participant FS2 as fernet-keys nodes 2..n
  O->>M: fernet_rotate --keystone-user keystone --keystone-group keystone
  M->>FS1: create a new staged key 0; rename the old 0 to N+1 (primary)
  M->>FS1: delete the oldest keys beyond max_active_keys
  O->>FS2: rsync of the repository (must be fast)
  Note over FS1,FS2: between rotation and sync, node 1 encrypts with key N+1, which nodes 2..n already know as key 0 (staged): validation keeps working

If the sync comes after a second rotation, the nodes no longer share their keys and validations fail (see scenario 1). The key lifetime should be at least expiration + allow_expired_window × the number of keys [inferred: Keystone documentation].

Source: reverse-spec-keystone/04-scenarios_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.