Introducing OpenID Connect identity tokens in CircleCI jobs! This token enables your CircleCI jobs to authenticate with cloud providers that support OpenID Connect like AWS, Google Cloud Platform, and Vault. In this blog post, we’ll introduce you to OpenID Connect, explain its usefulness in a CI/CD system, and show how it can be used to authenticate with AWS, letting your CircleCI job securely interact with your AWS account, without any static credentials.

What is OpenID Connect?

OpenID Connect (OIDC) is an authentication protocol that allows cloud services to verify the identity of end users. It adds an identity layer to OAuth2.0, an authorization protocol for providing single sign-on (SSO) access to cloud resources. Since its introduction in 2014, cloud providers have widely adopted OpenID Connect as the standard for providing secure access to protected resources.

According to the OpenID Connect Foundation, OpenID “allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server.” When you use CircleCI’s OpenID Connect token, the client is a cloud provider such as AWS. The end user is your job running in CircleCI. The authorization server is CircleCI itself.

Putting that all together, this means that a cloud provider like AWS can verify the identity of your job and allow it to take actions as if it were an authenticated user. This means your job can securely interact with AWS.

Why is OpenID Connect useful in CI/CD?

Physical key vs OIDC tokens

In your CI workflow, you may want to upload binaries, test artifacts, or logs to cloud storage. Perhaps you want to submit a package to an artifact repository. Or maybe you may want to deploy to your production environment. If you want to securely access any of these cloud resources, your job needs to have credentials to authenticate with your cloud provider. CircleCI’s contexts let you securely store credentials and use them in jobs throughout your organization.

Additional OIDC token documentation

However, these credentials are static. A physical key grants access to a space until the locks are changed, and analogously these static credentials are valid until you rotate them which you may want to do as a security best practice. You can rotate keys in the CircleCI web UI, with the CircleCI CLI or with the CircleCI API for Contexts.

Rotation requires a time investment, which reduces your engineering capacity. OpenID Connect tokens avoid this downside. When you configure your cloud provider to trust OpenID Connect tokens from your CircleCI jobs, your cloud provider will recognize CircleCI’s signature and treat your job’s requests as authentic. Your jobs securely interact with your cloud provider, so that you can upload to cloud storage, change the state of production, or whatever else you decide to permit.

OIDC token documentation

When your CircleCI job starts, CircleCI signs an OpenID Connect token and makes it available to your job. Your job can present this token to your cloud provider, which verifies its authenticity, grants your job temporary credentials, and permits it to take some actions.

You can read about the structure of CircleCI’s OIDC token in our documentation.

Using the OpenID Connect token to interact with AWS from a CircleCI job

Here’s an example of how you can use CircleCI’s OpenID Connect token to interact with AWS. First, we’ll do a one-time setup to configure your AWS account to trust CircleCI’s OpenID Connect tokens. Then, we’ll run a job that uses the token to interact with AWS and upload an image to ECR.

Setting up AWS

First, create an IAM identity provider and an IAM role in AWS. This allows your AWS account to trust CircleCI’s OpenID Connect tokens. You only have to do this once.

In IAM, create an OpenID Connect identity provider. You’ll need your organization ID, which you can find by going to your CircleCI organization settings and copying the Organization ID from the Overview page.

For the Provider URL, provide https://oidc.circleci.com/org/<organization-id>, where <organization-id> is the ID of your CircleCI organization.

For the audience, provide the same organization ID.

In IAM, create a role. For the trusted entity, select Web Identity then choose the identity provider that you created earlier. For audience, choose the only option, which is Add permissions. This allows you to specify what your CircleCI jobs can and cannot do. Choose only permissions that your job will need, which is an AWS best practice.

Note: You may find it useful to create your own policy.

Interacting with AWS from a CircleCI job

Now that you’ve set up AWS, you’re ready to write a CircleCI job that interacts with AWS.

Choose the CircleCI jobs in your workflow that you want to receive an OpenID Connect token for. Make sure each of these jobs uses a context. The OpenID Connect token is available only to jobs that use at least one context. The context may have no environment variables. You can read about CircleCI’s OIDC tokens in our OIDC token documentation.

In your job, use the OpenID Connect token to do AWS STS’s AssumeRoleWithWebIdentity. Have the following information ready:

  • AWS region that you want to operate in
  • ARN of the IAM role that you created earlier

Here’s an example config that uses the AWS CLI’s assume-role-with-web-identity subcommand to authenticate with AWS. It then performs trivial interaction (aws sts get-caller-identity) with AWS, demonstrating that the authentication worked. Replace that demonstration with whatever you want, such as uploading to an S3 bucket, pushing to ECR, or interacting with EKS.

jobs:
  deploy:
    docker:
      - image: amazon/aws-cli
    environment:
      AWS_DEFAULT_REGION: <your AWS region here>
      AWS_ROLE_ARN: <your role ARN here>
    steps:
      - run:
        name: authenticate-and-interact
        command: |
          # use the OpenID Connect token to obtain AWS credentials
          read -r AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \<<< \
            $(aws sts assume-role-with-web-identity \
             --role-arn ${AWS_ROLE_ARN} \
             --role-session-name "CircleCI-${CIRCLE_WORKFLOW_ID}-${CIRCLE_JOB}" \
             --web-identity-token $CIRCLE_OIDC_TOKEN \
             --duration-seconds 3600 \
             --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
             --output text)
          export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
          # interact with AWS
          aws sts get-caller-identity

Advanced usage

You can take advantage of the format of the claims in CircleCI’s OIDC token to limit what your CircleCI jobs can do in AWS. For example, if certain projects should only be able to access certain AWS resources, you can restrict your IAM role so that only CircleCI jobs in a specific project can assume that role.

To do this, edit your IAM role’s trust policy so that only an OIDC token from your chosen project can assume that role. The trust policy determines under what conditions the role can be assumed.

First, find your project’s Project ID under Project Settings > Overview.

Project settings overview

Then add the following condition to your role’s trust policy so that only jobs in your chosen project can assume that role:

"StringLike": {
  "oidc.circleci.com/org/<your organization ID>:sub": "org/<your organization ID>/project/<your project ID>/user/*"
}

This uses StringLike to match the sub claim of CircleCI’s OIDC token in your chosen project. Now, jobs in your other projects cannot assume this role.

Conclusion

Great work! Jobs that used to use a context now contain CircleCI’s OpenID Connect token. Use these tokens to securely interact with cloud providers from your jobs, without the burden of key rotation.