Authenticate to AWS Using OIDC for Downloading and Verification of Images

This guide explains how to configure OpenID Connect (OIDC) authentication to download Kloudfuse container images from AWS ECR and verify their signatures in CI/CD pipelines without managing long-lived AWS credentials.

Overview

When downloading Kloudfuse container images in CI/CD pipelines, you need AWS ECR access to pull images and AWS KMS access to verify their signatures. Instead of storing AWS credentials (access keys) in your CI/CD environment, you can use OIDC to establish a trust relationship between your CI/CD provider (like GitLab) and AWS.

Benefits of OIDC Authentication
  • No long-lived credentials - No AWS access keys to manage or rotate

  • Automatic token generation - CI/CD provider generates short-lived tokens automatically

  • Fine-grained access control - Limit access to specific projects, branches, or jobs

  • Enhanced security - Tokens expire automatically and cannot be reused

  • Audit trail - AWS CloudTrail logs show which jobs assumed roles

Architecture

The OIDC authentication flow works as follows:

┌─────────────────┐         ┌──────────────────┐         ┌─────────────┐
│   GitLab CI/CD  │         │   AWS STS        │         │  AWS KMS    │
│   Pipeline      │         │                  │         │             │
└────────┬────────┘         └────────┬─────────┘         └──────┬──────┘
         │                           │                          │
         │ 1. Request OIDC token     │                          │
         ├──────────────────────────>│                          │
         │                           │                          │
         │ 2. Return JWT token       │                          │
         │<──────────────────────────┤                          │
         │                           │                          │
         │ 3. AssumeRoleWithWebIdentity                         │
         │    (with OIDC token)      │                          │
         ├──────────────────────────>│                          │
         │                           │                          │
         │ 4. Validate token against │                          │
         │    OIDC provider          │                          │
         │                           │                          │
         │ 5. Return temporary       │                          │
         │    AWS credentials        │                          │
         │<──────────────────────────┤                          │
         │                           │                          │
         │ 6. Use credentials to call kms:Verify               │
         ├─────────────────────────────────────────────────────>│
         │                           │                          │
         │ 7. Signature verification result                     │
         │<─────────────────────────────────────────────────────┤
text

Setup Process

Setting up OIDC authentication requires configuring both AWS and your CI/CD provider.

Step 1: Create OIDC Provider in AWS

First, establish trust between AWS and your CI/CD platform’s OIDC provider.

For GitLab (Self-Hosted or GitLab.com)

  1. Navigate to AWS IAM ConsoleIdentity ProvidersAdd Provider

  2. Configure the OIDC provider:

    Provider Type

    OpenID Connect

    Provider URL

    Your GitLab OIDC endpoint

    Audience

    Your GitLab instance URL

Example for GitLab Corporate Instance:

Provider URL

https://gitlab-oidc-config.s3.us-west-2.amazonaws.com

Audience

https://gitlab.example.com

Example for GitLab.com:

Provider URL

https://gitlab.com

Audience

https://gitlab.com

For self-hosted GitLab instances, the OIDC provider URL may be an S3 bucket hosting the OIDC configuration (.well-known/openid-configuration), rather than the GitLab URL itself.

Step 2: Create and Attach IAM Policies for ECR and KMS Access

Create and attach policies that grant permissions to pull Kloudfuse container images from ECR and verify their signatures.

For ECR Access:

Attach both of these AWS managed policies:

AmazonEC2ContainerRegistryReadOnly
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetRepositoryPolicy",
                "ecr:DescribeRepositories",
                "ecr:ListImages",
                "ecr:DescribeImages",
                "ecr:BatchGetImage",
                "ecr:GetLifecyclePolicy",
                "ecr:GetLifecyclePolicyPreview",
                "ecr:ListTagsForResource",
                "ecr:DescribeImageScanFindings"
            ],
            "Resource": "*"
        }
    ]
}
json

Provides full read-only access to ECR, including repository metadata and image details.

AmazonEC2ContainerRegistryPullOnly
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchGetImage",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchImportUpstreamImage"
            ],
            "Resource": "*"
        }
    ]
}
json

Provides essential permissions needed to pull images.

For KMS Access:

Create a custom policy for KMS signature verification:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "KMSSignatureVerification",
            "Effect": "Allow",
            "Action": [
                "kms:GetPublicKey",
                "kms:Verify",
                "kms:DescribeKey"
            ],
            "Resource": [
                "arn:aws:kms:us-west-2:502496443919:alias/kfuse-cosign-signing-key"
            ]
        }
    ]
}
json

To attach these policies:

  1. Navigate to AWS IAM ConsolePoliciesCreate Policy

  2. Create the KMS policy above and name it: KloudfuseImageSignatureVerifyPolicy

  3. When creating the role in Step 3, attach all three policies:

    • AmazonEC2ContainerRegistryReadOnly (AWS managed)

    • AmazonEC2ContainerRegistryPullOnly (AWS managed)

    • KloudfuseImageSignatureVerifyPolicy (your custom policy)

Step 3: Create IAM Role with OIDC Trust Relationship

Create an IAM role that can be assumed by your CI/CD jobs using OIDC tokens.

  1. Navigate to AWS IAM ConsoleRolesCreate Role

  2. Select Web Identity as the trusted entity type

  3. Configure the trust relationship:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::YOUR_AWS_ACCOUNT_ID:oidc-provider/gitlab.example.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "gitlab.example.com:aud": "https://gitlab.example.com"
                },
                "StringLike": {
                    "gitlab.example.com:sub": "project_path:your-organization/*"
                }
            }
        }
    ]
}
json

Trust Relationship Breakdown:

Federated

The ARN of the OIDC provider created in Step 1

Action

sts:AssumeRoleWithWebIdentity - Allows assuming role with OIDC token

Condition.StringEquals (aud)

Validates the token audience matches your GitLab instance

Condition.StringLike (sub)

Limits access to specific GitLab projects (e.g., project_path:zscaler/*)

Customize the sub claim to match your organization:

  • Single project: "project_path:myorg/myproject"

  • All projects in group: "project_path:myorg/*"

  • Specific branch: "project_path:myorg/myproject:ref:refs/heads/main"

  • All branches: "project_path:myorg/myproject:ref:*"

See GitLab documentation for more examples.

  1. Attach the policy created in Step 2 (KloudfuseImagePullAndVerifyPolicy)

  2. Name the role: KloudfuseImagePullRole

  3. Note the Role ARN - you’ll need this in your CI/CD pipeline

Step 4: Configure CI/CD Pipeline

Configure your CI/CD pipeline to use the OIDC role for AWS authentication.

GitLab CI/CD Example

verify-kfuse-image:
  stage: verify
  image: alpine:latest

  variables:
    # Image to verify
    IMAGE_NAME: ui
    IMAGE_TAG: "0.1.0-b2274ac0"

    # Cosign configuration
    COSIGN_VERSION: "v3.0.3"
    KMS_KEY: "awskms:///arn:aws:kms:us-west-2:502496443919:alias/kfuse-cosign-signing-key"

    # AWS OIDC authentication
    AWS_ROLE_ARN: "arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/KloudfuseImageVerificationRole"
    AWS_DEFAULT_REGION: "us-west-2"

  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.example.com

  before_script:
    # Install dependencies
    - apk add --no-cache curl aws-cli jq

    # Install cosign
    - curl -sSfL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" -o /usr/local/bin/cosign
    - chmod +x /usr/local/bin/cosign

    # Authenticate to AWS using OIDC token
    - |
      echo "Assuming AWS role using OIDC..."

      STS_RESPONSE=$(aws sts assume-role-with-web-identity \
        --role-arn "${AWS_ROLE_ARN}" \
        --role-session-name "gitlab-ci-${CI_PROJECT_NAME}-${CI_PIPELINE_ID}" \
        --web-identity-token "${GITLAB_OIDC_TOKEN}" \
        --duration-seconds 3600)

      # Extract and export temporary credentials
      export AWS_ACCESS_KEY_ID=$(echo "${STS_RESPONSE}" | jq -r '.Credentials.AccessKeyId')
      export AWS_SECRET_ACCESS_KEY=$(echo "${STS_RESPONSE}" | jq -r '.Credentials.SecretAccessKey')
      export AWS_SESSION_TOKEN=$(echo "${STS_RESPONSE}" | jq -r '.Credentials.SessionToken')

      echo "✅ Successfully authenticated to AWS"

  script:
    - |
      IMAGE_REF="502496443919.dkr.ecr.us-west-2.amazonaws.com/kfuse/${IMAGE_NAME}:${IMAGE_TAG}"

      # Step 1: Login to Kloudfuse's ECR registry
      echo "Logging in to Kloudfuse ECR..."
      aws ecr get-login-password --region us-west-2 | \
        docker login --username AWS --password-stdin 502496443919.dkr.ecr.us-west-2.amazonaws.com

      # Step 2: Pull the container image
      echo "Pulling image ${IMAGE_REF}..."
      docker pull "${IMAGE_REF}"

      # Step 3: Verify the image signature (optional but recommended)
      echo "Verifying signature for ${IMAGE_REF}..."
      if cosign verify --key "${KMS_KEY}" "${IMAGE_REF}"; then
        echo "✅ Image signature verified successfully"
      else
        echo "⚠️  Warning: Image signature verification FAILED"
        # Uncomment the next line to fail the pipeline on verification failure
        # exit 1
      fi

      echo "✅ Image pulled and verified successfully"
yaml

Key Configuration Elements:

id_tokens.GITLAB_OIDC_TOKEN

GitLab automatically generates an OIDC token and makes it available as $GITLAB_OIDC_TOKEN

AWS_ROLE_ARN

The ARN of the IAM role created in Step 3

aws sts assume-role-with-web-identity

Exchanges the OIDC token for temporary AWS credentials

--role-session-name

Identifies the session in AWS CloudTrail logs

--duration-seconds

How long the temporary credentials remain valid (max: 43200 = 12 hours)

Verification and Testing

Test the Pipeline

  1. Commit the pipeline configuration to your GitLab repository

  2. Run the pipeline manually or via a commit

  3. Check the job logs for:

    Assuming AWS role using OIDC...
    ✅ Successfully authenticated to AWS
    Logging in to Kloudfuse ECR...
    Login Succeeded
    Pulling image 502496443919.dkr.ecr.us-west-2.amazonaws.com/kfuse/ui:0.1.0-b2274ac0...
    0.1.0-b2274ac0: Pulling from kfuse/ui
    ✅ Image pulled and verified successfully
    text

Verify in AWS CloudTrail

Check AWS CloudTrail logs to confirm OIDC authentication:

  1. Navigate to AWS CloudTrail ConsoleEvent History

  2. Filter by:

    • Event name: AssumeRoleWithWebIdentity

    • User name: Your role name

  3. Inspect the event details to see:

    • Which GitLab project assumed the role

    • Session name (includes pipeline ID)

    • Timestamp and source IP

Troubleshooting

"Not authorized to perform sts:AssumeRoleWithWebIdentity"

Cause: OIDC provider not configured or trust relationship incorrect
Solution: Verify the OIDC provider ARN in the role’s trust relationship matches the provider created in Step 1

"Access denied" with audience mismatch

Cause: The aud claim in the OIDC token doesn’t match the trust relationship
Solution: Ensure id_tokens.GITLAB_OIDC_TOKEN.aud in your pipeline matches the audience in the trust relationship condition

"Access denied" with subject mismatch

Cause: The sub claim in the OIDC token doesn’t match the trust relationship condition
Solution: Verify the StringLike condition in the trust relationship matches your GitLab project path

"Token has expired"

Cause: OIDC token lifetime exceeded
Solution: OIDC tokens are typically valid for 5-10 minutes. Ensure assume-role-with-web-identity runs early in the job

"Access Denied" when calling KMS operations

Cause: IAM role doesn’t have KMS permissions
Solution: Verify the KMS policy from Step 2 is attached to the role

Debug OIDC Token Claims

To inspect what claims GitLab is sending in the OIDC token:

# Add this to your pipeline before_script to decode the token
echo "${GITLAB_OIDC_TOKEN}" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
bash

This will show claims like:

{
  "aud": "https://gitlab.example.com",
  "sub": "project_path:your-organization/your-project:ref:refs/heads/main",
  "iss": "https://gitlab.example.com",
  "exp": 1735123456,
  "iat": 1735123156,
  "jti": "abc123...",
  ...
}
json

Security Best Practices

  1. Minimize trust scope - Use specific StringLike conditions to limit which projects/branches can assume the role

  2. Short session duration - Use the minimum --duration-seconds needed for your job (typically 3600 = 1 hour)

  3. Least privilege - Grant only the KMS permissions needed (GetPublicKey, Verify, DescribeKey)

  4. Monitor CloudTrail - Set up alerts for unexpected AssumeRoleWithWebIdentity events

  5. Rotate OIDC providers - Periodically review and update OIDC provider configurations

  6. Use session names - Include pipeline/job identifiers in session names for better audit trails

Advanced Configurations

Multiple AWS Accounts (Development, Staging, Production)

Use different IAM roles per environment:

.verify-template:
  stage: verify
  # ... (common configuration)
  script:
    - |
      case "${CI_ENVIRONMENT_NAME}" in
        production)
          AWS_ROLE_ARN="arn:aws:iam::111111111111:role/KfuseImageVerify-Prod"
          ;;
        staging)
          AWS_ROLE_ARN="arn:aws:iam::222222222222:role/KfuseImageVerify-Staging"
          ;;
        development)
          AWS_ROLE_ARN="arn:aws:iam::333333333333:role/KfuseImageVerify-Dev"
          ;;
      esac

      # ... (assume role and verify)

verify-prod:
  extends: .verify-template
  environment: production
  only:
    - main

verify-staging:
  extends: .verify-template
  environment: staging
  only:
    - staging
yaml

Cross-Account KMS Access

If your AWS account differs from Kloudfuse’s KMS account (502496443919), you need to:

  1. Ensure the KMS key policy allows your account

  2. Add kms:* permissions for the key ARN in your IAM policy

Kloudfuse’s KMS key policy already allows external accounts to use the public key for verification.

Other CI/CD Providers

GitHub Actions:

- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::YOUR_ACCOUNT:role/KloudfuseImageVerificationRole
    aws-region: us-west-2

- name: Verify image signature
  run: |
    cosign verify \
      --key awskms:///arn:aws:kms:us-west-2:502496443919:alias/kfuse-cosign-signing-key \
      502496443919.dkr.ecr.us-west-2.amazonaws.com/kfuse/ui:0.1.0-b2274ac0
yaml

CircleCI: Use CircleCI’s OIDC token integration with AWS (see CircleCI OIDC docs)

Jenkins: Configure OIDC authentication using the AWS Steps plugin or AWS CLI in pipeline scripts

Support

For questions or issues: