Walk-through - OIDC to AWS

Walk-through - OIDC to AWS

Feel free to comment. Just to note: We moved on to Vault.

OIDC Provider Configuration

Let’s create an OIDC provider configuration.

The URL is always https://oidc.circleci.com/org/<organization-id>, where <organization-id> is the CircleCI Org UUID that represents your organization.

ClientIDList is the audience defined by CircleCI, which is the organization ID as well.

To get the thumbprint follow this guide.

create-open-id-connect-provider.json

{
    "Url": "oidc.circleci.com/org/${CIRCLECI_ORG_UUID}",
    "ClientIDList": [
        "${CIRCLECI_ORG_UUID}"
    ],
    "ThumbprintList": [
        "9e99a48a9960b14926bb7f3b02e22da2b0ab7280"
    ],
    "Tags": [
        {
            "Key": "Name",
            "Value": "circleci-oidc"
        }
    ]
}
aws iam create-open-id-connect-provider \
        --cli-input-json file://create-open-id-connect-provider.json

OIDC IAM Assume Role

This IAM role will be assumed by the web identity authenticated via the OIDC provider.
We will use the OIDC provider ARN and add a condition, who is allowed to assume this role.

Following CircleCIs token format we are going to allow all workflows from our org-id/project-id by wildcarding the user.

create-iam-role.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/oidc.circleci.com/org/${CIRCLECI_ORG_UUID}"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "oidc.circleci.com/org/${CIRCLECI_ORG_UUID}:sub": "org/${CIRCLECI_ORG_UUID}/project/${CIRCLECI_PROJECT_UUID}/user/*"
                }
            }
        }
    ]
}
aws iam create-role \
        --role-name circleci-oidc \
        --assume-role-policy-document file://create-iam-role.json

IAM assume role (AWS Sub-Account)

create-iam-policy.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "eks:DescribeCluster",
                "eks:ListClusters"
            ],
            "Resource": "*"
        }
    ]
}
aws iam create-policy \
        --policy-name circleci-oidc \
        --policy-document file://create-iam-policy.json
aws iam attach-role-policy \
        --role-name circle-oidc \
        --policy-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/circle-oidc"

CI Implementation

Here is what it does:

  1. create aws config directory
  2. write aws profile config
  3. call sts:assume-role-with-web-identity using the ${CIRCLE_OIDC_TOKEN}
  4. parsing response for credentials
  5. write to aws credential file
- run:
    name: AWS OIDC auth
    command: |
      mkdir ~/.aws
      cat > ~/.aws/config \<<- EOF
        [default]
          region = eu-central-1
          output = json

        [profile playground]
          source_profile = default
          role_arn = "arn:aws:iam::${AWS_ACCOUNT_ID}:role/circleci-oidc"
      EOF

      AWS_OIDC_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/circleci-oidc"
      AWS_SESSION_NAME="circleci-oidc"

      AWS_STS_CREDENTIALS=$(aws sts assume-role-with-web-identity \
        --duration-seconds 900 \
        --output "json" \
        --query "Credentials" \
        --role-arn "${AWS_OIDC_ROLE_ARN}" \
        --role-session-name "${AWS_SESSION_NAME}" \
        --web-identity-token "${CIRCLE_OIDC_TOKEN}"
      )

      cat > ~/.aws/credentials \<<- EOF
      [default]
      aws_access_key_id=$(echo $AWS_STS_CREDENTIALS | jq -r '.AccessKeyId')
      aws_secret_access_key=$(echo $AWS_STS_CREDENTIALS | jq -r '.SecretAccessKey')
      aws_session_token=$(echo $AWS_STS_CREDENTIALS | jq -r '.SessionToken')
      EOF
2 Likes