Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I connect GitHub actions with AWS deployments without using a secret key?

I'd like to be able to use GitHub Actions to be able to deploy resources with AWS, but without using a hard-coded user.

I know that it's possible to create an IAM user with a fixed credential, and that can be exported to GitHub Secrets, but this means if the key ever leaks I have a large problem on my hands, and rotating such keys are challenging if forgotten.

Is there any way that I can enable a password-less authentication flow for deploying code to AWS?

like image 838
AlBlue Avatar asked Sep 19 '21 13:09

AlBlue


2 Answers

Yes, it is possible now that GitHub have released their Open ID Connector for use with GitHub Actions. You can configure the Open ID Connector as an Identity Provider in AWS, and then use that for an access point to any role(s) that you wish to enable. You can then configure the action to use the credentials acquired for the duration of the job, and when the job is complete, the credentials are automatically revoked.

To set this up in AWS, you need to create an Open Identity Connect Provider using the instructions at AWS or using a Terraform file similar to the following:

resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"
  client_id_list = [
    // original value "sigstore",
    "sts.amazonaws.com", // Used by aws-actions/configure-aws-credentials
  ]
  thumbprint_list = [
    // original value "a031c46782e6e6c662c2c87c76da9aa62ccabd8e",
    "6938fd4d98bab03faadb97b34396831e3780aea1",
  ]
}

The client id list is the 'audience' which is used to access this content -- you can vary that, provided that you vary it in all the right places. The thumbprint is a hash/certificate of the Open ID Connector, and 6938...aea1 is the current one used by GitHub Actions -- you can calculate/verify the value by following AWS' instructions. The thumbprint_list can hold up to 5 values, so newer versions can be appended when they are being made available ahead of time while continuing to use older ones.

If you're interested in where this magic value came from, you can find out at How can I calculate the thumbprint of an OpenID Connect server?

Once you have enabled the identity provider, you can use it to create one or more custom roles (replacing with :

data "aws_caller_identity" "current" {}
resource "aws_iam_role" "github_alblue" {
  name = "GitHubAlBlue"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Action = "sts:AssumeRoleWithWebIdentity"
      Effect = "Allow"
      Principal = {
        Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
      }
      Condition = {
        StringLike = {
          "token.actions.githubusercontent.com:aud" :  ["sts.amazonaws.com" ],
          "token.actions.githubusercontent.com:sub" : "repo:alblue/*"
        }
      }
    }]
  })
}

You can create as many different roles as you need, and even split them up by audience (e.g. 'production', 'dev'). Provided that the OpenID connector's audience is trusted by the account then you're good to go. (You can use this to ensure that the OpenID Connector in a Dev account doesn't trust the roles in a production account and vice-versa.) You can have, for example, a read-only role for performing terraform validate and then another role for terraform apply.

The subject is passed from GitHub, but looks like:

repo:<organization>/<repository>:ref:refs/heads/<branch>

There may be different formats that come out later. You could have an action/role specifically for PRs if you use :ref:refs/pulls/* for example, and have another role for :ref:refs/heads/production/*.

The final step is getting your GitHub Actions configured to use the token that comes back from the AWS/OpenID Connect:

Standard Way

jobs:
  terraform-validate:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Configure AWS credentials from Test account
        uses: aws-actions/configure-aws-credentials@master
        with:
          role-to-assume: arn:aws:iam::<accountid>:role/GitHubAlBlue
          aws-region: us-east-1
      - name: Display Identity
        run: aws sts get-caller-identity

Manual Way

What's actually happening under the covers is something like this:

jobs:
  terraform-validate:
    runs-on: ubuntu-latest
    env:
      AWS_WEB_IDENTITY_TOKEN_FILE: .git/aws.web.identity.token.file
      AWS_DEFAULT_REGION: eu-west-2
      AWS_ROLE_ARN: arn:aws:iam::<accountid>:role/GitHubAlBlue
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Configure AWS
        run: |
          sleep 3 # Need to have a delay to acquire this
          curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
            "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" \
            | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE
          aws sts get-caller-identity

You need to ensure that your AWS_ROLE_ARN is the same as defined in your AWS account, and that the audience is the same as accepted by the OpenID Connect and the Role name as well.

Essentially, there's a race condition between the job starting, and the token's validity which doesn't come in until after GitHub has confirmed the job has started; if the size of the AWS_WEB_IDENITY_TOKEN_FILE is less than 10 chars, it's probably an error and sleep/spinning will get you the value afterwards.

The name of the AWS_WEB_IDENTITY_TOKEN_FILE doesn't really matter, so long as it's consistent. If you're using docker containers, then storing it in e.g. /tmp will mean that it's not available in any running containers. If you put it under .git in the workspace, then not only will git ignore it (if you're doing any hash calculations) but it will also be present in any other docker run actions that you do later on.

You might want to configure your role so that the validity of the period used is limited; once you have the web token it's valid until the end of the job, but the token requested has a lifetime of 15 minutes, so it's possible for a longer-running job to expose that.

It's likely that GitHub will have a blog post on how to configure/use this in the near future. The above information was inspired by https://awsteele.com/blog/2021/09/15/aws-federation-comes-to-github-actions.html, who has some examples in CloudFormation templates if that's your preferred thing.

Update GitHub (accidentally) changed their thumbprint and the example has been updated. See for more information. The new thumbprint is 6938fd4d98bab03faadb97b34396831e3780aea1 but it's possible to have multiple thumbprints in the IAM OpenID connector.

like image 66
AlBlue Avatar answered Sep 22 '22 06:09

AlBlue


Github recently updated their certifcate chain and the thumprint has changed from the one mentioned above a031c46782e6e6c662c2c87c76da9aa62ccabd8e to 6938fd4d98bab03faadb97b34396831e3780aea1

Github Issue: https://github.com/aws-actions/configure-aws-credentials/issues/357

like image 36
smila Avatar answered Sep 22 '22 06:09

smila