- definitions.md: formal term disambiguation for overloaded concepts (service, interface, token, identity, domain) with cross-domain mapping tables (alknet ↔ Keystone, distributed git, rustfs) and 8 open questions - references/rustfs/: research on rustfs S3 store, Keystone/OIDC integration, and credential mapping to CredentialSet - references/gitserver/: research on gitserver library architecture and integration paths as HTTP MessageInterface and SSH adapter - references/openstack-keystone/: research on Keystone identity concepts (tokens, scoping, service catalog, RBAC, trust delegation, federation) and what alknet should adopt vs skip - references/distributed-identity/: research on decentralized git, smart contract ACL, on-chain identity, and Radicle comparison
963 lines
38 KiB
Markdown
963 lines
38 KiB
Markdown
# OpenStack Keystone Identity Service — Reference Document
|
|
|
|
> Status: Research reference
|
|
> Created: 2026-06-08
|
|
> Context: alknet auth/identity system design; rustfs S3-compatible store with Keystone auth
|
|
|
|
## 1. Overview
|
|
|
|
OpenStack Keystone is the identity service for the OpenStack cloud platform. It
|
|
provides authentication, authorization, and service discovery via a RESTful HTTP
|
|
API. Every other OpenStack service (Nova, Neutron, Cinder, Swift, etc.) depends
|
|
on Keystone for token validation and access control.
|
|
|
|
Key responsibilities:
|
|
|
|
| Responsibility | Description |
|
|
|---|---|
|
|
| **Authentication** | Verify identity via passwords, tokens, TOTP, SAML, OIDC, application credentials |
|
|
| **Authorization** | Role-based access control (RBAC) across projects, domains, and system scope |
|
|
| **Service Catalog** | Registry of available services and their endpoint URLs |
|
|
| **Token Management** | Issue, validate, and revoke bearer tokens with scoped authorization |
|
|
| **Federation** | Accept identity assertions from external IdPs (SAML, OIDC) |
|
|
| **Trust Delegation** | Allow users to delegate limited authority to other users |
|
|
|
|
---
|
|
|
|
## 2. Core Concepts
|
|
|
|
### 2.1 Domains
|
|
|
|
A **domain** is a top-level namespace that contains users, groups, and projects.
|
|
Domains provide administrative isolation: a domain administrator can manage
|
|
users and projects within their domain but not across domains.
|
|
|
|
- Domains were introduced in the Identity API v3 (the "v3" API).
|
|
- Before domains, OpenStack used "tenants" (v2 API) — projects are the v3
|
|
equivalent, but domains add a containment boundary.
|
|
- Every user, group, and project belongs to exactly one domain.
|
|
- The `Default` domain is created automatically and holds all v2-compatible
|
|
resources.
|
|
|
|
**Key property**: Domains are the unit of administrative delegation. A domain
|
|
admin can create/delete users, groups, and projects within their domain.
|
|
|
|
### 2.2 Projects
|
|
|
|
A **project** is a container for resources — compute instances, storage volumes,
|
|
networks, etc. Projects are the primary scope for authorization in OpenStack.
|
|
|
|
- Projects group resources: "who can see/use these VMs and volumes?"
|
|
- Projects belong to a domain.
|
|
- Projects are the primary unit for role assignment and token scoping.
|
|
- Projects can be hierarchical (parent/child) with inherited role assignments.
|
|
|
|
**Key property**: A project-scoped token lets you operate on resources within
|
|
that project. You cannot use a project-scoped token to access resources in a
|
|
different project.
|
|
|
|
### 2.3 Users
|
|
|
|
A **user** represents a digital identity — a person, system account, or service
|
|
account that can authenticate and be authorized.
|
|
|
|
- Users belong to a domain.
|
|
- Users can have multiple authentication methods (password, TOTP, application
|
|
credentials, federated identity).
|
|
- Users can be members of groups.
|
|
- Users receive role assignments on projects, domains, or system scope.
|
|
|
|
### 2.4 Groups
|
|
|
|
A **group** is a named collection of users. Groups simplify role management: you
|
|
assign a role to a group on a project, and every user in the group inherits that
|
|
role.
|
|
|
|
- Groups belong to a domain.
|
|
- Groups are used for role assignment: `group:X → role:member → project:Y`.
|
|
- Federation mappings often resolve external IdP groups to local Keystone groups.
|
|
|
|
### 2.5 Roles
|
|
|
|
A **role** is a named permission set. Roles by themselves don't define what
|
|
operations are allowed — they are labels that policy files map to API operations.
|
|
|
|
- Roles are assigned by binding an actor (user or group) to a target (project,
|
|
domain, or system) with a role.
|
|
- Assignment format: `{actor, role, target}` — e.g., `{user:alice, member,
|
|
project:engineering}`.
|
|
- OpenStack defines default roles: `admin`, `member`, `reader`.
|
|
- Custom roles can be created. Policy files (policy.yaml) map roles to API
|
|
operations.
|
|
- **Implied roles**: one role can imply another (e.g., `admin` implies `member`
|
|
implies `reader`).
|
|
- **Inherited roles**: a role assigned on a domain with `inherited_to_projects`
|
|
flag propagates to all projects within that domain.
|
|
|
|
### 2.6 Endpoints
|
|
|
|
An **endpoint** is a network-accessible URL for an OpenStack service. Each
|
|
service registers one or more endpoints in Keystone's service catalog.
|
|
|
|
- Endpoints have an **interface** type:
|
|
- `public` — for end users (public network)
|
|
- `internal` — for service-to-service communication (internal network)
|
|
- `admin` — for administrative operations (restricted network)
|
|
- Endpoints have a **region** attribute for multi-region deployments.
|
|
- Endpoint URLs can contain template variables like `$(project_id)s` that are
|
|
resolved at token time.
|
|
|
|
### 2.7 Service Catalog
|
|
|
|
The **service catalog** is a registry of all services available in the
|
|
deployment and their endpoints. It is included in token responses and is
|
|
available via `GET /v3/auth/catalog`.
|
|
|
|
- A service has a `type` (e.g., `identity`, `compute`, `object-store`) and a
|
|
`name` (e.g., `keystone`, `nova`, `swift`).
|
|
- The `type` follows the [service-types authority][] — it identifies the API
|
|
contract, not the implementation version.
|
|
- The service catalog in a token is filtered by scope: a project-scoped token
|
|
shows only endpoints relevant to that project.
|
|
- Endpoint filtering allows administrators to restrict which endpoints are
|
|
visible to specific projects via project-endpoint associations or endpoint
|
|
groups.
|
|
|
|
[service-types authority]: https://service-types.openstack.org/
|
|
|
|
**Example service catalog entry:**
|
|
|
|
```json
|
|
{
|
|
"catalog": [
|
|
{
|
|
"name": "Keystone",
|
|
"type": "identity",
|
|
"endpoints": [
|
|
{
|
|
"interface": "public",
|
|
"url": "https://identity.example.com:5000/"
|
|
},
|
|
{
|
|
"interface": "internal",
|
|
"url": "https://identity.internal:5000/"
|
|
},
|
|
{
|
|
"interface": "admin",
|
|
"url": "https://identity.admin:5000/"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Token Lifecycle
|
|
|
|
### 3.1 Token Types by Scope
|
|
|
|
| Token Type | Scope | Contains | Use Case |
|
|
|---|---|---|---|
|
|
| **Unscoped** | None | User identity only, no roles, no catalog | Prove identity for subsequent scoped auth |
|
|
| **Project-scoped** | Project | Roles, catalog, project info | Operate on project resources (VMs, volumes) |
|
|
| **Domain-scoped** | Domain | Roles, catalog, domain info | Manage users/projects within a domain |
|
|
| **System-scoped** | System | Roles, catalog, system info | Cloud-wide admin operations |
|
|
| **Trust-scoped** | Trust | Delegated roles, trust metadata | Act on behalf of another user |
|
|
|
|
### 3.2 Authentication Flow
|
|
|
|
```
|
|
1. Client → POST /v3/auth/tokens (with credentials)
|
|
2. Keystone validates credentials
|
|
3. Keystone issues token:
|
|
- Token ID returned in X-Subject-Token header
|
|
- Token body (JSON) returned in response body
|
|
4. Client uses token: X-Auth-Token: <token_id> on subsequent requests
|
|
5. Services validate token:
|
|
- Option A: Local validation (Fernet/JWS — self-contained)
|
|
- Option B: Call Keystone to validate (UUID tokens)
|
|
```
|
|
|
|
### 3.3 Token Providers
|
|
|
|
| Provider | Format | Persistence | Size | Security |
|
|
|---|---|---|---|---|
|
|
| **Fernet** (default) | AES256-encrypted ciphertext + SHA256 HMAC | None (self-contained) | ~200 bytes | Symmetric keys; only Keystone can decrypt |
|
|
| **JWS** | JSON Web Signature (ES256) | None (self-contained) | ~800 bytes | Asymmetric keys; anyone can verify signature, payload is readable |
|
|
| **UUID** (legacy) | Random UUID string | Database (must be stored) | ~32 bytes | Requires database lookup for validation |
|
|
|
|
**Fernet tokens** are the recommended default. They are:
|
|
- Self-contained: no database persistence needed.
|
|
- Encrypted: the token payload is opaque to clients.
|
|
- Compact: much smaller than JWS tokens.
|
|
- Key rotation: Fernet keys are rotated using `keystone-manage fernet_rotate`.
|
|
|
|
**JWS tokens** are appropriate when:
|
|
- You want asymmetric key verification (services can validate without sharing
|
|
symmetric keys).
|
|
- You're comfortable with the payload being readable by anyone who has the token.
|
|
|
|
### 3.4 Token Contents
|
|
|
|
A project-scoped token contains:
|
|
|
|
```json
|
|
{
|
|
"token": {
|
|
"methods": ["password"],
|
|
"user": {
|
|
"id": "aaa...",
|
|
"name": "alice",
|
|
"domain": { "id": "default", "name": "Default" }
|
|
},
|
|
"project": {
|
|
"id": "bbb...",
|
|
"name": "engineering",
|
|
"domain": { "id": "default", "name": "Default" }
|
|
},
|
|
"roles": [
|
|
{ "id": "ccc...", "name": "member" },
|
|
{ "id": "ddd...", "name": "reader" }
|
|
],
|
|
"catalog": [ ... ],
|
|
"expires_at": "2026-06-08T12:00:00.000000Z",
|
|
"issued_at": "2026-06-08T11:00:00.000000Z",
|
|
"audit_ids": ["eeee..."],
|
|
"is_domain": false
|
|
}
|
|
}
|
|
```
|
|
|
|
Key fields:
|
|
|
|
- `methods`: Authentication methods used (e.g., `["password"]` or
|
|
`["password", "totp"]` for MFA).
|
|
- `user`: Who the token belongs to.
|
|
- `project` / `domain` / `system`: The authorization scope.
|
|
- `roles`: The roles assigned to the user within the scope.
|
|
- `catalog`: Service catalog (absent in unscoped tokens).
|
|
- `expires_at` / `issued_at`: Token validity window.
|
|
- `audit_ids`: Chain of audit IDs for tracking token derivation.
|
|
|
|
### 3.5 Token Validation
|
|
|
|
When a service receives a request with a token:
|
|
|
|
1. Extract `X-Auth-Token` header.
|
|
2. For Fernet tokens: decrypt with local Fernet key, parse payload, verify
|
|
expiration. Check revocation events.
|
|
3. For JWS tokens: verify signature with public key, parse payload, verify
|
|
expiration. Check revocation events.
|
|
4. For UUID tokens: call Keystone to validate. (Deprecated, but still supported.)
|
|
|
|
Keystone middleware (`keystonemiddleware`) handles this automatically for
|
|
OpenStack services.
|
|
|
|
### 3.6 Token Revocation
|
|
|
|
Tokens can be revoked explicitly (`DELETE /v3/auth/tokens`) or implicitly via
|
|
revocation events triggered by:
|
|
|
|
- User account disabled
|
|
- Domain disabled
|
|
- Project disabled
|
|
- Password changed (invalidates all tokens for that user)
|
|
- Role assignment changed (invalidates tokens for the affected scope)
|
|
|
|
Revocation events use pattern matching for efficiency — a single event can
|
|
invalidate many tokens (e.g., all tokens for a user, or all tokens for a project).
|
|
|
|
---
|
|
|
|
## 4. Scoping
|
|
|
|
### 4.1 Unscoped → Scoped Flow
|
|
|
|
The typical authentication flow is two-step:
|
|
|
|
1. **Authenticate** → receive an **unscoped token** (proves identity, no
|
|
authorization).
|
|
2. **Re-authenticate with scope** → receive a **scoped token** (proves identity
|
|
+ authorization).
|
|
|
|
```bash
|
|
# Step 1: Get unscoped token
|
|
curl -X POST /v3/auth/tokens -d '{
|
|
"auth": {
|
|
"identity": {
|
|
"methods": ["password"],
|
|
"password": { "user": { "name": "alice", "password": "..." } }
|
|
}
|
|
}
|
|
}'
|
|
|
|
# Step 2: Get project-scoped token using unscoped token
|
|
curl -X POST /v3/auth/tokens -d '{
|
|
"auth": {
|
|
"identity": {
|
|
"methods": ["token"],
|
|
"token": { "id": "<unscoped_token>" }
|
|
},
|
|
"scope": {
|
|
"project": { "name": "engineering", "domain": { "name": "Default" } }
|
|
}
|
|
}
|
|
}'
|
|
```
|
|
|
|
### 4.2 Scope Types and Authorization
|
|
|
|
| Scope | Token Can Do | Token Cannot Do |
|
|
|---|---|---|
|
|
| **Project** | Operate on project resources (VMs, storage, networks) | Manage domain users, system-wide operations |
|
|
| **Domain** | Manage users/projects within that domain | Operate on project resources (without project scope) |
|
|
| **System** | Cloud-wide admin: manage endpoints, services, hypervisor info | Project-specific resource operations |
|
|
| **None (unscoped)** | Prove identity to Keystone | Access any service resources |
|
|
|
|
A project-scoped token **cannot** be reused in a different project. Each scope
|
|
is a separate token. This is a deliberate security design: token scope limits
|
|
the blast radius of a compromised token.
|
|
|
|
### 4.3 Design Rationale
|
|
|
|
The scoping model exists because:
|
|
|
|
1. **Principle of least privilege**: Users authenticate once (expensive), then
|
|
get narrowly scoped tokens (cheap) for each operation context.
|
|
2. **Multi-tenancy**: A cloud serves many organizations; project scoping
|
|
prevents cross-tenant access.
|
|
3. **Administrative separation**: Domain admins manage users; system admins
|
|
manage infrastructure. Different scopes for different jobs.
|
|
|
|
---
|
|
|
|
## 5. Role-Based Access Control (RBAC)
|
|
|
|
### 5.1 Role Assignments
|
|
|
|
A role assignment binds an **actor** (user or group) to a **role** on a
|
|
**target** (project, domain, or system).
|
|
|
|
The four assignment types:
|
|
|
|
| Assignment | Actor | Target | Example |
|
|
|---|---|---|---|
|
|
| User → Project | User | Project | Alice is `member` of `engineering` |
|
|
| Group → Project | Group | Project | `dev-team` group is `member` of `engineering` |
|
|
| User → Domain | User | Domain | Alice is `admin` of `acme-domain` |
|
|
| Group → Domain | Group | Domain | `ops-team` group is `admin` of `acme-domain` |
|
|
|
|
Plus **system** role assignments for cloud-wide operations.
|
|
|
|
### 5.2 Effective Role Assignments
|
|
|
|
When querying role assignments with `effective=True`, Keystone resolves:
|
|
|
|
1. **Direct assignments**: Roles explicitly granted.
|
|
2. **Group memberships**: Roles inherited from groups the user belongs to.
|
|
3. **Inherited roles**: Roles from parent projects or domains (via
|
|
`inherited_to_projects` flag).
|
|
4. **Implied roles**: Roles implied by other roles (e.g., `admin` → `member`
|
|
→ `reader`).
|
|
|
|
### 5.3 Policy Enforcement
|
|
|
|
Keystone uses `oslo.policy` for policy enforcement. Each OpenStack service
|
|
defines policy rules in `policy.yaml` files. A rule maps an API operation to a
|
|
check string:
|
|
|
|
```yaml
|
|
"identity:create_project": "role:admin and domain_id:%(target.domain.id)s"
|
|
"identity:list_projects": "role:reader"
|
|
"identity:update_project": "role:admin or project_id:%(target.project.id)s"
|
|
```
|
|
|
|
Policy rules can check:
|
|
|
|
- Role membership (`role:admin`)
|
|
- Scope type (`system_scope:all`, `domain_id:...`)
|
|
- Resource ownership (`user_id:%(target.user.id)s`)
|
|
- Arbitrary target attributes
|
|
|
|
### 5.4 Scope Enforcement in Policy
|
|
|
|
Since the Rocky release, policies can require specific token scopes:
|
|
|
|
```yaml
|
|
# System-scoped token required
|
|
"identity:list_projects": "role:reader and system_scope:all"
|
|
|
|
# Project-scoped token required
|
|
"nova:create_server": "role:member and project_id:%(target.project.id)s"
|
|
```
|
|
|
|
This prevents:
|
|
- Using a project-scoped token for system operations.
|
|
- Using a system-scoped token for project operations (without a project context).
|
|
|
|
---
|
|
|
|
## 6. Trust Delegation (OS-TRUST)
|
|
|
|
### 6.1 Overview
|
|
|
|
Trusts allow one user (**trustor**) to delegate a subset of their authority to
|
|
another user (**trustee**) for a limited scope and duration, without sharing
|
|
credentials.
|
|
|
|
**Key properties of a trust:**
|
|
|
|
| Property | Description |
|
|
|---|---|
|
|
| `trustor_user_id` | User creating the trust (delegating authority) |
|
|
| `trustee_user_id` | User receiving the delegation |
|
|
| `project_id` | Project scope for the delegated authority |
|
|
| `roles` | Subset of trustor's roles being delegated |
|
|
| `impersonation` | If `true`, tokens appear to come from the trustor |
|
|
| `expires_at` | Optional expiration timestamp |
|
|
| `remaining_uses` | Optional limit on how many tokens can be created from this trust |
|
|
| `allow_redelegation` | Whether the trustee can create sub-trusts |
|
|
| `redelegation_count` | Maximum depth of redelegation chain |
|
|
|
|
### 6.2 Trust-Scoped Tokens
|
|
|
|
When a trustee authenticates using a trust:
|
|
|
|
1. The trustee authenticates with their own credentials.
|
|
2. They specify `trust_id` in the auth request.
|
|
3. Keystone issues a **trust-scoped token** with:
|
|
- Roles: the intersection of the trust's roles and the trustor's current
|
|
roles (if trustor lost a role, the trust is invalidated).
|
|
- `OS-TRUST:trust` section in the token body containing trust metadata.
|
|
|
|
If `impersonation=true`, the token's `user` field shows the trustor — the
|
|
trustee acts as the trustor. If `impersonation=false`, the token's `user`
|
|
field shows the trustee.
|
|
|
|
### 6.3 Trust Delegation Chains
|
|
|
|
Trusts support **redelegation**: a trustee can create a new trust delegating to
|
|
a third party. This creates a trust chain:
|
|
|
|
```
|
|
Trustor → Trust(A) → Trustee1
|
|
Trustee1 → Trust(B) → Trustee2 (redelegation)
|
|
```
|
|
|
|
Delegation depth is controlled by:
|
|
|
|
- `allow_redelegation: true/false`
|
|
- `redelegation_count: N` (decremented on each redelegation; default max is 3)
|
|
|
|
**Security constraints:**
|
|
|
|
- The redelegated trust's roles must be a subset of the original trustor's
|
|
roles (not the intermediate trustee's).
|
|
- If `impersonation=false` in the source trust, the redelegated trust cannot
|
|
set `impersonation=true`.
|
|
- Application credentials cannot create or delete trusts (prevents automated
|
|
escalation chains).
|
|
|
|
### 6.4 Automatic Trust Revocation
|
|
|
|
Trusts are automatically revoked (soft-deleted) when:
|
|
|
|
- The trustor is deleted.
|
|
- The trustee is deleted.
|
|
- The project is deleted.
|
|
- The trust expires (`expires_at`).
|
|
- The remaining uses are exhausted (`remaining_uses` reaches 0).
|
|
- The trustor loses a role that was delegated in the trust.
|
|
|
|
---
|
|
|
|
## 7. Application Credentials
|
|
|
|
### 7.1 Overview
|
|
|
|
Application credentials allow users to create long-lived, restricted credentials
|
|
for applications without exposing their password. This is especially important
|
|
for users whose identity comes from LDAP or SSO — applications can't use their
|
|
password.
|
|
|
|
**Key properties:**
|
|
|
|
| Property | Description |
|
|
|---|---|
|
|
| `name` | Unique name within the user's application credentials |
|
|
| `secret` | Auto-generated or user-provided secret (hashed on storage, shown once) |
|
|
| `project_id` | Project scope (always the user's current project) |
|
|
| `roles` | Subset of the user's roles on the project (cannot exceed user's roles) |
|
|
| `expires_at` | Optional expiration timestamp |
|
|
| `unrestricted` | `false` by default — restricted from creating/deleting other app creds and trusts |
|
|
|
|
### 7.2 Authentication with Application Credentials
|
|
|
|
```bash
|
|
# Auth with application credential ID + secret
|
|
curl -X POST /v3/auth/tokens -d '{
|
|
"auth": {
|
|
"identity": {
|
|
"methods": ["application_credential"],
|
|
"application_credential": {
|
|
"id": "aa809205ed614a0e854bac92c0768bb9",
|
|
"secret": "oKce6DOC_WcZoE13l3eX..."
|
|
}
|
|
}
|
|
}
|
|
}'
|
|
```
|
|
|
|
Or by name + user:
|
|
|
|
```bash
|
|
"application_credential": {
|
|
"name": "monitoring",
|
|
"user": { "name": "glance", "domain": { "name": "Default" } },
|
|
"secret": "securesecret"
|
|
}
|
|
```
|
|
|
|
### 7.3 Restriction Model
|
|
|
|
By default (`unrestricted=false`), application credentials **cannot**:
|
|
|
|
- Create or delete other application credentials.
|
|
- Create or delete trusts.
|
|
- List other application credentials.
|
|
|
|
This prevents a compromised app credential from regenerating itself or escalating
|
|
privileges. Setting `unrestricted=true` removes these restrictions, but adds
|
|
risk.
|
|
|
|
### 7.4 Rotation
|
|
|
|
Application credentials support **zero-downtime rotation**:
|
|
|
|
1. Create a new application credential (names must be unique per user).
|
|
2. Update the application configuration with the new ID/secret.
|
|
3. Delete the old application credential.
|
|
|
|
Multiple application credentials can coexist for the same user+project,
|
|
enabling seamless transitions.
|
|
|
|
### 7.5 Invalidation
|
|
|
|
Application credentials are automatically invalidated when:
|
|
|
|
- The user is deleted or disabled.
|
|
- The user's role assignment on the project changes (roles are checked at
|
|
auth time against the user's current roles).
|
|
- The project is deleted or disabled.
|
|
- The credential expires (`expires_at`).
|
|
- The credential is explicitly deleted.
|
|
|
|
---
|
|
|
|
## 8. Federation
|
|
|
|
### 8.1 Overview
|
|
|
|
Keystone's federation module allows external Identity Providers (IdPs) to
|
|
authenticate users, with Keystone acting as a Service Provider (SP). Keystone
|
|
maps the external identity to local users, groups, and roles.
|
|
|
|
**Supported protocols:**
|
|
|
|
| Protocol | Module | Use Case |
|
|
|---|---|---|
|
|
| **SAML 2.0** | mod_shib / mod_auth_mellon | Enterprise SSO |
|
|
| **OpenID Connect** | mod_auth_openidc | OAuth2/OIDC providers (Google, Keycloak, Okta) |
|
|
| **Mapped** | Custom auth module | Any HTTP auth module |
|
|
| **K2K** | Keystone-to-Keystone | Multi-cloud federation between OpenStack deployments |
|
|
|
|
### 8.2 Federation Architecture
|
|
|
|
```
|
|
┌──────────────────┐
|
|
│ External IdP │
|
|
│ (SAML/OIDC/...) │
|
|
└────────┬────────┘
|
|
│
|
|
SAML assertion or
|
|
OIDC claims
|
|
│
|
|
▼
|
|
┌──────────┐ HTTPD auth module ┌───────────────┐
|
|
│ Browser │ ───────────────────────▶│ Apache/Nginx │
|
|
│ or CLI │ (mod_shib / │ + auth module │
|
|
└──────────┘ mod_auth_openidc) └───────┬────────┘
|
|
│
|
|
REMOTE_USER header
|
|
+ other attributes
|
|
│
|
|
▼
|
|
┌──────────────────┐
|
|
│ Keystone │
|
|
│ (SP) │
|
|
│ │
|
|
│ 1. Lookup IdP │
|
|
│ 2. Apply mapping│
|
|
│ │ remote attrs │
|
|
│ │ → local user,│
|
|
│ │ groups, │
|
|
│ │ roles │
|
|
│ 3. Issue token │
|
|
└──────────────────┘
|
|
```
|
|
|
|
### 8.3 Key Federation Components
|
|
|
|
1. **Identity Provider** object — represents the external IdP in Keystone.
|
|
Has `remote_ids` (entity IDs) that Keystone uses to match incoming
|
|
requests.
|
|
|
|
2. **Mapping** — a set of rules that transform attributes from the external IdP
|
|
into Keystone-local user properties and group memberships. Mappings can:
|
|
- Map remote users to local users (by name, email, or other attributes).
|
|
- Assign users to local groups (inherit group role assignments).
|
|
- Dynamically create projects based on remote attributes.
|
|
- Support complex condition logic.
|
|
|
|
3. **Protocol** — links an Identity Provider to a Mapping. Supported values:
|
|
`saml2`, `openid`, `mapped`, or custom.
|
|
|
|
4. **Mapping rule example:**
|
|
|
|
```json
|
|
[{
|
|
"local": [{
|
|
"user": { "name": "{0}" },
|
|
"group": { "domain": { "name": "Default" }, "name": "federated_users" }
|
|
}],
|
|
"remote": [{ "type": "REMOTE_USER" }]
|
|
}]
|
|
```
|
|
|
|
This maps all authenticated external users to a local user (named by the
|
|
`REMOTE_USER` attribute) and adds them to the `federated_users` group.
|
|
|
|
### 8.4 Federation Token Flow
|
|
|
|
1. User authenticates with the external IdP.
|
|
2. The HTTPD auth module (Apache/Nginx) validates the assertion and sets
|
|
`REMOTE_USER` and other headers.
|
|
3. Keystone receives the request at `/v3/OS-FEDERATION/identity_providers/{idp}/protocols/{protocol}/auth`.
|
|
4. Keystone applies the mapping rules to produce a local user + groups + roles.
|
|
5. Keystone issues a **federated unscoped token**.
|
|
6. The user can then exchange it for a scoped token (project, domain, or
|
|
system) just like any other unscoped token.
|
|
|
|
### 8.5 Identity Provider (Keystone as IdP)
|
|
|
|
Keystone can also act as an **Identity Provider** (SAML IdP), allowing it to
|
|
authenticate users from other OpenStack deployments (K2K federation) or other
|
|
SAML SPs.
|
|
|
|
---
|
|
|
|
## 9. Service Catalog Deep Dive
|
|
|
|
### 9.1 Service Registration
|
|
|
|
Services are registered with Keystone via the API:
|
|
|
|
```bash
|
|
openstack service create --name nova --description "Compute" compute
|
|
openstack endpoint create --region RegionOne compute public https://nova.example.com:8774/
|
|
openstack endpoint create --region RegionOne compute internal https://nova.internal:8774/
|
|
openstack endpoint create --region RegionOne compute admin https://nova.admin:8774/
|
|
```
|
|
|
|
### 9.2 Catalog Filtering
|
|
|
|
The catalog returned in a token is filtered by:
|
|
|
|
1. **Scope**: A project-scoped token includes endpoints filtered by
|
|
project-endpoint associations.
|
|
2. **Endpoint groups**: Admins can define endpoint groups (filtered by service
|
|
type, region, or interface) and associate them with projects.
|
|
3. **Enabled/disabled**: Disabled services and endpoints don't appear in the
|
|
catalog.
|
|
4. **Interface visibility**: `public`, `internal`, and `admin` endpoints serve
|
|
different audiences.
|
|
|
|
### 9.3 URL Templating
|
|
|
|
Endpoint URLs support template variables:
|
|
|
|
- `$(project_id)s` — replaced with the token's project ID
|
|
- `$(user_id)s` — replaced with the token's user ID
|
|
|
|
Example:
|
|
|
|
```
|
|
https://object-store.example.com/v1/KEY_$(project_id)s
|
|
```
|
|
|
|
When a project-scoped token is issued, the catalog resolves this to:
|
|
|
|
```
|
|
https://object-store.example.com/v1/KEY_d12af07f4e2c4390a21acc31517ebec9
|
|
```
|
|
|
|
### 9.4 Client Discovery
|
|
|
|
An OpenStack client authenticates with Keystone, receives a token (which
|
|
includes the service catalog), and then uses the catalog to discover the URL
|
|
for any service it needs:
|
|
|
|
```python
|
|
# After authentication, the catalog is in the token response:
|
|
for service in token['catalog']:
|
|
if service['type'] == 'compute':
|
|
for endpoint in service['endpoints']:
|
|
if endpoint['interface'] == 'public':
|
|
nova_url = endpoint['url']
|
|
break
|
|
```
|
|
|
|
This is how every OpenStack client discovers service endpoints — they never
|
|
hardcode URLs. They authenticate once, get the catalog, and dynamically route
|
|
to the correct endpoint.
|
|
|
|
---
|
|
|
|
## 10. Mapping to alknet Concepts
|
|
|
|
### 10.1 Concept Comparison Table
|
|
|
|
| Keystone Concept | alknet Concept | Notes |
|
|
|---|---|---|
|
|
| Domain | (Not directly mapped) | alknet is single-tenant/small-team focused; no need for domain-level admin boundaries yet |
|
|
| Project | `Identity.resources` | Projects scope resources; alknet's `resources: HashMap<String, Vec<String>>` serves a similar scoping purpose |
|
|
| User | `Identity.id` | Keystone users ↔ alknet identities (fingerprint or UUID) |
|
|
| Group | (Not directly mapped) | Could be added via `Identity.scopes` patterns or a groups concept in alknet-storage |
|
|
| Role | `Identity.scopes` | Keystone roles map to alknet scopes: `["relay:connect", "service:gitea:read"]` ≈ role assignments |
|
|
| Token (scoped) | `AuthToken` + scoped permissions | alknet's AuthToken proves identity + timestamp; scopes come from IdentityProvider lookup |
|
|
| Service Catalog | `OperationRegistry` + OpenAPI spec generation | Both solve service discovery; Keystone is runtime API catalog, alknet generates from OpenAPI |
|
|
| Trust Delegation | (Potential future model) | alknet doesn't have delegation yet; trust model could inspire future `DelegationToken` |
|
|
| Application Credentials | API keys in `api_keys` table | alknet's `api_keys` table parallels app creds: long-lived, scoped, user-bound |
|
|
| Federation (SAML/OIDC) | Phase D OIDC provider aspiration | alknet wants to *be* an OIDC provider; Keystone consumes external IdPs |
|
|
| Service Endpoint | (Implicit in OperationEnv) | alknet operations are discovered via registry, not external endpoint lookup |
|
|
| Policy (policy.yaml) | `ForwardingPolicy` + call protocol ACL | Both enforce "who can do what where"; alknet is code-based, not YAML-configured |
|
|
|
|
### 10.2 What to Adopt from Keystone
|
|
|
|
#### 10.2.1 Scoped Tokens (Strong Adopt)
|
|
|
|
**Keystone pattern**: Unscoped → project/domain/system scoped token flow.
|
|
|
|
**alknet application**: Currently, `AuthToken` proves identity with a timestamp.
|
|
`Identity.scopes` and `Identity.resources` are resolved *after* token
|
|
verification by `IdentityProvider`. This is analogous to Keystone's flow:
|
|
|
|
| Keystone | alknet |
|
|
|---|---|
|
|
| Unscoped token (identity only) | AuthToken (proves key possession + timestamp) |
|
|
| Scoped token (identity + roles + catalog) | Identity (resolved by IdentityProvider with scopes + resources) |
|
|
| Re-auth with scope | Not needed — alknet scopes come from the `IdentityProvider` lookup |
|
|
|
|
**Recommendation**: alknet's current model is already similar to Keystone's, but
|
|
more streamlined. alknet doesn't need a separate "re-auth with scope" step
|
|
because the `IdentityProvider` resolution *is* the scoping step. However,
|
|
consider adding explicit scope fields to the token in the future for
|
|
multi-tenant deployments.
|
|
|
|
#### 10.2.2 Service Catalog Pattern (Strong Adopt)
|
|
|
|
**Keystone pattern**: Services register endpoints; clients discover them from
|
|
the token/catalog.
|
|
|
|
**alknet application**: The `OperationRegistry` + `OpenAPIServiceRegistry`
|
|
serves a similar purpose:
|
|
|
|
- Keystone: `POST /v3/auth/tokens` → response includes catalog of services
|
|
and URLs.
|
|
- alknet: `OperationRegistry` knows all available operations; `FromOpenAPI`
|
|
generates them from specs.
|
|
|
|
**Key difference**: In Keystone, the catalog is returned *with the token* and
|
|
is dynamic (filtered by project scope). In alknet, the registry is built at
|
|
startup from configuration, and access control is enforced per-operation in the
|
|
call protocol.
|
|
|
|
**Recommendation**: Consider adding a "service discovery" operation to the
|
|
call protocol — a way for clients to ask "what operations are available to me?"
|
|
This would be analogous to Keystone's `GET /v3/auth/catalog`.
|
|
|
|
#### 10.2.3 Role Hierarchies and Implied Roles (Moderate Adopt)
|
|
|
|
**Keystone pattern**: Roles can imply other roles (`admin` → `member` →
|
|
`reader`). Role assignments on domains propagate to projects via inheritance.
|
|
|
|
**alknet application**: Currently, alknet's scopes are flat strings. Consider:
|
|
|
|
```
|
|
admin:service:* → implies → member:service:* → implies → reader:service:*
|
|
```
|
|
|
|
This would simplify scope assignment in the `IdentityProvider`: grant `admin:service:*`
|
|
and automatically get `member` and `reader` permissions.
|
|
|
|
**Recommendation**: Implement implied scopes as a Phase 2+ feature when
|
|
alknet-storage adds the ACL graph. Don't over-engineer in Phase 1.
|
|
|
|
#### 10.2.4 Application Credentials (Strong Adopt — alreded parallels)
|
|
|
|
**Keystone pattern**: Password-less auth with restricted capabilities, tied to a
|
|
user and project, with expiration and rotation support.
|
|
|
|
**alknet application**: The `api_keys` table in alknet-storage is exactly this:
|
|
|
|
| Keystone App Credential | alknet API Key |
|
|
|---|---|
|
|
| `id` + `secret` | `key_prefix` + `key_hash` |
|
|
| `roles` (subset of user's roles) | `scopes` (subset of account's scopes) |
|
|
| `project_id` (scope) | Account-scoped |
|
|
| `expires_at` | `expires_at` |
|
|
| `unrestricted` | (not yet implemented) |
|
|
| Rotation via create-new-then-delete | (not yet implemented) |
|
|
|
|
**Recommendation**: Add the `unrestricted` concept to API keys — by default,
|
|
API keys should NOT be able to create or delete other API keys or modify
|
|
account settings. Also add rotation support (create new key, update config,
|
|
delete old key).
|
|
|
|
#### 10.2.5 Trust Delegation (Future Consideration)
|
|
|
|
**Keystone pattern**: Trustor delegates limited authority to trustee with
|
|
impersonation, expiration, usage limits, and redelegation chains.
|
|
|
|
**alknet application**: alknet doesn't have this yet, but it could be useful
|
|
for:
|
|
|
|
- **Service-to-service auth**: An alknet node delegates limited authority to a
|
|
service wrapper (e.g., "let the rustfs wrapper access S3 on my behalf for 1
|
|
hour").
|
|
- **Temporary access grants**: "Give Alice access to the `engineering` scope
|
|
for 24 hours."
|
|
- **Impersonation for audit**: Trusted services acting on behalf of a user,
|
|
with the user's identity appearing in audit logs.
|
|
|
|
**Recommendation**: Design a `DelegationToken` or `Trust` model when
|
|
alknet-storage is built. The trust model — trustor, trustee, roles, expiration,
|
|
remaining_uses — is a good template.
|
|
|
|
#### 10.2.6 Federation (Phase D Alignment)
|
|
|
|
**Keystone pattern**: External IdPs (SAML, OIDC) authenticate users; Keystone
|
|
maps them to local identities via mapping rules.
|
|
|
|
**alknet application**: Phase D of `credential-provider.md` envisions alknet
|
|
*as* an OIDC provider for self-hosted services. This is the **inverse** of
|
|
Keystone's federation model:
|
|
|
|
- Keystone: external IdP → Keystone (SP) → local identity
|
|
- alknet Phase D: alknet (IdP) → rustfs/gitea (SP) → local identity on self-hosted service
|
|
|
|
**Key learning from Keystone's federation model**:
|
|
|
|
1. **Mapping rules** are critical. Keystone's mapping engine (`local` ← `remote`)
|
|
is how IdP attributes become local roles. alknet will need the inverse:
|
|
`Identity.scopes` → OIDC claims → rustfs/gitea policies.
|
|
2. **Group membership from federation** is temporary by default (valid for
|
|
token lifetime). alknet should consider whether federated identities are
|
|
permanent or session-scoped.
|
|
3. **Multiple IdP support**: Keystone can consume from multiple external IdPs.
|
|
alknet Phase D should support multiple SPs (multiple self-hosted services)
|
|
consuming from one alknet IdP.
|
|
|
|
**Recommendation**: When building Phase D, study Keystone's mapping rule
|
|
format. alknet will need a similar concept: `alknet.scope → oidc.claim →
|
|
service.policy`. This could be part of the `CredentialProvider` or a new
|
|
`IdentityMappingProvider`.
|
|
|
|
### 10.3 What NOT to Adopt from Keystone
|
|
|
|
#### 10.3.1 Domains (Not Needed)
|
|
|
|
Keystone's domain model is designed for multi-tenant cloud hosting where
|
|
different organizations share the same OpenStack deployment. alknet is designed
|
|
for self-hosted, single-organization or small-team deployments. The domain
|
|
concept adds complexity that doesn't justify itself in alknet's use case.
|
|
|
|
alknet's `Identity.resources` already provides a lightweight scoping mechanism
|
|
that covers the "which resources does this identity have access to" use case
|
|
without the overhead of a domain hierarchy.
|
|
|
|
#### 10.3.2 Separate Policy Engine (Over-Engineering)
|
|
|
|
Keystone's `oslo.policy` is a full YAML-based policy engine with complex rule
|
|
combinations (`role:admin AND domain_id:%(target.domain.id)s OR
|
|
project_id:%(target.project.id)s`). alknet's authorization model is
|
|
programmatic (Rust code in `ForwardingPolicy` and call protocol handlers), not
|
|
configured via YAML. This is appropriate for alknet's size and complexity.
|
|
|
|
**If** alknet needs configurable policies in the future (e.g., admin-editable
|
|
ACL rules stored in the database), a simple rule engine would suffice — not the
|
|
full oslo.policy model.
|
|
|
|
#### 10.3.3 Multiple Token/Scope Types (Unnecessary Complexity)
|
|
|
|
Keystone has separate token types for project/domain/system scope. alknet's
|
|
`AuthToken` is already simpler: it proves identity + timestamp, and the
|
|
`IdentityProvider` resolves scopes. There's no need for alknet to issue
|
|
different token types for different scopes.
|
|
|
|
If multi-tenancy is added in the future, the `Identity.resources` map can
|
|
encode project equivalents without needing a separate token type.
|
|
|
|
#### 10.3.3 Service Endpoint Registration (Unnecessary)
|
|
|
|
Keystone requires every service to register its endpoints in the catalog
|
|
before it can be discovered. alknet services are registered programmatically
|
|
(via `OperationRegistry::register()`) at startup, not via a central API. The
|
|
`OperationRegistry` is built from configuration and OpenAPI specs, not from a
|
|
catalog service.
|
|
|
|
This is appropriate for alknet's architecture: services are known at deploy
|
|
time, not dynamically registered. If dynamic service discovery is needed later,
|
|
a simple registry operation in the call protocol would suffice.
|
|
|
|
---
|
|
|
|
## 11. Summary of Recommendations
|
|
|
|
| Keystone Concept | Adoption Level | alknet Implementation |
|
|
|---|---|---|
|
|
| **Scoped tokens** | ✅ Strong Adopt | Already present in IdentityProvider resolution (AuthToken → Identity with scopes/resources) |
|
|
| **Service catalog** | ✅ Strong Adopt | `OperationRegistry` + `FromOpenAPI`; consider adding "list operations" discovery |
|
|
| **Application credentials** | ✅ Strong Adopt | `api_keys` table parallels exactly; add `unrestricted` flag and rotation support |
|
|
| **Role hierarchies / implied roles** | ⚡ Moderate | Implied scope hierarchies in Phase 2+ when ACL graph is built |
|
|
| **Trust delegation** | ⚡ Moderate | Design `DelegationToken` model for service-to-service and temporary access in Phase 2+ |
|
|
| **Federation mapping** | ⚡ Moderate | Phase D: adopt `scope → claim → policy` mapping pattern for OIDC provider |
|
|
| **Token revocation events** | ⚡ Moderate | Consider pattern-matching revocation for efficiency when alknet-storage supports it |
|
|
| **Domains** | ❌ Skip | alknet is self-hosted/small-team; `Identity.resources` provides lightweight scoping |
|
|
| **oslo.policy (YAML-based)** | ❌ Skip | alknet uses programmatic auth (Rust code); add simple rule engine only if needed |
|
|
| **Multiple token types** | ❌ Skip | One token type with scope resolution via `IdentityProvider` is sufficient |
|
|
| **Endpoint registration API** | ❌ Skip | `OperationRegistry` is configured at startup, not via a catalog API |
|
|
|
|
---
|
|
|
|
## 12. References
|
|
|
|
- [Keystone Architecture — OpenStack Docs](https://docs.openstack.org/keystone/2024.2/getting-started/architecture.html)
|
|
- [Keystone Tokens Overview](https://docs.openstack.org/keystone/latest/admin/tokens-overview.html)
|
|
- [Keystone Service Catalog Overview](https://docs.openstack.org/keystone/latest/contributor/service-catalog.html)
|
|
- [Keystone Trusts Documentation](https://docs.openstack.org/keystone/latest/user/trusts.html)
|
|
- [Keystone Application Credentials](https://docs.openstack.org/keystone/queens/user/application_credentials.html)
|
|
- [Keystone Federation Configuration](https://docs.openstack.org/keystone/latest/admin/federation/configure_federation.html)
|
|
- [Keystone RBAC and Authorization — DeepWiki](https://deepwiki.com/openstack/keystone/4-authorization-and-access-control)
|
|
- [Keystone Authentication and Token Management — DeepWiki](https://deepwiki.com/openstack/keystone/3-authentication-and-token-management)
|
|
- [Keystone Trust Delegation — DeepWiki](https://deepwiki.com/openstack/keystone/4.4-trust-delegation)
|
|
- [Keystone Service Catalog — DeepWiki](https://deepwiki.com/openstack/keystone/5.4-service-catalog)
|
|
- [Keystone Token Revocation — DeepWiki](https://deepwiki.com/openstack/keystone/3.4-token-revocation)
|
|
- [Understanding OpenStack Keystone: Scoped vs. Unscoped Tokens](https://osie.io/blog/understanding-openstack-keystone-scoped-vs-unscoped-tokens)
|
|
- [Trust Delegation in OpenStack Using Keystone Trusts](https://blog.zhaw.ch/icclab/trust-delegation-in-openstack-using-keystone-trusts/)
|
|
- [OpenStack Knowledge: Keystone Federation](https://github.com/stackers-network/openstack-knowledge/blob/main/core/identity/federation.md)
|
|
- [alknet identity.md](../../architecture/identity.md)
|
|
- [alknet auth.md](../../architecture/auth.md)
|
|
- [alknet credential-provider.md](../phase2/credential-provider.md) |