diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 80de405dc..ae0cb7130 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -48,7 +48,7 @@ export default defineConfig({ { text: 'Attach an LDAP Domain Backend', link: '/guides/keystone/ldap-domain-backend' }, { text: 'Attach an OIDC Federation Backend', link: '/guides/keystone/oidc-federation' }, { text: 'Attach a SAML Federation Backend', link: '/guides/keystone/saml-federation' }, - { text: 'Enable Keystone Database TLS', link: '/guides/keystone/enable-keystone-database-tls' }, + { text: 'Enable Keystone Database TLS/mTLS', link: '/guides/keystone/enable-keystone-database-tls' }, { text: 'Enable Keystone Operator Metrics', link: '/guides/keystone/enable-keystone-operator-metrics' }, { text: 'Enable Keystone Operator NetworkPolicy', link: '/guides/keystone/enable-keystone-operator-networkpolicy' }, { text: 'Migrate Keystone DB to Dynamic Credentials', link: '/guides/keystone/migrate-keystone-db-to-dynamic-credentials' }, diff --git a/docs/guides/advanced-configuration.md b/docs/guides/advanced-configuration.md index 92de5aa07..5cffe0222 100644 --- a/docs/guides/advanced-configuration.md +++ b/docs/guides/advanced-configuration.md @@ -88,7 +88,7 @@ spec: The reconciler deep-copies the whole `infrastructure.database` and `infrastructure.cache` blocks onto the `controlplane-keystone` child, so the -child connects to exactly the servers you declared here. +child connects to the servers you declared here. ::: warning In brownfield mode you own schema setup In brownfield mode (no `clusterRef`) the operator leaves the `secretRef` you diff --git a/docs/guides/day-2-operations.md b/docs/guides/day-2-operations.md index c30ff9588..52f242347 100644 --- a/docs/guides/day-2-operations.md +++ b/docs/guides/day-2-operations.md @@ -64,7 +64,7 @@ Scale down the same way. The keystone-operator maintains a `PodDisruptionBudget` named `controlplane-keystone`, sized from the child's replica count: at `replicas > 1` it sets `minAvailable=1` so a voluntary disruption never drains the last healthy pod; at `replicas == 1` it sets `maxUnavailable=1` instead, -deliberately allowing eviction so a node drain cannot deadlock on a +allowing eviction so a node drain cannot deadlock on a single-replica child. ::: tip Load-driven autoscaling is standalone-only diff --git a/docs/guides/dedicated-service-namespaces.md b/docs/guides/dedicated-service-namespaces.md index 1ca77c608..49baa4640 100644 --- a/docs/guides/dedicated-service-namespaces.md +++ b/docs/guides/dedicated-service-namespaces.md @@ -241,7 +241,7 @@ unset BAO_TOKEN ``` ::: warning If you skip it -The chain stalls exactly as in the Quick Start, one namespace over: the +The chain stalls as in the Quick Start, one namespace over: the ControlPlane reports `DBCredentialsReady=False`, the `controlplane-keystone-db-credentials` ExternalSecret — now in `openstack-internal` — sits in `SecretSyncedError`, and the external-secrets @@ -290,7 +290,7 @@ kubectl get mariadb openstack-db -n openstack-internal \ -o jsonpath='{.metadata.labels.c5c3\.io/controlplane-name}{" / "}{.metadata.ownerReferences}{"\n"}' ``` -**The API answers through the shared Gateway**, exactly as on the unsplit +**The API answers through the shared Gateway**, as on the unsplit devstack — the route attaches across namespaces thanks to Step 1: ```bash diff --git a/docs/guides/end-to-end-sso.md b/docs/guides/end-to-end-sso.md index 65540b966..deb3d0d8d 100644 --- a/docs/guides/end-to-end-sso.md +++ b/docs/guides/end-to-end-sso.md @@ -254,7 +254,7 @@ kubectl get horizon controlplane-horizon -n openstack -o jsonpath='{.spec.multiD The login page now shows a domain field, and users who leave it blank land in `Default` — so the bootstrap admin stays reachable. -The field is deliberately free text rather than a dropdown. Horizon bounds a +The field is free text rather than a dropdown. Horizon bounds a domain dropdown by `OPENSTACK_KEYSTONE_DOMAIN_CHOICES` and rejects every domain outside it, but the operator only sees the domains your LDAP backends declare. A dropdown built from those would lock out everyone in a domain it cannot @@ -319,7 +319,7 @@ the dashboard somewhere else. Two things must agree with what the browser sees: The operator then projects `https://horizon.127-0-0-1.nip.io:8443/auth/websso/` as the trusted origin. The -port cannot be derived from the hostname, which is exactly why the override +port cannot be derived from the hostname, which is why the override exists. ## Pinning the federation proxy image @@ -380,7 +380,7 @@ chainsaw test --test-dir tests/e2e-controlplane-sso ::: details The ControlPlane CR the suite applies The suite runs a second full ControlPlane in its own CI job (the webhook permits only one ControlPlane per namespace), so its CR name (`controlplane-sso`) -deliberately differs from the `controlplane` devstack name used in the +differs from the `controlplane` devstack name used in the walkthrough above. <<< @/../tests/e2e-controlplane-sso/02-controlplane-cr.yaml#controlplane-cr diff --git a/docs/guides/glance/enable-glance-operator-networkpolicy.md b/docs/guides/glance/enable-glance-operator-networkpolicy.md index b9186529d..790490f84 100644 --- a/docs/guides/glance/enable-glance-operator-networkpolicy.md +++ b/docs/guides/glance/enable-glance-operator-networkpolicy.md @@ -88,7 +88,7 @@ kubectl wait helmrelease/glance-operator -n glance-system \ --for=condition=Ready --timeout=5m ``` -The chart-level policy allows exactly what the operator needs: egress to the +The chart-level policy allows what the operator needs: egress to the kube-apiserver and DNS, ingress to the webhook and metrics ports. The glance-operator's health check reaches the Glance Service on TCP 9292 in the workload namespace; when the workload namespace itself runs a per-CR diff --git a/docs/guides/glance/glance-s3-multistore.md b/docs/guides/glance/glance-s3-multistore.md index 7f5798ec9..1de0e4d32 100644 --- a/docs/guides/glance/glance-s3-multistore.md +++ b/docs/guides/glance/glance-s3-multistore.md @@ -40,7 +40,7 @@ resource name in the examples below is one that devstack produces. - The devstack's Garage object store and the ESO-synced `garage-s3-credentials` Secret are already running (Step 1 describes them). A second store needs a second bucket the credentials can read and write — the devstack pre-creates - `glance-images-2` for exactly this. + `glance-images-2` for this. ## Step 1 — The Garage devstack pieces diff --git a/docs/guides/horizon/enable-horizon-operator-networkpolicy.md b/docs/guides/horizon/enable-horizon-operator-networkpolicy.md index 88d397a74..a1c954dc1 100644 --- a/docs/guides/horizon/enable-horizon-operator-networkpolicy.md +++ b/docs/guides/horizon/enable-horizon-operator-networkpolicy.md @@ -74,7 +74,7 @@ kubectl wait helmrelease/horizon-operator -n horizon-system \ --for=condition=Ready --timeout=5m ``` -The chart-level policy allows exactly what the operator needs: egress to the +The chart-level policy allows what the operator needs: egress to the kube-apiserver and DNS, ingress to the webhook and metrics ports. The horizon-operator's health check reaches the dashboard Service on TCP 8080 in the workload namespace; when the workload namespace itself runs a per-CR diff --git a/docs/guides/keystone/adopt-external-keystone.md b/docs/guides/keystone/adopt-external-keystone.md index c4360e605..c213a324e 100644 --- a/docs/guides/keystone/adopt-external-keystone.md +++ b/docs/guides/keystone/adopt-external-keystone.md @@ -72,7 +72,7 @@ kubectl -n brownfield-keystone wait --for=condition=Complete \ job/keystone-fixture-setup --timeout=5m ``` -The setup Job deliberately makes this look like a *real* installation rather than +The setup Job makes this look like a real installation rather than a fresh bootstrap: - a **non-default admin identity** — user `brownfield-admin`, project @@ -120,7 +120,7 @@ manifest here rather than a hand-kept copy of one — see [Tested by](#tested-by <<< @/../tests/e2e/c5c3/external-keystone/02-controlplane-external.yaml#controlplane-external That ControlPlane is the only thing in the file — the suite keeps its fixture -admin-password Secret in a sibling file precisely so applying this one cannot +admin-password Secret in a sibling file so applying this one cannot overwrite the Secret you filled in step 1 — so on the devstack apply it as it stands: @@ -139,7 +139,7 @@ Field by field: | `external.authURL` | The identity endpoint the operator manages against. | | `external.endpointType` | Which catalog interface to authenticate against. Omitted here, so it defaults to `public` — the interface that is normally reachable from outside the installation. | | `external.caBundleSecretRef` | The private-CA bundle, when the endpoint needs one. Omitted here: the kind fixture is plain HTTP — devstack only, see the warning below. | -| `external.catalog.identityServiceName` | Disambiguates the identity-service import. Only needed when your catalog holds **more than one** `identity`-type service — as the fixture deliberately does. | +| `external.catalog.identityServiceName` | Disambiguates the identity-service import. Only needed when your catalog holds **more than one** `identity`-type service — as the fixture does. | | `korc.adminCredential.cloudCredentialsRef` | Where the minted credential is materialized — the `clouds.yaml` Secret and cloud entry read back in [step 4](#_4-verify). Spelled out here, but `k-orc-clouds-yaml` / `admin` are the defaults. | | `korc.adminCredential.passwordSecretRef` | The Secret from step 1. | | `userName` / `projectName` / `domainName` | The admin identity to authenticate as. They default to `admin` / `admin` / `Default`; the fixture uses a non-default identity, so all three are set. | @@ -237,7 +237,7 @@ kubectl -n brownfield get secret k-orc-clouds-yaml \ ``` Use that `clouds.yaml` with an OpenStack client (`openstack token issue`, -`openstack catalog list`). The e2e suite does exactly this in a Job; see +`openstack catalog list`). The e2e suite does this in a Job; see [Tested by](#tested-by). ::: tip Read the credential from Kubernetes, not the OpenBao UI @@ -422,6 +422,6 @@ adopts it with an External-mode ControlPlane, authenticates an OpenStack client with the minted credential, rotates it, and asserts the imports survive deletion. The suite runs in namespace `brownfield` with the ControlPlane -`controlplane-external` — exactly the names this walkthrough uses — so +`controlplane-external` — the names this walkthrough uses — so [step 2](#_2-apply-the-external-mode-controlplane) imports the suite's own manifest rather than restating it. diff --git a/docs/guides/keystone/enable-keystone-database-tls.md b/docs/guides/keystone/enable-keystone-database-tls.md index 138a0a195..6fc62783e 100644 --- a/docs/guides/keystone/enable-keystone-database-tls.md +++ b/docs/guides/keystone/enable-keystone-database-tls.md @@ -64,7 +64,7 @@ examples below is one that devstack produces. 3. **A MariaDB that offers server-side TLS.** Client-side DB TLS only works against a server that speaks it. On the **managed** ControlPlane devstack the c5c3-operator provisions the `openstack-db` MariaDB from a minimal spec and - deliberately leaves server-side TLS/issuerRefs unset — DB-server hardening is a + leaves server-side TLS/issuerRefs unset — DB-server hardening is a platform-team concern outside the aggregate's knowledge (see the DECISION on `reconcileInfrastructure` in `operators/c5c3/internal/controller/reconcile_infrastructure.go`). So before enabling client DB TLS on a ControlPlane, run a MariaDB with @@ -247,9 +247,9 @@ encrypted — re-check `DatabaseTLSReady` and confirm the MariaDB CR has This check requires the MariaDB from prerequisite 3 to set `spec.tls.required=true`, so any connection that does not negotiate TLS is rejected at the transport layer before authentication. The `probe`/`probe` -credentials below are deliberately bogus — they are never checked, because the +credentials below are bogus — they are never checked, because the server rejects the plaintext handshake before it reaches authentication. Probe -from inside the Keystone Pod by deliberately omitting the `ssl=` kwarg: +from inside the Keystone Pod by omitting the `ssl=` kwarg: ```bash kubectl -n openstack exec "$POD" -c keystone -- python3 -c ' diff --git a/docs/guides/keystone/enable-keystone-operator-metrics.md b/docs/guides/keystone/enable-keystone-operator-metrics.md index 2d6f3fc1b..bd0d925eb 100644 --- a/docs/guides/keystone/enable-keystone-operator-metrics.md +++ b/docs/guides/keystone/enable-keystone-operator-metrics.md @@ -35,7 +35,7 @@ for the port-forward, default credentials, and the Prometheus targets sanity-check. The remainder of this guide is the **canonical non-kind path** — -production overlays (`deploy/flux-system/`) deliberately omit the +production overlays (`deploy/flux-system/`) omit the stack so production clusters can wire their own Prometheus, and the sections below cover that wiring end-to-end. ::: @@ -255,7 +255,7 @@ Operators with stricter cluster policies must take extra steps: does not ship this configuration — it is a forge-wide bootstrap change rather than a per-operator override. -The metrics endpoint deliberately exposes **no credentials, secrets, +The metrics endpoint exposes **no credentials, secrets, or per-tenant payloads** — only Prometheus collector samples described in the [reference catalogue](../../reference/keystone-operator-metrics.md) — so diff --git a/docs/guides/keystone/keystone-admin-password-rotation.md b/docs/guides/keystone/keystone-admin-password-rotation.md index 0f47aee6c..18465dcae 100644 --- a/docs/guides/keystone/keystone-admin-password-rotation.md +++ b/docs/guides/keystone/keystone-admin-password-rotation.md @@ -71,7 +71,7 @@ computes `hex(SHA-256(password))`, and stamps it onto the bootstrap Job's pod template as the `forge.c5c3.io/admin-password-hash` annotation. It passes that same digest to `job.RunJobWithRerunKey` as the bootstrap Job's **re-run key** — so the Job re-runs when, and only when, the admin password changes. The re-run -gate is keyed on the password digest *alone*, deliberately **not** on the full +gate is keyed on the password digest *alone*, not on the full pod template: an image-tag change must not re-run bootstrap, because re-running `keystone-manage bootstrap` after a cross-version DB migration fails on the already-migrated admin user. When the password digest changes, the operator diff --git a/docs/guides/keystone/keystone-admin-password-scheduled-rotation.md b/docs/guides/keystone/keystone-admin-password-scheduled-rotation.md index 5e30639c0..97f9efa9d 100644 --- a/docs/guides/keystone/keystone-admin-password-scheduled-rotation.md +++ b/docs/guides/keystone/keystone-admin-password-scheduled-rotation.md @@ -132,7 +132,7 @@ spec: > **Own the ExternalSecret for a durable setup.** If your devstack continuously > reconciles `deploy/kind/infrastructure/` (Flux/GitOps), a manual patch to the > shim is reverted — own a dedicated ExternalSecret for your CR reading the per-CR -> path instead, exactly as the `admin-password-scheduled-rotation` chainsaw suite +> path instead, as the `admin-password-scheduled-rotation` chainsaw suite > ships its own CR + ExternalSecret (`tests/e2e/keystone/admin-password-scheduled-rotation/00-keystone-cr.yaml`). > On a non-kind deployment you already own the ExternalSecret, so just set its > `remoteRef.key` to `bootstrap/openstack/keystone/admin`. @@ -373,7 +373,7 @@ Keystone CRs concurrently. > **Path in lockstep.** The admin-credentials ExternalSecret's `remoteRef.key` > must match the per-CR path of the Keystone CR whose rotation feeds it — this is -> exactly what Step 1 wired up. For a CR named `{name}` in `{namespace}`, that is +> what Step 1 wired up. For a CR named `{name}` in `{namespace}`, that is > `bootstrap/{namespace}/{name}/admin`. On a ControlPlane deployment the > operator-projected `controlplane-keystone-admin-credentials` ExternalSecret > reads `bootstrap/openstack/controlplane-keystone/admin`, and the bootstrap seed @@ -438,7 +438,7 @@ chainsaw test --test-dir tests/e2e/keystone/admin-password-scheduled-rotation ::: details The Keystone CR the suite applies The suite isolates its Keystone instance from the parallel suite pool, so its CR name (`keystone-adminpw-sched`) and logical database -(`keystone_adminpw_sched`) deliberately differ from the devstack names used in +(`keystone_adminpw_sched`) differ from the devstack names used in the walkthrough above. <<< @/../tests/e2e/keystone/admin-password-scheduled-rotation/00-keystone-cr.yaml#keystone-cr diff --git a/docs/guides/keystone/ldap-domain-backend.md b/docs/guides/keystone/ldap-domain-backend.md index ce544d562..f0f3a6488 100644 --- a/docs/guides/keystone/ldap-domain-backend.md +++ b/docs/guides/keystone/ldap-domain-backend.md @@ -38,7 +38,7 @@ examples below is one that devstack produces. - **Service users stay SQL-backed.** The backend is read-only by default, so OpenStack service accounts (and the bootstrap admin) must remain in the SQL-backed `Default` domain — the CRD hard-rejects attaching a backend to - `Default` for exactly this reason. Plan for humans in the LDAP domain and + `Default` for this reason. Plan for humans in the LDAP domain and services in `Default`. ## Step 1 — Deploy the seeded OpenLDAP fixture (kind devstack) @@ -203,7 +203,7 @@ chainsaw test --test-dir tests/e2e/keystone/ldap-domain-backend ::: details The backend CR the suite applies The suite isolates its Keystone instance from the parallel suite pool, so its backend CR name (`planetexpress-ldap`) and `keystoneRef` (`keystone-ldap`) -deliberately differ from the `corp-ldap` / `keystone` names used in the +differ from the `corp-ldap` / `keystone` names used in the walkthrough above. <<< @/../tests/e2e/keystone/ldap-domain-backend/02-backend-cr.yaml#backend-cr diff --git a/docs/guides/keystone/oidc-federation.md b/docs/guides/keystone/oidc-federation.md index aadafa999..922a4ff36 100644 --- a/docs/guides/keystone/oidc-federation.md +++ b/docs/guides/keystone/oidc-federation.md @@ -223,7 +223,7 @@ What the operator does with this: The mapping's `remote[].type` entries are **full WSGI environ keys**: the proxy passes claims as `OIDC-` headers, which uWSGI surfaces as -`HTTP_OIDC_`. The operator strips exactly these headers from inbound +`HTTP_OIDC_`. The operator strips these headers from inbound requests (in both dash and underscore spelling) so in-cluster clients cannot spoof claims past the module. @@ -336,7 +336,7 @@ provider; use the per-IdP paths. configuration first (with the last OIDC backend the sidecar disappears and the Service returns to uWSGI), then removes the protocol, mapping, and identity provider — always — and finally applies -`spec.domain.deletionPolicy` to the domain exactly like the LDAP flow. +`spec.domain.deletionPolicy` to the domain like the LDAP flow. Declarative groups live inside the domain and follow it. ## Discovery against an in-cluster identity provider @@ -475,7 +475,7 @@ chainsaw test --test-dir tests/e2e/keystone/oidc-federation ::: details The backend CR the suite applies The suite isolates its Keystone instance from the parallel suite pool, so its backend CR points `keystoneRef` at `keystone-oidc` (and enables -`oauth2Introspection` for the CLI bearer flow) — deliberately differing from the +`oauth2Introspection` for the CLI bearer flow) — differing from the `controlplane-keystone` reference used in the walkthrough above. <<< @/../tests/e2e/keystone/oidc-federation/02-backend-cr.yaml#backend-cr diff --git a/docs/guides/keystone/saml-federation.md b/docs/guides/keystone/saml-federation.md index e1b4e90c5..ddafe1a30 100644 --- a/docs/guides/keystone/saml-federation.md +++ b/docs/guides/keystone/saml-federation.md @@ -277,7 +277,7 @@ chainsaw test --test-dir tests/e2e/keystone/saml-federation ::: details The backend CR the suite applies The suite isolates its Keystone instance from the parallel suite pool, so its backend CR name (`keycloak-saml`) and `keystoneRef` (`keystone-saml`) -deliberately differ from the `corp-saml` / `keystone` names used in the +differ from the `corp-saml` / `keystone` names used in the walkthrough above. <<< @/../tests/e2e/keystone/saml-federation/02-backend-cr.yaml#backend-cr