From 02391e80989c3b35a2278b4371902feea6d0ec51 Mon Sep 17 00:00:00 2001 From: Alexanderamiri Date: Sat, 31 Oct 2026 13:03:59 +0100 Subject: [PATCH 1/2] fix(docs): describe per-team ECS execution roles and the Google OIDC identity module cognito-app-client and iam comments, platform-modules.md and reusable-modules.md still described a shared execution role reading every team's secrets, and platform-modules.md called the identity module unimplemented and SAML-based. Co-Authored-By: Claude Opus 5.5 --- docs/platform-modules.md | 6 +++--- docs/reusable-modules.md | 2 +- terraform/modules/cognito-app-client/main.tf | 3 ++- terraform/platform/iam/main.tf | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/platform-modules.md b/docs/platform-modules.md index 4b958ea..7cf60ae 100644 --- a/docs/platform-modules.md +++ b/docs/platform-modules.md @@ -57,7 +57,8 @@ GitHub OIDC provider (data source), CI roles, permission boundary, ECS execution | `javabin-ci-deploy-{team}` | CI broker Lambda (not OIDC) | ECR push, ECS deploy | | `javabin-ci-override-approver` | Board members, pinned to `approve-override.yml` | SSM override tokens | | `javabin-ci-registry` | Registry repo | Team provisioning dispatch | -| `javabin-ecs-execution` | ECS tasks | Pull images, write logs, read secrets | +| `javabin-ecs-exec-{team}` | ECS tasks | Pull images, write logs, read the team's own secrets (`/javabin/apps/{team}/*`). One per registry team | +| `javabin-ecs-execution` | ECS tasks | Deprecated shared role: image pull and logs only, no secrets. Teams can no longer pass it | **Key design:** `job_workflow_ref` condition prevents app repos from writing rogue workflows. @@ -115,5 +116,4 @@ Team SSO roles' `CredentialAccess:IAMUser/AnomalousBehavior` on `ssm:GetParamete ## identity -**Not yet implemented.** Will contain IAM Identity Center + Cognito user pools. -Blocked on Google Admin access for SAML federation. +Cognito user pools (internal + external) and the `auth-claims` trigger. The internal pool signs in through Google OIDC (provider `Google`, from the `internal_identity_providers` variable); the hand-made `GoogleSAML` provider is still in the pool but no longer offered on app clients. See [cognito-google-setup.md](cognito-google-setup.md). IAM Identity Center is in `terraform/org/`, not here. diff --git a/docs/reusable-modules.md b/docs/reusable-modules.md index 9c36146..7212a0e 100644 --- a/docs/reusable-modules.md +++ b/docs/reusable-modules.md @@ -16,7 +16,7 @@ source = "git::https://github.com/javaBin/platform.git//terraform/modules/{name} | Security group | `javabin-ecs-tasks-sg` | | ALB | `javabin-alb` | | ECS cluster | `javabin-platform` | -| Execution role | `javabin-ecs-execution` | +| Execution role | `javabin-ecs-exec-{team}` (from the `team` input; reads only that team's secrets) | | Route53 zones | `javazone.no` (`route53_zone_id`); `javazone.no`, `java.no`, `javabin.no` (`route53_zone_ids` map) | **Key outputs:** `vpc_id`, `private_subnet_ids`, `ecs_cluster_id`, `https_listener_arn`, `execution_role_arn`, `route53_zone_id`, `route53_zone_ids` diff --git a/terraform/modules/cognito-app-client/main.tf b/terraform/modules/cognito-app-client/main.tf index 06e7e45..9249ce2 100644 --- a/terraform/modules/cognito-app-client/main.tf +++ b/terraform/modules/cognito-app-client/main.tf @@ -18,7 +18,8 @@ # # Those parameters are owned by the Lambda, not by this Terraform. Their ARNs # are deterministic, so the ECS task definition can reference them up front; -# the shared execution role reads //apps/* at task launch. +# the team's execution role (-ecs-exec-, which can read only +# //apps//*) resolves them at task launch. # # With `slot` set (e.g. "login-1", used by service-routing for ALB sign-in), # everything moves one level down — //apps////auth — diff --git a/terraform/platform/iam/main.tf b/terraform/platform/iam/main.tf index a876816..b91b985 100644 --- a/terraform/platform/iam/main.tf +++ b/terraform/platform/iam/main.tf @@ -948,10 +948,10 @@ resource "aws_iam_role_policy" "ci_apply_gate" { } ################################################################################ -# ECS Execution Role — pulls images, writes logs, reads secrets +# ECS Execution Role (shared, deprecated) — pulls images, writes logs # -# This is the shared execution role for all ECS tasks on the platform. -# Individual task roles are created per-app via the service-role module. +# Tasks now use the per-team execution roles below. Individual task roles are +# created per-app via the service-role module. ################################################################################ # Deprecated shared execution role. Kept (image pull + logs only, no secrets) From 1cc0885d281c3ca687ef86b2d61b0d8856986091 Mon Sep 17 00:00:00 2001 From: Alexanderamiri Date: Sat, 31 Oct 2026 13:08:51 +0100 Subject: [PATCH 2/2] docs(identity): record Google OIDC cutover and missing SSO team tag Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 4 ++-- terraform/org/identity-center.tf | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6e8b747..884c8b7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -305,7 +305,7 @@ Team group OWNERs are admin-managed and never removed by the sync; non-@java.no | 0a | AWS Discovery | **Done** | | 0b | Bootstrap State Backend | **Done** — S3 backend live | | 0c | Organizations + Permission Boundary | **Done** — org enabled, boundary deployed, SCP deferred | -| 1 | Identity (Google + Identity Center + Cognito) | **Deployed** — GCP SA with domain-wide delegation, Identity Center with ABAC + 3 permission sets in `terraform/org/`. Google Workspace SAML IdP for SSO (auto-provisions users, groups synced via CI/team-provisioner). Cognito pool TF exists but not yet applied (needs Google OAuth client). | +| 1 | Identity (Google + Identity Center + Cognito) | **Deployed** — GCP SA with domain-wide delegation, Identity Center with ABAC + 3 permission sets in `terraform/org/`. Google Workspace SAML IdP for SSO (auto-provisions users, groups synced via CI/team-provisioner). Cognito pools deployed; the internal pool signs in with the Terraform-managed Google OIDC IdP. | | 2a | Networking | **Deployed** — VPC, subnets, NAT | | 2b | Ingress | **Deployed** — ALB + ACM cert | | 2c | IAM / OIDC | **Deployed** — 6 CI roles (infra, infra-plan, per-app, deploy, override-approver, registry), team-prefixed naming + permission boundary | @@ -324,7 +324,7 @@ Team group OWNERs are admin-managed and never removed by the sync; non-@java.no ### Known Issues - **ECS deploy stabilization**: platform-test-app task registers but service fails health check -- **Cognito**: pools deployed; internal pool's `GoogleSAML` IdP was created outside Terraform. It is replaced by the Terraform-managed Google OIDC IdP at cutover (docs/cognito-google-setup.md), then deleted +- **Cognito**: pools deployed; the internal pool has been cut over to the Terraform-managed Google OIDC IdP (`internal_identity_providers` defaults to `["Google"]`). The old `GoogleSAML` IdP, created outside Terraform, is still in the pool but offered on no app client until it is deleted (docs/cognito-google-setup.md, "Decommission GoogleSAML") - **SSO sessions carry no `team` tag**: the Identity Center ABAC attribute (`${path:enterprise.team}`) is never set on users, so `${aws:PrincipalTag/team}` is empty for humans. Per-team permission sets (`javabin-team-{team}`) therefore use `javabin-team-{team}-guardrails` (the developer boundary rendered with the team name) instead. The ABAC-based `javabin-developer` permission set in `terraform/org` cannot reach team resources - **App databases share one network path** (fixed by `app-network`): service-rds now allows ingress only from the app's own `{team}-{app}-tasks` SG; each app moves off the shared ECS tasks SG on its next pipeline run - **Team provisioner Lambda**: All sync functions working (Google/GitHub/Budget/Cognito/Identity Center). Password-set flow deployed. diff --git a/terraform/org/identity-center.tf b/terraform/org/identity-center.tf index 9f18d29..c47bdec 100644 --- a/terraform/org/identity-center.tf +++ b/terraform/org/identity-center.tf @@ -7,9 +7,14 @@ # Prerequisite: Google Workspace SAML sync must be configured first so that # Identity Center groups (infra@, heroes@, board@, pkom@) exist. # -# ABAC: The "team" attribute flows from Google group membership through -# SAML assertion → Identity Center → STS session tag. This lets a single -# JavabinDeveloper permission set work for all teams. +# ABAC: The "team" attribute was meant to flow from Google group membership +# through SAML assertion → Identity Center → STS session tag, so a single +# JavabinDeveloper permission set would work for all teams. +# +# NOTE: the "team" attribute is never populated on Identity Center users, so +# SSO sessions carry no team tag and javabin-developer gets no access to team +# resources. Developers use the per-team permission sets (javabin-team-{team}) +# created by the team-provisioner Lambda instead (see CLAUDE.md, Known Issues). ################################################################################ data "aws_ssoadmin_instances" "this" {} @@ -20,6 +25,7 @@ locals { ################################################################################ # Access Control Attributes (ABAC) — map "team" from SAML assertion +# (the source attribute is never set today; see the ABAC note above) ################################################################################ resource "aws_ssoadmin_instance_access_control_attributes" "this" { @@ -54,7 +60,8 @@ resource "aws_ssoadmin_managed_policy_attachment" "admin" { ################################################################################ # Permission Set: JavabinDeveloper # ABAC-scoped access for heroes. Uses permission boundary. -# Team tag from SAML assertion enables access to team-tagged resources. +# Team tag from SAML assertion was meant to enable access to team-tagged +# resources; no team tag is set, so this grants no team access (see above). ################################################################################ resource "aws_ssoadmin_permission_set" "developer" {