Secure Deploys from GitHub with the AWS CDK
Tags: aws, cdk, github, security
Published: 2023-06-11 13:07:38

---
title: Secure Deploys from GitHub with the AWS CDK
published: true
date: 2023-06-09 12:00:00 UTC
tags: AWS,CDK,GitHub,Security
canonical_url: https://jasonbutz.info/2023/06/aws-cdk-deploys-github/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejwjvqisdzcb0ts5zsz6.png
description: 'Learn how to enhance security and streamline the deployment process from GitHub to AWS using OpenID Connect (OIDC). This article explains how to establish a trust relationship between AWS and GitHub, configure an IAM Role, and update GitHub Workflow to leverage OIDC for temporary credentials.'
---

I recently started consolidating the DNS for all the domains I own to AWS Route 53. During that change, I had a few sites that used root CNAME capabilities of different services, but that isn’t something Route 53 provides. It was easy enough to switch to deploying the site on AWS using the CDK and hosting it with S3 and CloudFront. The whole setup took me about an hour to get everything deployed from my machine. When it came time to add a deployment through a GitHub Workflow, I decided to stop copying and pasting credentials into workflow secrets in GitHub. Instead, I set up OpenID Connect (OIDC).

## What is OIDC?

In general, OpenID Connect is an authentication layer built on OAuth 2.0. It is a mechanism to verify an identity based on authentication performed by an authorization server and provide access to basic information about that identity. Focusing on this particular use case, OIDC is a mechanism that enables GitHub to receive temporary credentials from AWS. We configure information about GitHub and define an IAM Role GitHub can assume, then without needing any additional credentials from us, GitHub can use that Role while executing our workflow. Configuring OIDC means we trust GitHub, and there is a degree of risk but less risk than sharing access credentials. We can adhere to least-privilege access guidelines by properly configuring our IAM Role.

## Establishing the Trust Relationship

The first step is to indicate to AWS that we want to trust GitHub. GitHub [provides documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) focused on AWS to help through this process.

Working through the AWS Console, this process is straightforward. First, you go to the Identity and Access Management (IAM) service; then you select _Identity providers_, and then _Add provider_.

Select _OpenID Connect_ and then put `https://token.actions.githubusercontent.com` as the _Provider URL_. Then click _Get thumbprint_. That should load information about GitHub’s public-key certificate. The thumbprint value at the time of writing should be `6938fd4d98bab03faadb97b34396831e3780aea1`. Enter `sts.amazonaws.com` for the audience and click the _Add provider_ button.

![AWS Console screenshot showing the IAM service's 'Add an Identity provider' form with the details filled in to add GitHub as an OIDC identity provider](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snz3afagcnexe72te7ez.png)

Once you have done that, you should see `token.actions.githubusercontent.com` in your list of identity providers. A given provider can only be defined once per AWS account. If you plan to configure the identity provider via automation, you’ll need to account for that.

## Building the IAM Role

Building our IAM Role for GitHub to use is simple but does require careful attention to the trust policy and permissions. In the AWS Console, go to the Identity and Access Management (IAM) service; select _Roles_, then click the _Create role_ button. For the _Trust entity type_ choose _Web identity_. For the _Identity provider_ field, select _token.actions.githubusercontent.com_, and for the _Audience_, select _sts.amazonaws.com_. Click the _Next_ button.

![ç](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3xn52tg7hl7ks1tz151f.png)

If you want to define policies here, feel free. Be sure you keep your policies as restricted as possible. I use the AWS CDK and will define the permissions using an inline policy. Click the _Next_ button. On the next page, enter a role name and a description. Click _Create role_.

Pull up the IAM Role you created in the console. The trust policy needs to be updated. Any GitHub repository could use this IAM Role if they know the ARN for it. We will add a second condition and restrict access to a single repository.

Your trust policy should look similar to this:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::000000000000:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
```

Add a `StringLike` property for the operator that checks the `token.actions.githubusercontent.com:sub` condition key against a value like `repo:jbutz/example-repo:*`. You’ll end up with something like this:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::000000000000:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:jbutz/example-repo:*"
        }
      }
    }
  ]
}
```

In this example policy, any branch from the GitHub repository named `example-repo` attached to my GitHub username (jbutz) can assume this IAM Role.

If I wanted only to allow the main branch to deploy, I could move the expression to the `StringEquals` object and use `repo:jbutz/example-repo:ref:refs/heads/main` as the value. The values present in the subject property will vary based on the GitHub Workflow. GitHub has [details in its documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims). You do need to be aware that there is a limited set of OIDC claims AWS supports, AWS has the details in the [IAM service documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_iam-condition-keys.html#cross-condition-keys-wif). I have an environment configured, which changes the `sub` value, and tried to use the `ref` claim. Unfortunately, AWS doesn’t support arbitrary OIDC claims. I hope that is a feature added in the future.

As I said, my goal was to enable the CDK to deploy; this means the permissions I need to grant are relatively limited. The CDK assumes specific IAM Roles during deployment, so I can focus on allowing only those actions. The CDK uses four IAM Roles, one for deployment, one for lookups, one for publishing files, and one for container image publishing. The role names follow a predictable pattern; this enables the CDK to know what roles to look up by default. The only action required is `sts:AssumeRole`. An example policy that provides access to all four roles is below:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Resource": [
        "arn:aws:iam::000000000000:role/cdk-hnb659fds-deploy-role-000000000000-us-east-1",
        "arn:aws:iam::000000000000:role/cdk-hnb659fds-file-publishing-role-000000000000-us-east-1",
        "arn:aws:iam::000000000000:role/cdk-hnb659fds-image-publishing-role-000000000000-us-east-1",
        "arn:aws:iam::000000000000:role/cdk-hnb659fds-lookup-role-000000000000-us-east-1"
      ],
      "Effect": "Allow"
    }
  ]
}
```

## GitHub

Now that you have your IAM Role, you can update your GitHub Workflow to use this new IAM Role to provide your AWS credentials. The repository for [AWS’s _configure AWS credentials_ GitHub Action provides ample examples](https://github.com/aws-actions/configure-aws-credentials#assumerolewithwebidentity-recommended), as well as a [sample CloudFormation template](https://github.com/aws-actions/configure-aws-credentials#sample-iam-oidc-cloudformation-template) to configure an IAM Role.

I’ve put together the CDK construct I created for myself and my example GitHub Workflow in a [GitHub Gist](https://gist.github.com/jbutz/f21c73de8e5d23c25691149b30a2cb4b). You can also find the Gist at the bottom of the page.

## Quick Setup, Better Security

Once you have the OIDC identity provider configured in your account and know how to define the IAM Role’s trust policy, improving your security and credential handling becomes much easier when deploying from AWS to GitHub. You can further customize the `sub` claim in the tokens sent to AWS and better restrict access to AWS using GitHub’s OIDC API. They explain this functionality in the [OpenID Connect documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#customizing-the-token-claims).

{% embed https://gist.github.com/jbutz/f21c73de8e5d23c25691149b30a2cb4b %}