Cloud Security Index
aws

critical

Compliance Frameworks

BETA
CIS AWS Foundations Benchmark v3.0
1.14
NIST 800-53 Rev 5
IA-2, IA-5
ISO 27001:2022
A.8.5
PCI DSS v4.0
8.2.1
SOC 2
CC6.1
AWS Foundational Security Best Practices
IAM.1

// Beta Mapping Disclaimer

Framework mappings are currently in beta. These references are generated based on technical specifications and common industry alignments. Mappings may be incomplete or inaccurate; always consult with a qualified auditor for official compliance validation.

Overview

Mandate the use of OIDC federation for all CI/CD pipeline connections from GitHub, GitLab or any other version control system (VCS) to central AWS accounts, eliminating long-lived IAM access keys from VCS secrets entirely.

CI/CD pipelines that deploy to or read from central landing zone accounts must authenticate via OIDC rather than static IAM access keys. In the OIDC flow, the VCS provider issues a short-lived JWT to the pipeline job, which exchanges it for temporary STS credentials by assuming an IAM role. The IAM role trust policy must include a condition on the sub claim to restrict which repositories and branches can assume the role - without this condition, any repository in the same GitHub organization or GitLab instance can assume the role. The SCP in enforcement_reference denies iam:CreateAccessKey for any principal that is not an explicitly approved role, preventing pipelines from creating new static credentials as a bypass. Existing access keys stored as VCS secrets must be audited and rotated to OIDC before the SCP Deny takes effect.

Long-lived IAM access keys stored as VCS secrets are the most common source of AWS credential leaks. They appear in logs, get committed to repositories, are copied between environments, and persist long after the pipeline that needed them is decommissioned. OIDC credentials are valid for 15 minutes by default, are never stored, and are scoped to the specific job that requested them. A leaked OIDC token is worthless seconds after the job completes.

Remediation Strategy

Create the OIDC identity provider in IAM for GitHub and/or GitLab. Create a scoped IAM role with the trust policy from enforcement_reference. Update pipelines to use the OIDC authentication action. Audit and remove all existing static access keys from VCS secrets.

1.

Create the OIDC identity provider in IAM for each VCS in use (GitHub, GitLab, ...).

2.

Create an IAM role with a trust policy scoped to specific repositories and branches using the sub claim condition.

3.

Attach the minimum required IAM policy to the role - never AdministratorAccess.

4.

Update CI/CD pipelines to use OIDC authentication (aws-actions/configure-aws-credentials@v4 for GitHub; id_tokens for GitLab).

5.

Audit all VCS secrets for AWS_ACCESS_KEY_ID values and rotate to OIDC.

6.

Apply the SCP from enforcement_reference after all pipelines are migrated.

# GitHub Actions OIDC provider
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \

# GitLab OIDC provider
aws iam create-open-id-connect-provider \
  --url https://gitlab.com \
  --client-id-list https://gitlab.com \

# Verify both providers exist
aws iam list-open-id-connect-providers \
  --output table

Enforcement Logic

Automatedaws-api

Detection operates at two levels. First, verify that OIDC providers for GitHub and GitLab exist in the account. Second, and more critically, scan all IAM roles whose trust policies reference the OIDC providers and verify that each trust policy includes a sub claim condition - a role without this condition allows any repository in the org to assume it. Additionally, scan for active IAM access keys owned by users whose names suggest CI/CD usage (e.g. containing "ci", "deploy", "pipeline") as an indicator that OIDC migration is incomplete.

# Confirm OIDC providers exist
aws iam list-open-id-connect-providers \
  --query "OpenIDConnectProviderList[*].Arn" \
  --output table

# Find IAM roles that trust the GitHub OIDC provider
# and check whether a sub condition is present
aws iam list-roles \
  --query "Roles[*].{Name:RoleName,Trust:AssumeRolePolicyDocument}" \
  --output json | \
  python3 -c "
import sys, json
roles = json.load(sys.stdin)
for r in roles:
    doc = r['Trust']
    for stmt in doc.get('Statement', []):
        principal = stmt.get('Principal', {})
        federated = principal.get('Federated', '')
        if 'token.actions.githubusercontent.com' in str(federated) or \
           'gitlab.com' in str(federated):
            cond = stmt.get('Condition', {})
            has_sub = any(
                'sub' in str(k).lower()
                for k in cond.get('StringEquals', {}) | cond.get('StringLike', {})
            )
            status = 'SCOPED' if has_sub else 'UNSCOPED (risk)'
            print(f\"{status}: {r['Name']}\")
"
SPEC_METADATA

Complexity
low
Impact
low
Likelihood
high
Residual Risk
low
Scope
account
Environment
production, non-production
Resources
OIDCIAM
Remediation
account
Auto-Remediate
Available
Category
iam
Enforcement
mandatory
Tags
oidccicdvcsgithubgitlabiamcredentialslanding-zone

Owner
cloud-security-team
Type
team
Frequency
quarterly

// EXCEPTION

Legacy pipeline tooling that does not support OIDC token exchange may retain a time-limited IAM access key while a migration plan is in progress. The key must have no console access, must be rotated every 90 days, must be scoped to the minimum required permissions, and the exception must be renewed quarterly with documented progress toward OIDC migration.

// EXCEPTION

Self-hosted GitLab instances use a different OIDC URL than gitlab.com. The OIDC provider URL must match the self-hosted instance URL exactly. The thumbprint must be retrieved from the instance's OIDC discovery endpoint and verified independently.

// SYSTEM_NOTE

The sub claim is the critical security boundary in OIDC role trust policies. A trust policy that conditions only on aud (audience) allows any repository in the same GitHub organization or GitLab instance to assume the role - this is a common misconfiguration. Always scope sub to the specific repository and branch or environment. Use StringLike with a wildcard only when multiple repositories in the same org legitimately need the same role. For self-hosted GitLab, thumbprint validation is required and the correct value must be retrieved from the instance.