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
4 changes: 3 additions & 1 deletion docs/platform-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ GitHub OIDC provider (data source), CI roles, permission boundary, ECS execution
| `javabin-ci-team-{team}` | CI broker and apply gate Lambdas (not OIDC) | Team TF plan + apply, scoped to the team by the developer boundary |
| `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-ci-registry` | Registry repo, `main` only | Team provisioning dispatch |
| `javabin-ecs-execution` | ECS tasks | Pull images, write logs, read secrets |

**Key design:** `job_workflow_ref` condition prevents app repos from writing rogue workflows.

**OIDC subject formats:** every OIDC trust policy accepts both the legacy subject (`repo:javaBin/<repo>:...`) and GitHub's immutable subject (`repo:javaBin@95047/<repo>@<repo_id>:...`), which new repos get by default and can't opt out of. The org-wide roles (`ci-app-broker`, `ci-override-approver`, `ci-apply-gate`) pin the org ID (`github_org_id`). The platform and registry roles also pin the repo ID (`github_platform_repo_id`, `github_registry_repo_id` in `iam/variables.tf`); look one up with `gh api repos/javaBin/<repo> --jq .id`.

## compute

ECS Fargate cluster + CI ECR repositories.
Expand Down
31 changes: 26 additions & 5 deletions terraform/platform/iam/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ data "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
}

# GitHub's immutable OIDC subject replaces "<org>/<repo>" with
# "<org>@<org_id>/<repo>@<repo_id>". New repos get it by default and can't
# opt out; existing repos can opt in. The infra roles below accept both forms,
# with the immutable one pinned to the exact repo ID so a renamed or
# recreated repo with the same name can't match.
locals {
platform_immutable_repo = "${var.github_org}@${var.github_org_id}/platform@${var.github_platform_repo_id}"
registry_immutable_repo = "${var.github_org}@${var.github_org_id}/registry@${var.github_registry_repo_id}"
}

################################################################################
# Permission Boundary Reference
#
Expand All @@ -22,7 +32,8 @@ data "aws_iam_openid_connect_provider" "github" {
################################################################################
# 1a. javabin-ci-infra-plan — Platform repo plan + review (read-only)
#
# Trust: GitHub OIDC, repo:javaBin/platform on ANY ref (PRs need plan too).
# Trust: GitHub OIDC, repo:javaBin/platform on ANY ref (PRs need plan too),
# legacy or immutable (repo:javaBin@95047/platform@<repo_id>) subject.
# Permissions: ReadOnlyAccess + specific writes for plan operations:
# - S3: upload plan artifacts
# - DynamoDB: Terraform state locking
Expand Down Expand Up @@ -50,8 +61,12 @@ resource "aws_iam_role" "ci_infra_plan" {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
# Any ref — PRs and main both need plan
"token.actions.githubusercontent.com:sub" = "repo:${var.github_org}/platform:*"
# Any ref — PRs and main both need plan. The second form is
# GitHub's immutable subject, pinned to the platform repo ID.
"token.actions.githubusercontent.com:sub" = [
"repo:${var.github_org}/platform:*",
"repo:${local.platform_immutable_repo}:*",
]
}
}
}
Expand Down Expand Up @@ -179,6 +194,8 @@ resource "aws_iam_role" "ci_infra" {
"token.actions.githubusercontent.com:sub" = [
"repo:${var.github_org}/platform:ref:refs/heads/main",
"repo:${var.github_org}/platform:environment:production",
"repo:${local.platform_immutable_repo}:ref:refs/heads/main",
"repo:${local.platform_immutable_repo}:environment:production",
]
# The job_workflow_ref pin is what ties this role to main: only
# platform-ci.yml as committed on main can assume it, whatever
Expand Down Expand Up @@ -820,7 +837,8 @@ resource "aws_iam_role_policy" "ci_override_approver" {
################################################################################
# 5. javabin-ci-registry — Registry repo team provisioning
#
# Trust: GitHub OIDC pinned to javaBin/registry on main branch.
# Trust: GitHub OIDC pinned to javaBin/registry on main branch (legacy or
# immutable subject, the latter pinned to the registry repo ID).
# Permissions: ONLY lambda:InvokeFunction on the team-provisioner Lambda.
# This lets the registry invoke the Lambda directly on merge, without
# dispatching through the platform repo.
Expand All @@ -844,7 +862,10 @@ resource "aws_iam_role" "ci_registry" {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:${var.github_org}/registry:ref:refs/heads/main"
"token.actions.githubusercontent.com:sub" = [
"repo:${var.github_org}/registry:ref:refs/heads/main",
"repo:${local.registry_immutable_repo}:ref:refs/heads/main",
]
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions terraform/platform/iam/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ variable "github_org_id" {
default = "95047"
}

variable "github_platform_repo_id" {
description = "GitHub repository ID of <org>/platform, pinned in the immutable OIDC subject of the ci-infra and ci-infra-plan roles"
type = string
default = "1175354559"
}

variable "github_registry_repo_id" {
description = "GitHub repository ID of <org>/registry, pinned in the immutable OIDC subject of the ci-registry role"
type = string
default = "1175355770"
}

variable "apply_gate_role_arn" {
description = "ARN of the apply-gate Lambda role (for team role trust policy)"
type = string
Expand Down
Loading