Skip to content

Security hardening: GitHub Actions OIDC roles are trusted with a repo-wide wildcard subject #1289

Description

@bjagg

Summary

The IAM roles GitHub Actions assumes are trusted with a repo-wide wildcard subject rather than one scoped to the workflows that need them. The trust condition does not distinguish a deploy workflow on main from any other workflow in the repository, so the deploy roles for both dev and demo are reachable from a broader set of contexts than the deploy path itself.

Filed as hardening, not as a live incident: the wildcard has not been exploited, nothing is currently mis-assuming these roles, and the practical reach is limited to people who already have write access (see Scope).

Current state

Three roles trust this GitHub org via OIDC:

role trusted subject reachable by
lif-github-actions-dev repo:LIF-Initiative/lif-core:* any workflow context in the repo
lif-github-actions-demo repo:LIF-Initiative/lif-core:* any workflow context in the repo
lif-github-actions repo:LIF-Initiative/lif-metadata-repository:*, repo:LIF-Initiative/lif-main:* two retired repos

The two active roles hold permissions appropriate to deployment — including the ability to update ECS services and to read individual SSM parameters. That is correct for the deploy path. The issue is only that the trust condition does not confine them to it.

lif-github-actions is separately worth attention: it trusts two repositories that were consolidated into lif-core (see the workspace CLAUDE.md on repo lineage) and currently has no attached managed policy. A role trusting retired repos should be removed rather than left dormant.

Why it is worth fixing now

Today the exposure is bounded mostly by accident rather than by design — no current workflow abuses it, and the contexts that could are limited to actors who already hold write access. Both of those are properties of how the repo happens to be used, not guarantees the trust policy provides.

It is also load-bearing for the design of #1288. That issue proposes a task-definition drift check, and the obvious shortcut is to hand PR CI the existing deploy role — which the wildcard permits with no IAM change. That shortcut should not be taken, and this issue is why.

Proposed remediation

  1. Replace the wildcard subject with scoped conditions. Trust the specific contexts that deploy — for example the main ref and/or a named GitHub Environment — rather than repo:LIF-Initiative/lif-core:*. GitHub's OIDC subject supports ref, environment, and job_workflow_ref claims for exactly this.
  2. Prefer a GitHub Environment as the boundary. Binding deploy roles to an Environment also gets required reviewers and branch restrictions for free, which is a stronger control than a ref match alone.
  3. Add a separate read-only role for checks, with only the describe permissions a verification job needs and none of the mutating ones. This is what CF/CI: a merged change can require task-definition env values that CI never applies, and nothing reports the drift #1288's PR-time check should use.
  4. Delete lif-github-actions, or re-point it if either predecessor repo is genuinely still deploying.
  5. Re-verify after each change by confirming the deploy workflows still assume successfully and that a non-deploy context cannot — the same bar CF/CI: deploy workflow paths: filters miss packaged bricks — a brick-only change merges green and never rebuilds the image (9 of 11 services affected) #1171 and CF/CI: add a PR-time guard for stale per-project uv.lock files (crash-looped a service in #1125, nearly again in #1174) #1209 were held to: prove the control by observing it deny, not only by observing the happy path pass.

Scope / severity

  • Not remotely exploitable by an anonymous party. Workflows triggered by pull_request from a fork are not issued an OIDC token at all, which excludes the untrusted case.
  • The realistic reach is someone who can already push a branch to this repository — i.e. already trusted. This is privilege-shaping within a trusted group, not an external hole.
  • No evidence of misuse; this is a configuration review finding.

Note on venue

lif-core is a public repository and private vulnerability reporting is currently disabled, so this write-up is deliberately limited to the misconfiguration class and its remediation. It contains no exploitation steps. If the team wants to track specifics — exact permission inventories, or which contexts can reach what — enable private vulnerability reporting (Settings → Security) or move that detail to a private tracker, and link it from here rather than pasting it in.

Related

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions