diff --git a/charts/flex-auth/templates/deployment.yaml b/charts/flex-auth/templates/deployment.yaml index 0b414c9..0c413e0 100644 --- a/charts/flex-auth/templates/deployment.yaml +++ b/charts/flex-auth/templates/deployment.yaml @@ -20,6 +20,9 @@ spec: {{- end }} securityContext: runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 seccompProfile: type: RuntimeDefault containers: @@ -71,7 +74,9 @@ spec: defaultMode: 0440 sources: - serviceAccountToken: + {{- if .Values.callerAuth.reviewer.audience }} audience: {{ .Values.callerAuth.reviewer.audience | quote }} + {{- end }} expirationSeconds: {{ .Values.callerAuth.reviewer.expirationSeconds }} path: token - configMap: diff --git a/charts/flex-auth/values.yaml b/charts/flex-auth/values.yaml index d1b3c66..ca46bb0 100644 --- a/charts/flex-auth/values.yaml +++ b/charts/flex-auth/values.yaml @@ -38,7 +38,11 @@ callerAuth: binding: "" reviewer: mountPath: /var/run/secrets/flex-auth-reviewer - audience: https://kubernetes.default.svc + # Empty: kubelet mints the API server's default audiences so TokenReview + # authenticates. A custom audience (e.g. https://kubernetes.default.svc) + # 401s on this k3s cluster, whose API audiences are + # https://kubernetes.default.svc.cluster.local and k3s. + audience: "" expirationSeconds: 3600 resources: diff --git a/deploy/README.md b/deploy/README.md index b70b84b..bdfcdeb 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -96,8 +96,8 @@ last-known-good digest below. | `flex-auth-tenant-engine` *(live until warn promote)* | `sha256:1bf060e61122693ce98359c167cc5fe8bdafc84e097e090eaa71af94d0f27cbc` | nine-action policy (FLEX-WP-0014), CI-built from `f304688` | | `flex-auth-tenant-engine` *(previous)* | `sha256:9320df394a642eff24da8af4a0ee8886a7bb78b0f14d8ee1deeb30ea8eeeaba7` | seven-action policy, FLEX-WP-0013 restore; guardrail actions deny `unknown_action` | | `flex-auth-tenant-engine` *(rollback)* | `sha256:c25fc34a6cd7e64d955f8723ec70e176a583d5ae71d76280c4e2d89fba0fe0aa` | four-action policy; lifecycle actions deny `unknown_action` | -| `flex-auth-user-engine` | `sha256:138aa3471c46bca6e814691fa1e6520aedda3dffd743e6b09141ab433afdb64b` | **pin, not live** — caller-auth **warn** (FLEX-WP-0015-T02), CI `main-3de72fe` | -| `flex-auth-user-engine` *(live until warn promote)* | `sha256:1f5290376dc5fcf456dc7a785e394d8b90949dabecd1d3e856f38557149bb5f4` | FLEX-WP-0009-T04, nine fixtures (incl. registration-applicant) verified live 2026-08-16 | +| `flex-auth-user-engine` | `sha256:138aa3471c46bca6e814691fa1e6520aedda3dffd743e6b09141ab433afdb64b` | **live** — caller-auth **enforce** (FLEX-WP-0015-T02), CI `main-3de72fe`, A2 probe 2026-08-19 | +| `flex-auth-user-engine` *(previous)* | `sha256:1f5290376dc5fcf456dc7a785e394d8b90949dabecd1d3e856f38557149bb5f4` | FLEX-WP-0009-T04, nine fixtures (incl. registration-applicant) verified live 2026-08-16 | | `flex-auth-user-engine` *(previous)* | `sha256:a31961c45215aa6baf3bc748c6741ab703c2c8325e61aa7983a355026195e51b` | FLEX-WP-0009-T03, six fixtures verified live 2026-08-10 | Rolling back the tenant-engine service to `c25fc34a…` restores fail-closed diff --git a/deploy/flex-auth-tenant-engine.yaml b/deploy/flex-auth-tenant-engine.yaml index 974abe5..3b4fb8f 100644 --- a/deploy/flex-auth-tenant-engine.yaml +++ b/deploy/flex-auth-tenant-engine.yaml @@ -63,7 +63,10 @@ spec: name: flex-auth-reviewer readOnly: true securityContext: + fsGroup: 65532 + runAsGroup: 65532 runAsNonRoot: true + runAsUser: 65532 seccompProfile: type: RuntimeDefault volumes: @@ -72,7 +75,6 @@ spec: defaultMode: 0440 sources: - serviceAccountToken: - audience: https://kubernetes.default.svc expirationSeconds: 3600 path: token - configMap: diff --git a/deploy/flex-auth-user-engine.yaml b/deploy/flex-auth-user-engine.yaml index 9ab46fc..f616dca 100644 --- a/deploy/flex-auth-user-engine.yaml +++ b/deploy/flex-auth-user-engine.yaml @@ -25,7 +25,7 @@ spec: - --policy - /opt/flex-auth/examples/user-engine/policy_package.md - --caller-auth-mode - - warn + - enforce - --caller-kubernetes-url - https://10.43.0.1 - --caller-binding @@ -63,7 +63,10 @@ spec: name: flex-auth-reviewer readOnly: true securityContext: + fsGroup: 65532 + runAsGroup: 65532 runAsNonRoot: true + runAsUser: 65532 seccompProfile: type: RuntimeDefault volumes: @@ -72,7 +75,6 @@ spec: defaultMode: 0440 sources: - serviceAccountToken: - audience: https://kubernetes.default.svc expirationSeconds: 3600 path: token - configMap: diff --git a/docs/adr/0004-inbound-caller-authentication.md b/docs/adr/0004-inbound-caller-authentication.md index 18f36aa..ae613a9 100644 --- a/docs/adr/0004-inbound-caller-authentication.md +++ b/docs/adr/0004-inbound-caller-authentication.md @@ -22,7 +22,11 @@ Use Kubernetes ServiceAccount tokens with audience `flex-auth`. flex-auth calls the Kubernetes TokenReview API through a separately projected reviewer token and binds the authenticated ServiceAccount principal to every `resource.system` in the request. Both the single and batch endpoints use the -same choke point; health remains unauthenticated. +same choke point; health remains unauthenticated. The reviewer token uses the +API server's default audiences so it can authenticate to TokenReview; a +custom audience of `https://kubernetes.default.svc` 401s on this cluster +(`https://kubernetes.default.svc.cluster.local` and `k3s`). Caller tokens +keep audience `flex-auth`. Each deployed policy instance has an explicit, exact binding. For example: diff --git a/docs/tenancy-posture-review.md b/docs/tenancy-posture-review.md index b11957d..6595b6c 100644 --- a/docs/tenancy-posture-review.md +++ b/docs/tenancy-posture-review.md @@ -271,7 +271,7 @@ volunteer. | Task | | |---|---| | T01 | Publish the posture vector and this review; reply to `rapp-postgres` | -| T02 | **Close the A0**: TokenReview source + warn-first overlay pin done; operator promote-then-per-consumer-enforce pending | +| T02 | **Close the A0**: user-engine live enforce + A2 probe 2026-08-19; tenant-engine pin still unauthenticated | | T03 | Deleted the unused tenant-engine live-roles adapter | | T04 | AuthZEN endpoint — `wait`, with a written trigger | | T05 | `make verify-posture` guards declaration/source/deployment drift | diff --git a/railiance/README.md b/railiance/README.md index 465f9a3..490b2d1 100644 --- a/railiance/README.md +++ b/railiance/README.md @@ -106,6 +106,6 @@ or re-apply the last-known-good digest in `deploy/README.md`. | `flex-auth-tenant-engine` *(live until warn promote)* | `sha256:1bf060e61122693ce98359c167cc5fe8bdafc84e097e090eaa71af94d0f27cbc` | nine-action policy (FLEX-WP-0014), CI-built from `f304688` | | `flex-auth-tenant-engine` *(previous)* | `sha256:9320df394a642eff24da8af4a0ee8886a7bb78b0f14d8ee1deeb30ea8eeeaba7` | seven-action policy, FLEX-WP-0013 restore | | `flex-auth-tenant-engine` *(rollback)* | `sha256:c25fc34a6cd7e64d955f8723ec70e176a583d5ae71d76280c4e2d89fba0fe0aa` | four-action policy; lifecycle actions deny `unknown_action` | -| `flex-auth-user-engine` | `sha256:138aa3471c46bca6e814691fa1e6520aedda3dffd743e6b09141ab433afdb64b` | **pin, not live** — caller-auth warn (FLEX-WP-0015-T02), CI `main-3de72fe` | -| `flex-auth-user-engine` *(live until warn promote)* | `sha256:1f5290376dc5fcf456dc7a785e394d8b90949dabecd1d3e856f38557149bb5f4` | FLEX-WP-0009-T04, nine fixtures, live 2026-08-16 | +| `flex-auth-user-engine` | `sha256:138aa3471c46bca6e814691fa1e6520aedda3dffd743e6b09141ab433afdb64b` | **live** — caller-auth enforce (FLEX-WP-0015-T02), CI `main-3de72fe`, A2 probe 2026-08-19 | +| `flex-auth-user-engine` *(previous)* | `sha256:1f5290376dc5fcf456dc7a785e394d8b90949dabecd1d3e856f38557149bb5f4` | FLEX-WP-0009-T04, nine fixtures, live 2026-08-16 | | `flex-auth-user-engine` *(previous)* | `sha256:a31961c45215aa6baf3bc748c6741ab703c2c8325e61aa7983a355026195e51b` | FLEX-WP-0009-T03, six fixtures | diff --git a/tenancy.yaml b/tenancy.yaml index c6705dd..a065b8b 100644 --- a/tenancy.yaml +++ b/tenancy.yaml @@ -39,12 +39,12 @@ tenancy: decision point judges asserted claims, it cannot be the verifier of its own inputs. Not a defect and not a target for movement. A: >- - The running immutable digest still authenticates no caller, so current - remains A0. Source, overlay, and reviewed desired manifests implement - A2 with an audience-scoped Kubernetes TokenReview choke point and exact - protected-system-to-ServiceAccount bindings. The first production pin - is warn, independently per consumer, under FLEX-WP-0011/FLEX-WP-0015-T02. - Current moves to A2 only after enforce and a live unbound-request probe. + flex-auth-user-engine is live A2 as of 2026-08-19: digest + sha256:138aa347…, callerAuth.mode=enforce, unbound request returns + 401 and a cross-system token is 403. flex-auth-tenant-engine still + runs sha256:1bf060e6… with no caller authentication, so the service + declaration stays current A0 until that pin is warn-then-enforce. + Source and overlay implement A2 on both consumers. E: >- No tenant data at rest. Tenant scoping in decisions runs through one choke point (internal/decision/engine.go normalizeRequest and the diff --git a/tests/stage1.sh b/tests/stage1.sh index c9bc7e5..b1822e0 100755 --- a/tests/stage1.sh +++ b/tests/stage1.sh @@ -14,7 +14,8 @@ done user_render="$(helm template flex-auth-user-engine charts/flex-auth -f values/user-engine.yaml --namespace flex-auth)" echo "$user_render" | grep -q -- '--caller-auth-mode' || { echo "user-engine render omits caller-auth-mode" >&2; exit 1; } -echo "$user_render" | grep -A1 -- '--caller-auth-mode' | grep -q warn || { echo "user-engine first pin must be warn" >&2; exit 1; } +echo "$user_render" | grep -A1 -- '--caller-auth-mode' | grep -Eq 'warn|enforce' \ + || { echo "user-engine caller-auth-mode must be warn or enforce" >&2; exit 1; } echo "$user_render" | grep -q tokenreviews || { echo "user-engine render omits TokenReview RBAC" >&2; exit 1; } echo "$user_render" | grep -q flex-auth-reviewer || { echo "user-engine render omits reviewer projection" >&2; exit 1; } diff --git a/values/user-engine.yaml b/values/user-engine.yaml index 21fdd96..2747f49 100644 --- a/values/user-engine.yaml +++ b/values/user-engine.yaml @@ -1,6 +1,7 @@ # Production pin for the user-engine policy service. Independently rollable. -# First caller-auth pin is warn (FLEX-WP-0015-T02). Flip mode to enforce only -# after warn logs are clean of unauthenticated callers for this consumer. +# Caller-auth pin. Warn logs on 2026-08-19 were clean for the user-engine +# projected token; this consumer is enforce. Do not copy this to +# values/tenant-engine.yaml until its warn logs are clean. name: flex-auth-user-engine image: repository: forgejo.coulomb.social/coulomb/flex-auth @@ -14,7 +15,7 @@ args: - --policy - /opt/flex-auth/examples/user-engine/policy_package.md callerAuth: - mode: warn + mode: enforce kubernetesURL: https://10.43.0.1 binding: user-engine=system:serviceaccount:user-engine:user-engine consumer: diff --git a/workplans/FLEX-WP-0015-tenancy-posture-conformance.md b/workplans/FLEX-WP-0015-tenancy-posture-conformance.md index 2bb40eb..ed4ebe7 100644 --- a/workplans/FLEX-WP-0015-tenancy-posture-conformance.md +++ b/workplans/FLEX-WP-0015-tenancy-posture-conformance.md @@ -67,7 +67,7 @@ corrections. Done 2026-08-17. ```task id: FLEX-WP-0015-T02 -status: wait +status: progress priority: high state_hub_task_id: "b2e87a81-b63d-4be4-ad44-01426b7e6f74" ``` @@ -134,6 +134,13 @@ would delay user-engine A2 evidence for no safety gain. Sequence: flex-auth- charts/flex-auth --namespace flex-auth -f values/.yaml`. Isolated canary may boot the same digest with caller-auth disabled. Do not `kubectl apply` an enforce manifest. + **user-engine done 2026-08-19** (Helm release `flex-auth-user-engine` + rev 4). Live objects were kubectl-managed; they were labelled for Helm + adoption first. Two production bugs had to be fixed during warn: the + reviewer volume needed `fsGroup: 65532`, and the reviewer token must + use the API server's default audiences (a custom + `https://kubernetes.default.svc` audience 401s TokenReview on this + k3s). tenant-engine is still the old digest. 3. Confirm warn logs per consumer. `user-engine` is migrated and deployed as of 2026-08-18 (image `sha256:c501aeb2…`, token at `/var/run/secrets/flex-auth-caller/token`). On 2026-08-19 they confirmed @@ -148,15 +155,23 @@ would delay user-engine A2 evidence for no safety gain. Sequence: written in user-engine `docs/flex-auth-caller-identity.md`; they must not run against warn, because "no token returns 401" would still be a decision. + **Done 2026-08-19** from pod `user-engine-8569d7cb87-fp7px`: + (1) Bearer projected token → 200 `decision:350b46fb42989606`; + (2) no Authorization → **401** `unauthenticated`; + (3) `resource.system=tenant-engine` → **403** `forbidden`. + Digests: flex-auth `sha256:138aa347…`, user-engine `sha256:c501aeb2…`. + Live user-engine also needed `fsGroup: 10001` or the projected token + was `0440 root:root` and uid 10001 could not send it — persist that + in rapp-user-engine `manifests/runtime.yaml`. 5. Flip tenant-engine to enforce only after its warn logs are clean. 6. Only then may `policy.enabled` flip anywhere — `ops-warden` names that the real deadline, and it is the same gate as FLEX-WP-0007. -**Operator gate.** Steps 2–5 need cluster credentials this session does not -have: `kubectl` returns `Unauthorized` and the context is `default`, which -`tenant-engine` documented on 2026-08-16 as indistinguishable from a -wrong-cluster KUBECONFIG. Promotion follows FLEX-WP-0011 and the CI image, -not a hand-built image. +**Operator gate.** Default `KUBECONFIG` pointed at HostEurope and is +Unauthorized. railiance01 is `~/.kube/config-railiance01`. user-engine +warn→enforce used that kubeconfig. tenant-engine warn-then-enforce is +the remaining operator step. Promotion follows FLEX-WP-0011 and the CI +image, not a hand-built image. `RISK-F-0001` (risk-nexus) tracks this A0 externally. Their NetworkPolicy question was answered 2026-08-18: both Deployments carry an ingress policy