Skip to content

fix(iam): accept immutable OIDC subjects on ci-infra, ci-infra-plan and ci-registry - #178

Merged
Alexanderamiri merged 1 commit into
mainfrom
fix/oidc-immutable-infra-roles
Oct 31, 2026
Merged

Alexanderamiri merged 1 commit into
mainfrom
fix/oidc-immutable-infra-roles

Conversation

@Alexanderamiri

Copy link
Copy Markdown
Member

Problem

PR #168 made the org-wide CI roles accept GitHub's immutable OIDC subject (repo:javaBin@95047/<repo>@<repo_id>:...). The platform and registry roles were not changed and still match only the legacy subject:

  • javabin-ci-infra-plan: repo:javaBin/platform:*
  • javabin-ci-infra: repo:javaBin/platform:ref:refs/heads/main / :environment:production
  • javabin-ci-registry: repo:javaBin/registry:ref:refs/heads/main

Both repos use the legacy format today (use_immutable_subject: false according to gh api repos/javaBin/{platform,registry}/actions/oidc/customization/sub). If either repo opts in, or is ever recreated (new repos can't opt out), platform plan/apply and team provisioning stop with Not authorized to perform sts:AssumeRoleWithWebIdentity.

Change

  • New github_platform_repo_id (default 1175354559) and github_registry_repo_id (default 1175355770) variables in iam/variables.tf, from gh api repos/javaBin/<repo> --jq .id.
  • Each of the three roles now accepts the legacy subject and the immutable one. The immutable subject is pinned to the org ID and the exact repo ID, and keeps the same ref/environment restriction. The job_workflow_ref pin on ci-infra is unchanged.
  • docs/platform-modules.md describes the two subject formats and where the IDs are pinned.

I also searched the rest of the repo (workflows, scripts, Lambdas, terraform/org) for code that parses or matches sub. Nothing else needs a change. slack_alert already strips the IDs (#168), and ci_broker/apply_gate (shared/github_oidc.py) check the repository_owner, repository and job_workflow_ref claims, not sub. The other "sub" hits are Google service-account JWTs and Cognito, which have nothing to do with GitHub OIDC.

Verification

  • terraform fmt -recursive -check and terraform validate in terraform/platform pass.
  • A read-only targeted plan (-lock=false, targeting the three roles) shows 0 to add, 3 to change, 0 to destroy. The only change is the sub condition, which becomes a list with the legacy and the immutable form, for example repo:javaBin@95047/platform@1175354559:*.
  • python3 -m unittest discover -s terraform/lambda-src/tests passes (no Lambda changes).

🤖 Generated with Claude Code

…nd ci-registry

The platform and registry roles only matched repo:javaBin/<repo>:..., so
switching either repo to GitHub's immutable subject format would lock CI
out. Accept repo:javaBin@95047/<repo>@<repo_id>:... too, pinned to the
repo IDs via new github_platform_repo_id / github_registry_repo_id
variables, with the same ref/environment restrictions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Alexanderamiri
Alexanderamiri requested a review from a team as a code owner October 31, 2026 12:02
@github-actions

Copy link
Copy Markdown

Terraform Plan

🚧 Changes detected — Plan: 0 to add, 3 to change, 0 to destroy.

Plan output
Acquiring state lock. This may take a few moments...
aws_ssm_parameter.atlassian_org_id: Refreshing state... [id=/javabin/platform/atlassian-org-id]
aws_ssm_parameter.atlassian_api_key: Refreshing state... [id=/javabin/platform/atlassian-api-key]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # module.iam.aws_iam_role.ci_infra will be updated in-place
  ~ resource "aws_iam_role" "ci_infra" {
      ~ assume_role_policy    = jsonencode(
          ~ {
              ~ Statement = [
                  ~ {
                      ~ Condition = {
                          ~ StringLike   = {
                              ~ "token.actions.githubusercontent.com:sub"              = [
                                    # (1 unchanged element hidden)
                                    "repo:javaBin/platform:environment:production",
                                  + "repo:javaBin@95047/platform@1175354559:ref:refs/heads/main",
                                  + "repo:javaBin@95047/platform@1175354559:environment:production",
                                ]
                                # (1 unchanged attribute hidden)
                            }
                            # (1 unchanged attribute hidden)
                        }
                        # (3 unchanged attributes hidden)
                    },
                ]
                # (1 unchanged attribute hidden)
            }
        )
        id                    = "javabin-ci-infra"
        name                  = "javabin-ci-infra"
        tags                  = {
            "Name" = "javabin-ci-infra"
        }
        # (9 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

  # module.iam.aws_iam_role.ci_infra_plan will be updated in-place
  ~ resource "aws_iam_role" "ci_infra_plan" {
      ~ assume_role_policy    = jsonencode(
          ~ {
              ~ Statement = [
                  ~ {
                      ~ Condition = {
                          ~ StringLike   = {
                              ~ "token.actions.githubusercontent.com:sub" = "repo:javaBin/platform:*" -> [
                                  + "repo:javaBin/platform:*",
                                  + "repo:javaBin@95047/platform@1175354559:*",
                                ]
                            }
                            # (1 unchanged attribute hidden)
                        }
                        # (3 unchanged attributes hidden)
                    },
                ]
                # (1 unchanged attribute hidden)
            }
        )
        id                    = "javabin-ci-infra-plan"
        name                  = "javabin-ci-infra-plan"
        tags                  = {
            "Name" = "javabin-ci-infra-plan"
        }
        # (9 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # module.iam.aws_iam_role.ci_registry will be updated in-place
  ~ resource "aws_iam_role" "ci_registry" {
      ~ assume_role_policy    = jsonencode(
          ~ {
              ~ Statement = [
                  ~ {
                      ~ Condition = {
                          ~ StringLike   = {
                              ~ "token.actions.githubusercontent.com:sub" = "repo:javaBin/registry:ref:refs/heads/main" -> [
                                  + "repo:javaBin/registry:ref:refs/heads/main",
                                  + "repo:javaBin@95047/registry@1175355770:ref:refs/heads/main",
                                ]
                            }
                            # (1 unchanged attribute hidden)
                        }
                        # (3 unchanged attributes hidden)
                    },
                ]
                # (1 unchanged attribute hidden)
            }
        )
        id                    = "javabin-ci-registry"
        name                  = "javabin-ci-registry"
        tags                  = {
            "Name" = "javabin-ci-registry"
        }
        # (9 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 3 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

LLM Review

Risk: 🔴 HIGH

Plan updates the trust policies (assume_role_policy) of three existing CI IAM roles (ci_infra, ci_infra_plan, ci_registry) to add GitHub OIDC subject claims for a repo path "javaBin@95047/..." that does not look like a legitimate javaBin repository, which would let that external-looking GitHub identity assume deploy-capable roles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant