Boundlayer
AWS SecurityCI/CDOpenID ConnectGitHub ActionsGitLab CI

AWS CI/CD with OIDC: Short-Lived Credentials for GitHub Actions and GitLab

Replace static AWS keys in CI/CD with OIDC federation. Learn to scope IAM trust policies to GitHub Actions or GitLab projects, protected refs, and least-privilege deployment roles.

9 min read

By BoundLayer Engineering Team

BoundLayer is a senior engineering partner for SaaS, fintech, AI automation, cloud infrastructure, legacy modernization, Web3, IoT, GPU computing, and data systems.

CI pipelines often need AWS access to publish a container, update infrastructure, or deploy an application. Storing long-lived AWS access keys as repository secrets makes that integration easy to start and difficult to govern: the secret can be copied, exposed by a compromised workflow, or remain valid after the person or system that created it is gone.

OpenID Connect (OIDC) federation gives a CI job a signed, short-lived identity token. AWS Security Token Service (STS) validates the token against an IAM identity provider and a role trust policy, then returns temporary credentials. The pipeline receives no permanent AWS key to rotate.

OIDC does not make a pipeline trusted by itself. The security boundary is the exact relationship between the CI issuer, token audience, repository or project, branch or environment, and AWS role. This guide shows how to constrain that relationship for GitHub Actions and GitLab CI.

The trust chain

Protected CI job
   | signed ID token: issuer + audience + subject
   v
AWS IAM OIDC identity provider
   | validates issuer and accepted audience
   v
IAM role trust policy
   | permits only the intended repository/project and ref/environment
   v
STS temporary role session
   | limited by the role's permissions policy
   v
Specific AWS deployment resources

Two different policies are involved:

  • The role trust policy decides which CI identity may assume a role.
  • The permissions policy decides what that role can do after assumption.

Tightening only one is not enough. A broadly trusted role with narrow access can still allow an unintended branch to deploy. A tightly trusted role with administrator permissions can turn a workflow compromise into an account-wide incident.

Scope the AWS role to one CI identity

For GitHub Actions, the issuer is https://token.actions.githubusercontent.com and the common AWS audience is sts.amazonaws.com. A branch-triggered job's default sub claim has a form like repo:ORG/REPO:ref:refs/heads/main. A job that uses a GitHub Environment has an environment-based subject instead, such as repo:ORG/REPO:environment:production.

An IAM trust policy for a branch-scoped role can use exact claim matches:

{
  "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:aud": "sts.amazonaws.com",
        "token.actions.githubusercontent.com:sub": "repo:example-org/example-app:ref:refs/heads/main"
      }
    }
  }]
}

The account number, organization, repository, and subject must match your environment. Do not replace the subject with a repository-wide wildcard unless every eligible branch and event is intentionally allowed to deploy. GitHub's OIDC reference describes the subject formats and how triggers and environments affect claims. GitHub also documents newer immutable subject formats for repositories created or renamed after July 15, 2026, so verify the actual format used by your repository before copying an older trust policy.

For production deployments, a protected GitHub Environment can add required reviewers and restrict deployment branches. When a job uses an environment, scope IAM to that environment subject and make sure the Environment itself restricts which refs can reach it. The AWS trust condition and GitHub protection rule should reinforce each other.

For GitLab.com, the issuer is https://gitlab.com; the token audience is configured in the job and must match the audience accepted by the IAM identity provider. The default subject is commonly shaped like project_path:GROUP/PROJECT:ref_type:branch:ref:main. GitLab supports project and namespace identifiers as additional AWS condition keys on GitLab.com, which can make a trust policy more resilient to project path changes. Self-managed and Dedicated offerings expose a different set of usable claims, so check the current GitLab ID token documentation for your deployment.

The main rule is the same for either provider: restrict the issuer, audience, and subject to the intended deploy identity. Do not trust any job from an organization, group, or project if only one protected deployment workflow needs AWS access.

GitHub Actions: issue the token only to the deploy job

GitHub requires the workflow to grant id-token: write before a job can request an OIDC token. Keep that permission at the job that performs the deployment, rather than granting it to every job in the workflow:

permissions:
  contents: read

jobs:
  deploy:
    environment: production
    permissions:
      contents: read
      id-token: write

This is the permission and environment portion of a workflow, not a complete deploy file. Use the official AWS credentials action or the AWS CLI's web-identity support to exchange the token. Pin third-party actions to reviewed full commit SHAs; a mutable tag can change what code runs with access to the token. GitHub's secure use reference explains action pinning and workflow hardening.

Avoid giving production OIDC permission to pull-request jobs that execute untrusted code, including workflows that check out a fork's source. Separate test and deploy jobs, and use an approved environment or a dedicated protected branch for production credentials.

GitLab CI: use ID tokens, not removed JWT variables

Use GitLab's id_tokens keyword to request a token with the audience AWS expects. Older variables such as CI_JOB_JWT and CI_JOB_JWT_V2 have been removed; current pipelines should use ID tokens. A minimal job pattern is:

deploy_production:
  id_tokens:
    AWS_ID_TOKEN:
      aud: sts.amazonaws.com
  script:
    - umask 077
    - printf '%s' "$AWS_ID_TOKEN" > "$CI_PROJECT_DIR/aws-oidc-token"
    - export AWS_ROLE_ARN="$PRODUCTION_ROLE_ARN"
    - export AWS_WEB_IDENTITY_TOKEN_FILE="$CI_PROJECT_DIR/aws-oidc-token"
    - export AWS_ROLE_SESSION_NAME="gitlab-${CI_PROJECT_ID}-${CI_PIPELINE_ID}"
    - aws sts get-caller-identity
    - ./deploy.sh

The trust policy must accept the exact audience used above and restrict the subject to the intended project and protected ref. On GitLab.com, consider stable project and namespace IDs in the IAM conditions where supported. For GitLab Self-Managed, use the claims supported by that issuer and AWS; do not assume every GitLab.com condition key is available. Never print the token to job logs or upload it as an artifact.

Protect the deployment branch and environment, limit who can modify the pipeline definition, and restrict runner access. A token is short-lived, but a malicious job can still use it for the duration of its AWS role session.

Keep the assumed role narrow and observable

Create separate roles for separate deployment targets when their permissions differ. A frontend deployment may only need to upload to a specific bucket and invalidate a specific distribution. A container release may need to push to one ECR repository and update one ECS service. Infrastructure provisioning requires different permissions and should not inherit broad runtime access.

Use a short role session, add a meaningful session name, and make AWS CloudTrail events attributable to repository, project, branch, and workflow where possible. Set resource-level permissions and conditions rather than granting Action: "*" and Resource: "*". If a central tooling account assumes a role in another account, constrain the second role to the expected source role and account.

Federation should also fit the wider software supply-chain controls. Pin actions and dependencies, protect workflow files with code review, separate build artifacts from deployment authority, and verify the artifact being deployed. See our software supply-chain security guide and AWS infrastructure audit guide for related controls.

Common failure and security checks

  • Audience mismatch: the CI token's aud does not equal the audience configured in AWS.
  • Subject mismatch: the job uses an environment subject while IAM expects a branch subject, or a repository has moved to an immutable subject format.
  • Wildcard trust: a pattern allows pull requests, tags, or unrelated branches to assume a production role.
  • Permission sprawl: a deployment role can change unrelated accounts or infrastructure.
  • Token exposure: a workflow prints the JWT, writes it to a shared artifact, or passes it to an untrusted action.
  • Workflow compromise: a third-party action or an unreviewed change can execute in the job that has id-token: write.
  • No audit trail: STS role sessions are indistinguishable, making incident investigation slow.

Test both allowed and denied identities. Confirm that the production branch or environment succeeds, while a feature branch, fork pull request, tag, or other repository fails to assume the role. Test the role's permissions separately by checking that the deployment succeeds only for the intended resources.

A practical rollout

  1. Inventory stored AWS keys in CI variables, runner configuration, and deployment scripts.
  2. Create one OIDC provider and a dedicated, least-privilege deployment role.
  3. Bind trust to the exact issuer, audience, and protected branch or environment; for GitLab.com, include stable project identifiers where appropriate.
  4. Add token permission to only the deploy job and pin third-party actions.
  5. Validate the identity with aws sts get-caller-identity, then test both permitted and rejected pipeline contexts.
  6. Review CloudTrail attribution, session duration, role permissions, and recovery procedures before removing the old credentials.
  7. Revoke and remove static keys after the OIDC path is proven, then monitor for attempts to assume the role from unapproved subjects.

OIDC replaces stored long-lived credentials with a verifiable identity and temporary AWS session. The security outcome still depends on how tightly the role trust and permissions are scoped, and on whether the pipeline itself is protected. BoundLayer designs AWS deployment workflows, CI/CD identity, cloud access controls, and release automation that teams can audit and operate safely.

Further reading

Are long-lived AWS keys still stored in your CI pipeline?

We migrate CI/CD to federated workload identity, scope deployment roles, and validate cloud access boundaries across protected environments.

Free consultation

Get a Free 30-Minute Technical Consultation

Share a few details about your project and we'll get back to you within 48 hours with a clear next step.

  • No sales pressure — a senior engineer, not a sales rep
  • Clear next step within 48 hours
  • We can sign an NDA before we talk

By submitting, you agree to be contacted about your request. We respect your privacy and can sign an NDA on request.