Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLVI · Deployment EnvironmentsIdentityIsolation

Identity isolation per environment — separate OIDC identities and the blast-radius discipline

Intermediate⏱ ~22 mingit

What you'll learn

  • State the identity-isolation rule: one OIDC identity and one IAM role per environment
  • Explain how short-lived federated credentials scope access to the requesting environment
  • Recognise that identity isolation is blast-radius discipline, not security theatre
  • Configure an environment with a distinct trust policy so a compromise in one environment cannot reach another

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Identity isolation is the discipline that gives every environment its own OIDC identity, its own IAM role, and its own trust policy. A workflow that targets the production environment cannot obtain the development identity; a workflow that targets the development environment cannot obtain the production identity. The boundary is enforced by the platform’s identity provider, not by convention in the workflow file. The discipline is blast-radius control: a compromised development identity cannot reach production, and a compromised production identity has the narrowest possible scope.

The identity-isolation rule

One rule: one OIDC identity per environment. The implementation:

  • A distinct OIDC subject claim issued by the platform for each named environment.
  • A distinct IAM role in the cloud provider that trusts only that subject claim.
  • A distinct trust policy that rejects tokens issued for any other environment.
gh environment create production --protected
gh environment create staging
gh environment create development

The --protected flag marks the environment as a protected object; the platform treats requests for production credentials with the full protection rule set. The development and staging environments have their own identity pools.

flowchart LR
    A["Workflow runs"] --> B{"Target environment?"}
    B -- "development" --> C["OIDC subject: env:development"]
    B -- "staging" --> D["OIDC subject: env:staging"]
    B -- "production" --> E["OIDC subject: env:production"]
    C --> F["Dev IAM role"]
    D --> G["Staging IAM role"]
    E --> H["Production IAM role"]
    F --> I["Dev resources only"]
    G --> J["Staging resources only"]
    H --> K["Production resources only"]

The diagram is the identity-isolation pattern. Each workflow target resolves to a distinct OIDC subject; each subject is trusted by exactly one IAM role; each IAM role has permissions scoped to one environment’s resources. A token issued for development cannot be exchanged for a production IAM role because the production IAM role’s trust policy does not list the development subject.

How short-lived credentials enforce the boundary

The platform issues a short-lived OIDC token for each job. The token’s sub (subject) claim identifies the environment:

  • repo:myorg/myrepo:environment:development
  • repo:myorg/myrepo:environment:staging
  • repo:myorg/myrepo:environment:production

The job exchanges the OIDC token for a cloud-provider credential by assuming the IAM role whose trust policy matches the subject. The credential is short-lived - typically minutes - and scoped to the IAM role’s permissions.

The exchange is the enforcement point. The IAM role’s trust policy is a JSON document that lists the exact subject claims it will accept. A token with the wrong subject fails the trust-policy check; the role assumption fails; the workflow job cannot obtain credentials.

The blast-radius discipline

Identity isolation is blast-radius discipline. The blast radius of a compromised identity is the set of resources the identity can write. An identity that can write to development and production has a blast radius that spans both environments; an identity that can write only to development has a blast radius that ends at the development boundary.

The discipline applies to every kind of compromise:

  • Compromised runner. A self-hosted runner that is compromised can run workflows with whatever credentials the runner can obtain. If the runner is registered against all three environments, the compromise has access to all three. If the runner is scoped to development, the compromise is contained.
  • Compromised dependency. A malicious dependency injected into the build can exfiltrate the OIDC token. The token is scoped to the workflow’s environment; the exfiltrated token cannot be exchanged for credentials outside that scope.
  • Compromised engineer credential. An engineer with the ability to trigger workflows in development and staging, but not in production, has a compromised credential that cannot reach production. The production trigger requires additional platform permissions; the engineer credential is not one of them.
  • Compromised CI configuration. A workflow file that targets the wrong environment cannot obtain the credentials for that environment. The OIDC token’s subject is determined by the platform, not by the workflow file; a misconfigured environment: value is a misconfigured target, not a misconfigured identity.

Configuring the trust policy

The IAM role’s trust policy is the enforcement point. For an environment named production:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:environment:production"
        }
      }
    }
  ]
}

The Condition clause restricts the trust to a specific environment subject. A token with subject environment:development fails the condition; the role assumption is denied. The role’s permission policy (scoped to production resources) is not consulted because the assumption fails first.

Common failure modes

Three failure modes appear repeatedly when teams adopt identity isolation:

  • One role, multiple subjects. A single IAM role whose trust policy lists all three environment subjects. The role’s permissions are shared; the isolation is missing. Fix: split into three roles.
  • Permissions wider than the environment. A development IAM role with permissions to read production resources (for debugging, the team says). The role is no longer scoped to development. Fix: remove the production permissions; if debugging requires them, the debugging workflow targets production with a separate, narrowly-scoped role.
  • Tokens reused across environments. Long-lived tokens cached on a runner, then reused when the runner’s environment changes. The token’s subject is the original environment; the new environment gets the old credentials. Fix: short-lived tokens issued per job, no on-runner caching.

Production discipline

  1. One IAM role per environment. Never share a role across environments.
  2. Trust policy lists only the environment’s subject. A development subject cannot assume the production role.
  3. Permissions scoped to the environment’s resources. A development role cannot write to production resources, even by accident.
  4. Short-lived tokens only. No long-lived access keys; no on-runner credential caching.

Cross-course references

  • This course, Part LVI-01 (Environments) establishes the boundary model this lesson enforces at the identity layer.
  • This course, Part XLIV-04 (Provenance) covers the signing chain that identity isolation extends with subject claims.
  • AWS for Production Sysadmins — Part IX (OIDC) covers the AWS-side configuration of trust policies.
  • Terraform for Production Sysadmins — Part XXIV (StateLock) covers the identity pattern for state locking across environments.

Quiz

Knowledge check · 4 questions

  1. Q1. A team creates one IAM role for all environments to simplify configuration. The role's trust policy lists all three environment subjects. What blast-radius discipline has been lost?

  2. Q2. An IAM role scoped to production resources can be safely assumed by a development workflow if the role's trust policy lists the development subject, because the workflow's `environment:` value determines which resources it can reach.

  3. Q3. Name the two IAM components that enforce identity isolation per environment and identify which component determines the blast radius of a compromised credential.

  4. Q4. Diagnose why a development compromise reached production despite identity isolation being configured, and propose the boundary that should have stopped it.

    A team has three IAM roles, one per environment, with trust policies scoped to each environment's subject. A developer adds a debugging step to a development workflow that reads from the production secrets store. The step assumes the development IAM role (correct), but the development IAM role's permission policy includes `s3:GetObject` on `arn:aws:s3:::production-config-bucket/*` because the team needed to read production configuration for a staging debug. The development workflow reads production configuration successfully. A malicious dependency exfiltrates the production configuration.

Passing score: 75%. Answers are checked in this browser.