diff --git a/docs/sts-credential-vending-assessment.md b/docs/sts-credential-vending-assessment.md new file mode 100644 index 0000000..bce0d40 --- /dev/null +++ b/docs/sts-credential-vending-assessment.md @@ -0,0 +1,95 @@ +# STS Credential Vending Assessment for NetKingdom + +Status: assessment complete (ARTIFACT-STORE-WP-0007 D7.3) +Date: 2026-07-02 +Upstream baseline: `net-kingdom/docs/object-storage-sts-credential-vending.md` +(NK-WP-0007, 2026-05-18) — this assessment specializes that architecture for +artifact-store and records the current-state inventory it asks for. + +## 1. Current object-storage credential inventory + +| Consumer | Credential path today | Temporary-credential ready? | +| --- | --- | --- | +| artifact-store S3 backend | Static key pair via `ARTIFACTSTORE_S3_ACCESS_KEY_REF` / `ARTIFACTSTORE_S3_SECRET_KEY_REF` (file/env refs, no plaintext in config) | **No** — `S3BackendConfig` has no `session_token` field and `aioboto3.Session` is constructed without `aws_session_token` (`src/artifactstore/storage/backends/s3.py`); STS credentials cannot be consumed until D7.4 lands | +| NetKingdom CNPG backups (`net-kingdom-pg-backup-s3`) | Documented as a static `kubectl create secret generic --from-literal` key pair in `sso-mfa/k8s/postgresql/README.md`; **not yet provisioned** — backups are parked "until object storage is available" | No — static by design today; should adopt the vending flow (or at minimum an ExternalSecret lane) when object storage is provisioned | +| MinIO compatibility harness (`make test-minio` / `test-minio-local`) | One-run generated throwaway root credentials, local container only | N/A — dev double, never a production path | + +No other live S3/MinIO credential consumers were found in net-kingdom or +artifact-store. The most important inventory conclusion: **nothing is +production-live yet**, so the vending architecture can be adopted without a +migration burden — the CNPG backup lane should start on the target pattern +rather than shipping static keys first. + +## 2. Can Keycloak / Authelia / local-identity act as the OIDC IdP for MinIO `AssumeRoleWithWebIdentity`? + +MinIO's STS `AssumeRoleWithWebIdentity` accepts any OIDC JWT whose issuer is +configured in MinIO's identity-provider settings and whose claims map to a +MinIO policy. Assessment per NetKingdom issuer: + +- **key-cape (lightweight mode)** — yes, and it is the preferred first + issuer: it already issues IAM Profile v0.2 tokens with OIDC discovery + + JWKS (proven by the Core Hub verifier integration test, CUST-WP-0025-T03). + MinIO consumes the discovery URL directly. +- **Keycloak (expanded mode)** — yes; standard, widely documented MinIO OIDC + pairing. This is the production/enterprise-federation issuer per the + NetKingdom baseline; not yet deployed (NK-WP-0001 Keycloak was cancelled as + superseded, so key-cape leads until enterprise federation is needed). +- **Authelia** — technically an OIDC provider, but it is the NetKingdom + session/portal SSO layer and does not issue IAM Profile v0.2 claims + (`tenant`, `principal_type`, `assurance`). Using it as a storage IdP would + bypass the IAM Profile contract. **Do not use** as the vending issuer. +- **local-identity** — dev/bootstrap contexts only, per the baseline's + local-dev restrictions: only explicitly configured dev vending instances + may accept it, and minted credentials must be confined to local/sandbox + object stores. + +Important nuance from the baseline: consumers should not hit MinIO STS +directly with raw IdP tokens. The **credential-vending service** verifies the +IAM Profile and asks **flex-auth** for the decision first; MinIO's own +claim-to-policy mapping is then deliberately coarse (one policy per +tenant/prefix class), keeping authorization in flex-auth rather than in MinIO +policy JSON. + +## 3. Target architecture (artifact-store specialization) + +Follow the NetKingdom baseline flow (IAM Profile token → vending service → +flex-auth decision → backend exchange → temporary credentials). The +artifact-store-specific bindings: + +| Element | Binding | +| --- | --- | +| Issuer | key-cape lightweight mode first; Keycloak expanded mode when enterprise federation arrives; local-identity for sandbox only | +| Audience | the credential-vending service audience (not artifact-store, not MinIO) — `aud` per IAM Profile v0.2 | +| Role/policy mapping | flex-auth vocabulary from the baseline: tenant, protected-system=`object-storage`, bucket, prefix, actions (`read`/`write`/`list`), TTL; MinIO side keeps one coarse policy per tenant-prefix class | +| Expiration | default lease 15–60 min with refresh-before-expiry + jitter in the consumer; TTL bounds enforced by flex-auth (proven pattern: `ttl_out_of_bounds` denial in the FLEX-WP-0007 smoke) | +| Revocation | short TTLs are the primary control; for immediate cuts, disable the MinIO policy or the vending grant; OpenBao lease revocation applies only to broker-held parent material | +| Audit | vending service emits the baseline's audit event (issuer, sub, tenant, decision id, backend, TTL, non-secret request ids); OpenBao audit covers parent-credential access | +| Break-glass | platform-control-plane path per the baseline: short-lived, post-event review record mandatory; never a tenant-plane shortcut | + +### artifact-store consumer work (feeds D7.4) + +1. Add `session_token: str | None` to `S3BackendConfig` and pass + `aws_session_token` into `aioboto3.Session`. +2. Add `ARTIFACTSTORE_S3_SESSION_TOKEN_REF` (and optionally + `ARTIFACTSTORE_S3_CREDENTIAL_EXPIRATION_REF`) alongside the existing refs, + with atomic refresh of all three values. +3. Support the delivery modes from the baseline in priority order: mounted + files refreshed by a controller/sidecar first (fits the current `_REF` + file pattern), `credential_process` for CLI/batch use later. + +### Sequencing recommendation + +1. D7.4: session-token consumer support (small, local, no deployment gate). +2. Vending service + flex-auth vocabulary (NetKingdom/flex-auth owned; + artifact-store is a consumer, not the owner). +3. Wire the CNPG backup lane and artifact-store deployment to the vending + flow when Railiance provisions the production object store — do not ship + static production keys in the interim. + +## Non-goals confirmed + +- artifact-store does not own identity, authorization, or secret custody + (baseline ownership table). +- OpenBao is not the object-storage authorization engine; it holds parent + material and audit only. +- MinIO policy JSON is not the canonical tenant policy model; flex-auth is. diff --git a/workplans/ARTIFACT-STORE-WP-0007-minio-maxio-sts-vending.md b/workplans/ARTIFACT-STORE-WP-0007-minio-maxio-sts-vending.md index 0c146b2..53ef6ee 100644 --- a/workplans/ARTIFACT-STORE-WP-0007-minio-maxio-sts-vending.md +++ b/workplans/ARTIFACT-STORE-WP-0007-minio-maxio-sts-vending.md @@ -137,7 +137,7 @@ Completed 2026-07-02: ```task id: ARTIFACT-STORE-WP-0007-T003 -status: todo +status: done priority: high state_hub_task_id: "d3d5c4c1-d3b2-4163-b99d-1b08f90566d1" ``` @@ -152,6 +152,19 @@ Acceptance: token audience, role/policy mapping, expiration, revocation, audit, and break-glass behavior. +Completed 2026-07-02: added `docs/sts-credential-vending-assessment.md`, +specializing the NetKingdom baseline (`net-kingdom/docs/object-storage-sts- +credential-vending.md`, NK-WP-0007) for artifact-store. Inventory found no +production-live object-storage credentials yet (artifact-store static-ref +bridge, CNPG backup lane parked pre-provisioning), confirmed key-cape/Keycloak +as viable MinIO `AssumeRoleWithWebIdentity` issuers (Authelia rejected — +no IAM Profile claims; local-identity sandbox-only), and bound the target +architecture: vending-service audience, flex-auth decision vocabulary, 15–60 +min leases with refresh jitter, audit event shape, and break-glass rules. +Key code finding for D7.4: `S3BackendConfig` lacks `session_token` and the +`aioboto3.Session` omits `aws_session_token`, so STS credentials cannot be +consumed until that lands. + ## D7.4 - Artifact-Store Temporary Credential Support ```task