Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions doc/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
# OpenStack Keystone

[Introduction](./intro.md)
[Introduction](./intro.md)

[Installation](./install.md)

---

# Keystone internals

* [Architecture](./architecture.md)
* [Architecture decision records](adr/index.md)
* [Record architecture decisions](adr/0001-record-architecture-decisions.md)
* [Open Policy Agent](adr/0002-open-policy-agent.md)
* [Sea ORM](adr/0003-sea-orm.md)
* [v4 API](adr/0004-v4-api.md)
* [Passkey Auth](adr/0005-auth-passkey.md)
* [Federation IDP](adr/0006-federation-idp.md)
* [Federation Mapping](adr/0007-federation-mapping.md)
* [Workload Federation](adr/0008-federation-workload.md)
* [Auth token revocation](adr/0009-auth-token-revoke.md)
* [PCI-DSS: Failed Auth Protection](adr/0010-pci-dss-failed-auth-protection.md)
* [PCI-DSS: Inactive Account Deactivation](adr/0011-pci-dss-inactive-account-deactivation.md)
* [PCI-DSS: Account Password Expiration](adr/0012-pci-dss-account-password-expiry.md)
* [Policy enforcement](./policy.md)
* [Fernet token]()
* [Token payloads]()
- [Architecture](./architecture.md)
- [Architecture decision records](adr/index.md)
- [Record architecture decisions](adr/0001-record-architecture-decisions.md)
- [Open Policy Agent](adr/0002-open-policy-agent.md)
- [Sea ORM](adr/0003-sea-orm.md)
- [v4 API](adr/0004-v4-api.md)
- [Passkey Auth](adr/0005-auth-passkey.md)
- [Federation IDP](adr/0006-federation-idp.md)
- [Federation Mapping](adr/0007-federation-mapping.md)
- [Workload Federation](adr/0008-federation-workload.md)
- [Auth token revocation](adr/0009-auth-token-revoke.md)
- [PCI-DSS: Failed Auth Protection](adr/0010-pci-dss-failed-auth-protection.md)
- [PCI-DSS: Inactive Account Deactivation](adr/0011-pci-dss-inactive-account-deactivation.md)
- [PCI-DSS: Account Password Expiration](adr/0012-pci-dss-account-password-expiry.md)
- [Policy enforcement](./policy.md)
- [Fernet token]()
- [Token payloads]()

---

Expand Down
6 changes: 4 additions & 2 deletions doc/src/adr/0001-record-architecture-decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ We need to record the architectural decisions made on this project.

## Decision

We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
We will use Architecture Decision Records, as
[described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).

## Consequences

See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see
Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
14 changes: 7 additions & 7 deletions doc/src/adr/0002-open-policy-agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ Accepted
## Context

Use of oslo.policy is not easily possible from Rust. In addition to that during
the OpenStack Summit 2025 it [was
shown](https://www.youtube.com/watch?v=_B4Zsd8RG88&list=PLKqaoAnDyfgr91wN_12nwY321504Ctw1s&index=33)
the OpenStack Summit 2025 it
[was shown](https://www.youtube.com/watch?v=_B4Zsd8RG88&list=PLKqaoAnDyfgr91wN_12nwY321504Ctw1s&index=33)
how Open Policy Agent can be used to further improve the policy control in
OpenStack. As such the Keystone implement the policy enforcement using the OPA
with the following rules:

1. `List` operation MUST receive the all query parameters of the operation in the
target.
1. `List` operation MUST receive the all query parameters of the operation in
the target.

2. For `Show` operation the policy MUST receive the current record as the target
(fetch the record and pass it into the policy engine).

3. `Update` operation MUST receive current and new state of the resource (first
the current resource is fetched and passed together with the new state
[current, target] to the policy engine).
[current, target] to the policy engine).

4. `Create` operation works similarly as current oslo.policy with the desired
state passed to the policy engine.
Expand All @@ -40,5 +40,5 @@ Engine.

- Policy evaluation requires external service (OPA) to be running.

- When covering existing functionality of the python Keystone policies SHOULD
be converted as is and do not introduce a changed flow.
- When covering existing functionality of the python Keystone policies SHOULD be
converted as is and do not introduce a changed flow.
4 changes: 2 additions & 2 deletions doc/src/adr/0004-v4-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ v4 API version should be introduced.
- New extended functionality will not be available in the v3.

- Certain necessary changes may be ported to the v3 (including python) to
implement backwards compatibility. Example: acknowledge new token payload issued
with v4 with the python Keystone.
implement backwards compatibility. Example: acknowledge new token payload
issued with v4 with the python Keystone.
8 changes: 4 additions & 4 deletions doc/src/adr/0005-auth-passkey.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ authenticators.
to the standard.

- User should be able to request the desired scope in the authentication
initialization request. In this case a scoped token is returned when user has
the required access.
initialization request. In this case a scoped token is returned when user has
the required access.

- To prevent attacks authentication requests for not existing users or users
without registered authenticators MUST return fake (but valid) authentication
state.
without registered authenticators MUST return fake (but valid) authentication
state.

## Consequences

Expand Down
8 changes: 4 additions & 4 deletions doc/src/adr/0007-federation-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ for Keystone.

## Decision

"Mapping" (attribute mapping) MUST describe how the information from OIDC
claims need to be translated into the Keystone data model. It MUST also describe
user defined bounds to allow use restriction.
"Mapping" (attribute mapping) MUST describe how the information from OIDC claims
need to be translated into the Keystone data model. It MUST also describe user
defined bounds to allow use restriction.

When `domain_id` is not being set on the IdP level it MUST be defined either on
the mapping entry, or the mapping MUST define `domain_id_claim` to extract the
Expand All @@ -37,4 +37,4 @@ used.
## Consequences

- Mappings MUST be configured carefully to prevent login of users across the
domain borders. `bound_xxx` should be used extensively to guard this.
domain borders. `bound_xxx` should be used extensively to guard this.
20 changes: 10 additions & 10 deletions doc/src/adr/0008-federation-workload.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ workflow, Zuul job, etc). Usually such services provide a JWT issued by the
platform which the service provider can trust. This is very similar (and
technically relates) to the OIDC standard.

In the JWT flow the "user" is exchanging a JWT token issued by the trusted
IdP for a Keystone token. This authentication response includes a token and a
In the JWT flow the "user" is exchanging a JWT token issued by the trusted IdP
for a Keystone token. This authentication response includes a token and a
service catalog to provide a known OpenStack usage scenario.

## Decision

OIDC mappings MUST specify a `type` which is `oidc` or `jwt` to specify the
flow they define. A `jwt` type mapping can be only used in the JWT flow.
OIDC mappings MUST specify a `type` which is `oidc` or `jwt` to specify the flow
they define. A `jwt` type mapping can be only used in the JWT flow.

The new authentication API includes the IdP ID. The authentication request does
not support the Json request body and uses a generic `authorization: bearer
<jwt>` header and `openstack-mapping-name: <mapping_name>` to request the
information. Depending on the mapping configuration the desired authorization
scope is returned. The flow does not support explicitly requesting the scope
beyond what is described by the mapping.

not support the Json request body and uses a generic
`authorization: bearer <jwt>` header and
`openstack-mapping-name: <mapping_name>` to request the information. Depending
on the mapping configuration the desired authorization scope is returned. The
flow does not support explicitly requesting the scope beyond what is described
by the mapping.

## Consequences

Expand Down
23 changes: 13 additions & 10 deletions doc/src/adr/0009-auth-token-revoke.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,22 @@ Following conditions are combined with the AND condition:
database record. When this list is empty an error is being returned.
- `token.project_id` is compared against the database record when present.
- `token.user_id` is compared against the database record when present.
- `token.trustor_id` is compared against the database record `user_id` when present.
- `token.trustee_id` is compared against the database record `user_id` when present.
- `token.trust_id` is compared against the database record `trust_id` when present.
- `token.trustor_id` is compared against the database record `user_id` when
present.
- `token.trustee_id` is compared against the database record `user_id` when
present.
- `token.trust_id` is compared against the database record `trust_id` when
present.
- `token.issued_at` is compared against the database record with
`revocation_event.issued_before >= token.issued_at`.

Python version of the Keystone applies additional match verification for the
selected data on the server side and not in the database query.

- When `revocation_event.domain_id` is set it is compared against
`token.domain_id` and `token.identity_domain_id`.
`token.domain_id` and `token.identity_domain_id`.
- When `revocation_event.role_id` is present it is compared against every of the
`token.roles`.
`token.roles`.

After the first non matching result further evaluation is being stopped.
Logically there does not seem to be a reason for such handling and it looks to
Expand All @@ -89,7 +92,6 @@ While following checks allow much higher details of the revocation events in the
context of the usual fernet token revocation it is only going to match on the
`audit_id` and `issued_before`.


### Revocation table purge

In the python Keystone there is no automatic cleanup handling. Due to that
Expand All @@ -105,9 +107,10 @@ try to delete expired records errors can occur. However, if only rust version is
validating the tokens python version will not perform any backups. Additionally
no errors were reported yet in installations with multiple Keystone instances.
Therefore it is necessary for the rust implementation to do periodic cleanup. It
should be exexcuted with the following query filter: `revoked_at < (now -
(expiration + expiration_buffer))`. Such implementation must be made optional
with possibility to disable this behavior using the config file.
should be exexcuted with the following query filter:
`revoked_at < (now - (expiration + expiration_buffer))`. Such implementation
must be made optional with possibility to disable this behavior using the config
file.

## Consequences

Expand All @@ -116,4 +119,4 @@ with possibility to disable this behavior using the config file.
- Token validation processing time is increased with the database lookup.

- Expired revocation records are optionally periodically cleaned by the rust
implementation.
implementation.
9 changes: 5 additions & 4 deletions doc/src/adr/0010-pci-dss-failed-auth-protection.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ must be applied part of the locked account verification:

- When `user_options.IGNORE_LOCKOUT_ATTEMPT` is set user account is NOT locked

- When `user.failed_auth_count >= conf.security_compliance.lockout_failure_attempts`
- When
`user.failed_auth_count >= conf.security_compliance.lockout_failure_attempts`
the account is locked.

- When `user.failed_auth_at + conf.security_compliance.lockout_duration >
now()` account is locked. When the time is `< now()` - reset the counters
in the database.
- When `user.failed_auth_at + conf.security_compliance.lockout_duration > now()`
account is locked. When the time is `< now()` - reset the counters in the
database.

- Otherwise the account is NOT locked.

Expand Down
10 changes: 5 additions & 5 deletions doc/src/adr/0011-pci-dss-inactive-account-deactivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ authentication request must be rejected with `http.Unauthorized`.
Additional background process must be implemented to deactivate inactive
accounts. For this when
`conf.security_compliance.disable_user_account_days_inactive` is set a process
should loop over all user accounts. When the `user.last_active_at +
disable_user_account_days_inactive < now()` presence of the
`user.options.IGNORE_USER_INACTIVITY_OPT` should be checked. When absent the
should loop over all user accounts. When the
`user.last_active_at + disable_user_account_days_inactive < now()` presence of
the `user.options.IGNORE_USER_INACTIVITY_OPT` should be checked. When absent the
account must be updated setting `user.enabled` to `false`.

Since it is technically possible that the background process is not running for
Expand All @@ -82,8 +82,8 @@ workflow the `user.last_active_at` should be set to the current date time.

- Authentication with methods other than username password are not updating the
`lst_active_at`. Due to that the account that used i.e. application
credentials for the activation for more than X days would become disabled. This
requires account to perform periodic login using the password.
credentials for the activation for more than X days would become disabled.
This requires account to perform periodic login using the password.

- It should be considered to update application credentials workflow to update
the `user.last_active_at` attribute after successful authentication.
Expand Down
8 changes: 5 additions & 3 deletions doc/src/adr/0012-pci-dss-account-password-expiry.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ PCI-DSS contains the following requirement to the IAM system:

If passwords/passphrases are used as the only authentication factor for user
access (i.e., in any single-factor authentication implementation) then either:
• Passwords/passphrases are changed at least once every 90 days, OR
• The security posture of accounts is dynamically analyzed, and real-time
access to resources is automatically determined accordingly.

- Passwords/passphrases are changed at least once every 90 days, OR

- The security posture of accounts is dynamically analyzed, and real-time
access to resources is automatically determined accordingly.

Python Keystone implements this requirement with the help of the
`conf.security_compliance.password_expires_days` and `password.expires_at`
Expand Down
23 changes: 11 additions & 12 deletions doc/src/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,25 @@ between sqlalchemy and sea-orm. The later suggests doing database schema first.
In the next step object types are created out of the database. That means that
the database migration must be written first and cannot be automatically
generated from the code (easily, but there is a way). Current migrations do not
create database schema that is managed by the py-keystone. Therefore in order
to get a fully populated database schema it is necessary to apply
create database schema that is managed by the py-keystone. Therefore in order to
get a fully populated database schema it is necessary to apply
`keystone-manage db_sync` and `keystone-db up` independently.

Target of the keystone-ng is to be deployed in pair with the python keystone of
"any" version. Due to that it is not possible to assume the state of the
database, nor to apply any changes to the schema manaaged by the py-keystone. A
federation rework assumes model change. To keep it working with the
python-keystone artificial table entries may be created (in the example when a
new identity provider is being created automatically sanitized entries are
being added for the legacy identity provider and necessary protocols) A
federation rework assumes model change. To keep it working with the
python-keystone artificial table entries may be created (in the example when a
new identity provider is being created automatically sanitized entries are
being added for the legacy identity provider together with necessary idp
protocols).
new identity provider is being created automatically sanitized entries are being
added for the legacy identity provider and necessary protocols) A federation
rework assumes model change. To keep it working with the python-keystone
artificial table entries may be created (in the example when a new identity
provider is being created automatically sanitized entries are being added for
the legacy identity provider together with necessary idp protocols).

## Fernet

keystone-ng uses the same mechanism for tokens to provide compatibility. The
fernet-keys repository must be provided in the runtime (i.e. by mounting them
as a volume into the container). There is no tooling to create or rotate keys
as the py-keystone does.
fernet-keys repository must be provided in the runtime (i.e. by mounting them as
a volume into the container). There is no tooling to create or rotate keys as
the py-keystone does.
27 changes: 14 additions & 13 deletions doc/src/federation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

Python Keystone is not implementing the Federation natively (neither SAML2, nor
OIDC). It relies on the proxy server for the authentication protocol specifics
and tries to map resulting users into the local database. This leads to a
pretty big number of limitations (not limited to):
and tries to map resulting users into the local database. This leads to a pretty
big number of limitations (not limited to):

- Identity Provider can be only configured by cloud administrators only

Expand All @@ -13,19 +13,20 @@ pretty big number of limitations (not limited to):
initiated logout)

- Forces deployment of the proxy service in front of Keystone relying on the
modules for SAML2 and/or OIDC implementation (such modules may be abandoned
or removed).
modules for SAML2 and/or OIDC implementation (such modules may be abandoned or
removed).

- Client authentication right now is complex and error prone (every public
provider has implementation specifics that are often even not cross-compatible)
provider has implementation specifics that are often even not
cross-compatible)

In order to address those challenges a complete reimplementation is being done
with a different design. This allows implementing features not technically
possible in the py-keystone:

- Federation is controlled on the domain level by the domain managers. This
means that the domain manager is responsible for the configuration of how users
should be federated from external IdPs.
means that the domain manager is responsible for the configuration of how
users should be federated from external IdPs.

- Identity providers and/or attribute mappings can be reused by different
domains allowing implementing social logins.
Expand All @@ -34,21 +35,22 @@ possible in the py-keystone:
decreases amount of different flows to the minimum making client applications
much simpler and more reliable.


## API changes

A series of brand new API endpoints have been added to the Keystone API.

- **/v4/federation/identity_providers** (manage the identity providers)

- **/v4/federation/mappings** (manage the mappings tied to the identity provider)
- **/v4/federation/mappings** (manage the mappings tied to the identity
provider)

- **/v4/federation/auth** (initiate the authentication and get the IdP url)

- **/v4/federation/oidc/callback** (exchange the authorization code for the Keystone token)
- **/v4/federation/oidc/callback** (exchange the authorization code for the
Keystone token)

- **/v4/federation/identity_providers/{idp_id}/jwt**
(exchange the JWT token issued by the referred IdP for the Keystone token)
- **/v4/federation/identity_providers/{idp_id}/jwt** (exchange the JWT token
issued by the referred IdP for the Keystone token)

## DB changes

Expand All @@ -72,7 +74,6 @@ Following tables are added:
{{#include ../../src/db/entity/federated_auth_state.rs:8:16}}
```


## Compatibility notes

Since the federation is implemented very differently to how it was done before
Expand Down
Loading