CertNote/ Commute Study Notes
Roadmap
KOEN
CLF-C02 · FoundationalCloud Practitioner - Foundational
DVA-C02 · AssociateDeveloper - Associate
SAA-C03 · AssociateSolutions Architect - Associate
SOA-C02 · AssociateCloudOps Engineer - Associate
SAP-C02 · ProfessionalSolutions Architect - Professional
DOP-C02 · ProfessionalDevOps Engineer - Professional
  • Week 1
    • 1.DevOps as an Operating Model: The Five Axes of CALMS and the Truth DORA Proved Through Measurement
    • 2.Well-Architected Framework: Rereading It Through the Six Lenses of DevOps
    • 3.The AWS DevOps Tool Map: The Code* Series and the Real Picture Beyond It
    • 4.Multi-Account Strategy: The Real Picture of Organizations, Control Tower, and IAM Identity Center
    • 5.Week 1 Wrap-Up: Cementing the DevOps Thinking Frame with Scenarios
  • Week 2
    • 1.CodeCommit Deep Dive: What Changes When Git Hosting is Integrated with IAM
    • 2.GitHub Actions ↔ AWS OIDC: Eliminating Static Keys Permanently Through Federation
    • 3.CodeArtifact and Supply Chain Security: When Dependencies Become Attack Surface
    • 4.DevSecOps Shift Left: Automated Security Gates Built with Code Signing, CodeGuru, and Inspector
    • 5.Week 2 Wrap-Up: From Source Control to Code Signing, Cementing the DevSecOps Mindset Through Scenarios
  • Week 3
  • Week 4
  • Week 5
  • Week 6
  • Week 7
  • Week 8
  • Week 9
  • Week 10
  • Week 11
  • Week 12
  • Week 13
  • Week 14
  • Week 15
  • Week 16
Linux Master 2 · 2급KAIT Linux Master - Level 2
SCS-C03 · SpecialtySecurity - Specialty
Linux Master 1 · 1급KAIT Linux Master - Level 1
MLA-C01 · AssociateMachine Learning Engineer - Associate
AIF-C01 · FoundationalAI Practitioner - Foundational
DEA-C01 · AssociateData Engineer - Associate
MLS-C01 · SpecialtyMachine Learning - Specialty
SQLD · 개발자SQL Developer (SQLD)

© CertNote · 주간랩스

  • About
  • Changelog
  • Contact
  • Terms
  • Privacy
← DOP-C02/Week 2/Day 2

Based on the DOP-C02 exam guide·Report an error

DOP-C02· ProWeek 2 · Day 2~35 min read

Day 2 - GitHub Actions ↔ AWS OIDC: Eliminating Static Keys Permanently Through Federation

For the past decade, securely handling AWS credentials in CI/CD systems has been a chronic headache for nearly every DevOps team. Embedding IAM User access keys in GitHub Secrets is familiar, but that familiarity is itself the source of incidents. According to a 2022 Snyk report, AWS access keys discovered in public GitHub repositories exceeded 10,000 per year. Capital One's 2019 incident, Twitter's 2023 internal key leak—the root cause is the same: "static credentials issued once, copied to unknown locations, lost to audit trails".

OIDC federation solves this problem from a fundamentally different direction. It doesn't issue credentials. Instead, GitHub creates a short-lived JWT proving its identity every workflow run, and AWS STS validates it to issue a 1-hour temporary credential. Since static keys don't exist, there's nothing to leak.

Today covers: ① the exact mechanism of how this works and its six-step flow ② which conditions in the IAM Trust Policy determine security ③ how it combines with GitHub Environments to create dual defense ④ what trade-offs exist with Self-hosted Runners and CodeBuild.

Exact Sequence of How OIDC Federation Works — Six Steps Where JWT Becomes STS Credential

Let's view one complete flow of GitHub Actions calling AWS APIs in slow motion.

1. GitHub Actions runner starts workflow
   permissions: id-token: write must be present
        |
        v
2. runner requests token from GitHub OIDC provider
   POST https://token.actions.githubusercontent.com/...
   Body: { audience: "sts.amazonaws.com" }
        |
        v
3. GitHub issues JWT
   header: { alg: "RS256", kid: "..." }
   payload: {
     iss: "https://token.actions.githubusercontent.com",
     aud: "sts.amazonaws.com",
     sub: "repo:my-org/my-app:environment:production",
     ref: "refs/heads/main",
     repository: "my-org/my-app",
     workflow: "Deploy",
     ...
   }
   signature: RSA signature (verifiable via JWKS endpoint)
        |
        v
4. aws-actions/configure-aws-credentials@v4 calls STS
   AssumeRoleWithWebIdentity {
     RoleArn: "arn:aws:iam::ACCOUNT:role/GHActionsProdRole",
     WebIdentityToken: <above JWT>,
     RoleSessionName: "github-actions-<run-id>"
   }
        |
        v
5. STS validates
   - JWT signature verification (GitHub JWKS endpoint)
   - iss == OIDC Provider URL
   - aud matches trust policy condition
   - sub matches trust policy condition (StringEquals/StringLike)
   - Role's trust policy is "Allow"
        |
        v
6. Returns temporary credentials (default 1h, max 12h)
   AccessKeyId: "ASIA..."
   SecretAccessKey: "..."
   SessionToken: "..."
   Expiration: "..."

Let's examine each stage's details.

🔍 Deep Dive: The JWT issued in step 2 is signed with RS256 (RSA signature), and AWS STS validates it by retrieving the public key from GitHub's JWKS endpoint (https://token.actions.githubusercontent.com/.well-known/jwks). When registering an IAM Identity Provider, you were asked for a thumbprint—this is that step: pre-trusting GitHub's identity via TLS certificate thumbprint. Since July 2023, AWS began auto-validating thumbprints for well-known IdPs (GitHub, GitLab, etc.), reducing the need for manual entry. This change prevents incidents during thumbprint rotation.

💡 Related Theory: OIDC (OpenID Foundation 2014) is a standard that adds an authentication layer on top of OAuth 2.0 (RFC 6749, 2012). While OAuth 2.0 handles "authorization delegation", OIDC handles "proving user identity". The key is an ID Token, a JWT (RFC 7519), containing standard claims (iss, sub, aud, exp, iat, nbf) that the IdP issued it, enabling self-verification. SAML 2.0 (OASIS 2005) serves the same role but is XML-based and heavy for mobile/API environments. AWS supports both, but GitHub Actions integration uses OIDC only.

📚 Case Study: In 2022, a fintech company discontinued IAM User access key policy and migrated to GitHub Actions OIDC. Immediately after, a workflow stopped working. Root cause: they forgot the permissions: block in the workflow YAML, and GitHub defaults to id-token: none, so it doesn't issue OIDC tokens. The error message was cryptic (Could not assume role). After 30 minutes of debugging, they found it. Afterward, the team made permissions: id-token: write, contents: read standard in all workflow templates. In exams, when "OIDC isn't working → what to check first?", the answer is almost always this one line.

The sub Condition in Trust Policy — Security Hinges Here

The security of OIDC integration depends almost entirely on how precise the sub condition in Trust Policy is. If sub is a wildcard, the blast radius of a breach explodes.

The sub value GitHub issues in JWT varies by context:

Workflow Contextsub Value
push to mainrepo:my-org/my-repo:ref:refs/heads/main
push to feature/xrepo:my-org/my-repo:ref:refs/heads/feature/x
pull_requestrepo:my-org/my-repo:pull_request
Tag pushrepo:my-org/my-repo:ref:refs/tags/v1.0
Environment usedrepo:my-org/my-repo:environment:production
Reusable workflowrepo:my-org/my-repo:job_workflow_ref:org/shared/.github/workflows/deploy.yml@refs/heads/main

How you write the trust policy condition determines security level:

// 1) Too loose — any repo in org with main branch can assume Role
"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:my-org/*:ref:refs/heads/main"
}
 
// 2) Moderate — only specific repo's main branch
"StringEquals": {
  "token.actions.githubusercontent.com:sub": "repo:my-org/my-app:ref:refs/heads/main"
}
 
// 3) Safest — specific repo + specific environment + workflow passing Required Reviewers only
"StringEquals": {
  "token.actions.githubusercontent.com:sub": "repo:my-org/my-app:environment:production"
}

⚠️ Trap: The danger of pattern 1 is a frequent exam and real-world topic. With 100 repos in the org, if even one is compromised, that repo's main branch can assume prod Role. In exams, when asked "what's the risk of wildcard repo:org/* sub condition?", the answer is always "if any other repo is compromised, prod credentials can be stolen."

🔍 Deep Dive: Pattern 3's environment:production is safest because GitHub Environment can have Required Reviewers or wait timer. To use environment: production, the workflow must first pass GitHub's gate; only then does GitHub issue a JWT with sub = environment:production. This means AWS Role assumption doesn't happen without human approval. AWS IAM restriction + GitHub Environment gate completes the dual defense.

💡 Related Theory: This pattern is a textbook application of defense in depth (NIST SP 800-160). Even if one layer (GitHub Environment) breaches, the next (IAM trust policy sub condition) blocks. It's also an implementation of principle of least privilege (Saltzer & Schroeder 1975) in OIDC federation—the Role's Permission Policy only touches prod resources, and the contexts that can assume that Role are narrowed.

📚 Case Study: In 2023, a SaaS company almost suffered an incident where fork PRs triggered workflows accessing secrets. GitHub's default is that secrets aren't exposed in fork PRs, but a developer misused pull_request_target event, making fork code access base repo secrets. Fortunately, the OIDC token's sub was pull_request, and prod IAM Role's trust policy only allowed sub: ref:refs/heads/main, so the fork PR couldn't assume the Role. Critical blocking point. After this, the company org-wide banned pull_request_target usage.

GitHub Environments — A People Gate Right Before Workflow Execution

GitHub Environments aren't just labels; they're protection layers that intervene right before workflow execution. Available gates:

GateBehavior
Required ReviewersSpecified person or team must approve before deploy job starts (max 6, some required)
Wait TimerWait N minutes (max 30 days) even after approval. Creates emergency rollback window
Deployment BranchesRestrict which branches/tags can deploy to this environment
Environment SecretsSeparate secrets storage per environment
Custom Protection Rules(GitHub Apps) external tool evaluates gate
jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://app.example.com  # Link for post-deploy verification
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::222:role/GHA-Production
          aws-region: ap-northeast-2
      - run: ./deploy.sh production

🔍 Deep Dive: Wait Timer is often overlooked but highly valuable in practice. "5-minute wait after approval" gives someone 5 minutes to shout "stop". Combined with CodeDeploy's BlockTraffic auto hook, you get 4-stage protection: human approval → wait → auto-validation → traffic shift.

💡 Related Theory: GitHub Environments + Required Reviewers is fundamentally the manual gating pattern (Google SRE Book, Chapter 16). Full automation is fast but has wide blast radius; full manual is safe but slow. Gating is compromise—"ask humans only where human judgment has value". AWS CodePipeline's Manual Approval Action embodies the same philosophy.

AWS Code* vs GitHub Actions — Which Excels in What Scenario

Pro exams frequently ask the selection criteria for both tools. The key is "AWS-native depth vs multi-cloud + developer experience".

DimensionGitHub ActionsAWS CodePipeline/CodeBuild
AWS Native IntegrationOIDC + AWS Action, general depthNative, all steps in one console
Multi-CloudExcellent, GCP/Azure identicalAWS-only
Build EnvironmentGH-hosted runner (per-minute billing) + Self-hostedCodeBuild (per-minute billing, VPC-native)
PR IntegrationNative PR comments, status checksCodeStar Connections + Notifications
Approval GateEnvironments + Required ReviewersManual Approval Action
SecretsRepo/Org/Environment SecretsSecrets Manager / SSM Parameter Store
Multi-AccountOIDC + per-environment IAM RoleCross-account Role + Pipeline roleArn
Free TierCertain free allocation100 minutes/month free (CodeBuild)
StrengthsDeveloper UX, rich marketplace actionsAWS fine-grained control, IAM/KMS consistency, VPC-native

🎯 Scenario: A company runs "main builds in GitHub Actions, multi-account prod deployment in AWS CodePipeline". Why? GitHub Actions excels at build/test developer experience; CodePipeline's Cross-account Role + Manual Approval + KMS integration suits prod deployment precision. In exams when "which of these?" choices appear, look at the scenario's "what's most important?"—auto build/test PR = GH Actions, multi-account precision deploy = CodePipeline.

Self-hosted Runner vs CodeBuild VPC Mode — Two Paths to Building Private Resources

GitHub-hosted runners run on GitHub's public infrastructure, so they can't access VPC-internal resources (private RDS, internal ELB, EFS). Two solutions exist:

[Option A: Self-hosted Runner on AWS]
  ASG of EC2 (or ECS Fargate)
    |
    | GitHub Actions Runner Controller (ARC) on EKS
    | or actions-runner installation script
    v
  VPC private subnet
    |
    v
  Private RDS / internal ALB / EFS access

[Option B: CodeBuild VPC mode]
  GitHub webhook → CodeBuild project
    |
    | vpcConfig: { vpcId, subnets, securityGroupIds }
    v
  Build runs in VPC private subnet
DimensionSelf-hosted RunnerCodeBuild VPC Mode
MaintenanceYou patch runner, OS updatesAWS fully managed
ScalingASG or ARC scales with GitHub queueCodeBuild auto-scales
CostEC2/Fargate + operational timePer-build-minute
VPC IntegrationInstance lives inside VPCENI attachment
GitHub Integration Depth100% (native GH Actions)GH webhook + status API
Startup LatencyInstant if runner always runningCold start tens of seconds

⚠️ Trap: Connecting Self-hosted Runner to a public repo risks external PRs executing arbitrary code on the runner. GitHub explicitly warns "use self-hosted runners for private repos only". In exams, when asked about "public repo + self-hosted runner" danger, it's almost always the right answer.

🔍 Deep Dive: GitHub Actions Runner Controller (ARC) is a Kubernetes operator that dynamically spins up runners. When PR queue grows, it provisions EKS pods; when done, it reclaims. AWS commonly pairs this with Karpenter for spot nodes, significantly reducing self-hosted runner operational costs.

Reusable Workflows and Composite Actions — Deploying Policies Consistently Across 100 Repos

As orgs grow, copying deploy logic to 100 repos becomes inevitable. That's when reusable workflows pay dividends.

# .github/workflows/shared-deploy.yml in org/shared-workflows
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
      role-arn:
        required: true
        type: string
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ inputs.role-arn }}
          aws-region: ap-northeast-2
      - run: ./deploy.sh
# All 100 service repos call this
jobs:
  prod-deploy:
    uses: my-org/shared-workflows/.github/workflows/shared-deploy.yml@v1
    with:
      environment: production
      role-arn: arn:aws:iam::222:role/GHA-Production

🔍 Deep Dive: The crucial detail: reusable workflow's OIDC sub value differs from normal. It's not the calling workflow's repo but the called reusable workflow's repo + ref that goes into sub. Pattern: repo:my-org/shared-workflows:job_workflow_ref:my-org/shared-workflows/.github/workflows/shared-deploy.yml@refs/heads/main. Trust policy conditions must be written against this. Exams almost always ask this detail when reusable workflow + OIDC appear together.

💡 Related Theory: Reusable workflows are essentially DRY (Don't Repeat Yourself, Hunt & Thomas 1999) applied to CI/CD. With the same logic in 100 repos, one security patch (e.g., changing AssumeRole region) requires 100 applications. Shared workflows change once and automatically apply on next invocation. This is CALMS's Sharing axis expressed in code.

Practical OIDC Integration Traps

⚠️ Trap 1: Missing permissions: id-token: write — most common mistake. Must be explicit at workflow or job level.

⚠️ Trap 2: sub condition with wildcard repo:org/* — any compromised repo in org reaches prod.

⚠️ Trap 3: Changing aud without updating trust policy — if aws-actions/configure-aws-credentials@v4 audience option changes, trust policy aud must match.

⚠️ Trap 4: Attempting session duration > 12 hours — default 1h, max 12h. Increase Role's MaxSessionDuration property and specify RoleSessionDuration in call.

⚠️ Trap 5: Assuming same workflow_run executed twice uses cached OIDC token on second execution — actually a new token each time. In exams: "Is OIDC token cached?" → No.

⚠️ Trap 6: Thinking pull_request event can access secrets → fork PRs have secrets masked. But pull_request_target runs in base repo context so secrets are accessible → fork malicious code execution risk. Frequent exam topic.

⭐ Key Points

  1. ⭐ OIDC = eliminates long-lived credentials. Storing AWS Access Keys in GitHub Secrets is an antipattern.
  2. ⭐ Trust Policy sub condition is security's core. environment:production safest, repo:org/* risky.
  3. ⭐ GitHub Environments Required Reviewers + AWS IAM restriction = dual defense.
  4. ⭐ permissions: id-token: write is OIDC's absolute requirement. Missing is the most common failure.
  5. ⭐ Self-hosted Runner for private repos only. Public repo + self-hosted runner = arbitrary code execution risk.
  6. ⭐ Reusable workflow OIDC sub is by definition repo, not calling repo. Careful writing trust policy.
  7. ⭐ Default credential validity 1 hour, max 12 hours (requires Role MaxSessionDuration setting).

📝 Practice Questions

Click a choice to reveal the answer and explanation.

Question 1

What is the most secure way to manage credentials when deploying to AWS from GitHub Actions?

Question 2

What is the risk of setting sub in the OIDC trust policy to repo:my-org/*:ref:refs/heads/main?

Question 3

An OIDC token is not issued in a GitHub Actions workflow. What should be checked first?

Question 4

A build that "runs a migration script against a private RDS database" is required. What is the most appropriate configuration?

Question 5

At which stage does the Required Reviewers feature of GitHub Environments provide protection?

Question 6

What is the default value of the aud condition in the trust policy, and why would you change it?

Question 7

100 repos use common deploy logic through a reusable workflow. How should the trust policy sub condition be written?

Question 8

What is the risk in a scenario where a forked PR accesses the base repo secrets?

Question 9

What are the default and maximum validity periods of temporary credentials issued through OIDC?

Question 10

You built a combination of OIDC + GitHub Environments + trust policy sub: environment:production. Someone tries to run the prod deploy job directly from the main branch. How is this blocked?

📌 Today's Summary

OIDC federation is not just "keyless authentication" but a paradigm shift that removes static credentials from the system entirely. Because the JWT sub claim carries the full context (repo, ref, environment, reusable workflow ref), writing the trust policy conditions precisely enables very fine-grained access control. Combined with GitHub Environments + Required Reviewers, it completes a dual defense of a GitHub gate and an AWS IAM gate. The three most common practical pitfalls by far are a missing permissions: id-token: write, a sub wildcard, and misuse of pull_request_target. Tomorrow we revisit this from the perspective of the CodeBuild build system.

PreviousCodeCommit Deep Dive: What Changes When Git Hosting is Integrated with IAMWeek 2 · Day 1Next CodeArtifact and Supply Chain Security: When Dependencies Become Attack SurfaceWeek 2 · Day 3

Official references

  • DOP-C02 official exam page(opens in a new tab)
  • AWS IAM documentation(opens in a new tab)
  • Amazon VPC documentation(opens in a new tab)
  • AWS CodeBuild documentation(opens in a new tab)
  • AWS CodePipeline documentation(opens in a new tab)
  • Amazon RDS · Aurora documentation(opens in a new tab)

On this page

  • Exact Sequence of How OIDC Federation Works — Six Steps Where JWT Becomes STS Credential
  • The `sub` Condition in Trust Policy — Security Hinges Here
  • GitHub Environments — A People Gate Right Before Workflow Execution
  • AWS Code* vs GitHub Actions — Which Excels in What Scenario
  • Self-hosted Runner vs CodeBuild VPC Mode — Two Paths to Building Private Resources
  • Reusable Workflows and Composite Actions — Deploying Policies Consistently Across 100 Repos
  • Practical OIDC Integration Traps
  • Key Points
  • Practice Questions
  • Today's Summary