GitHub Actions autenticando na AWS via OIDC
Tags: terraform, oidc, aws, github
Published: 2021-11-02 22:41:14

O GitHub recentemente lançou uma funcionalidade para fazer autenticação via OIDC na AWS de dentro dos *workflows*, dando a oportunidade de se livrar de uma vez por todas do gerenciamento de um usuário específico para isso.

Basicamente, a autenticação agora ocorre via OIDC e a única coisa que você precisa para isso é informar qual a *role* vai ser utilizada.

Neste artigo, iremos realizar toda a configuração necessária na AWS via Terraform, e claro, ver como isso funciona.

## Terraform

A configuração via Terraform é relativamente simples e não exige muito conhecimento avançado. 

Vamos começar criando o nosso oidc provider:

```terraform
resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
```

Tanto a informação de *url* quanto *client_id_list* são fornecidas pelo GitHub na própria documentação deles, que você pode ler [aqui](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services). 

Já o *thumbprint_list* foi mais chato de entender pois ele é gerado baseado na chave ssl do certificado da configuração do openid do GitHub. O que importa aqui é: esse valor é baseado na url, então ele é estático e você pode apenas copiar e colar sem medo.

Mas caso você queira ir fundo nesse tópico, leia um pouco [Como Obter a impressão digital da CA raiz para um provedor de identidade OpenID Connect](https://docs.aws.amazon.com/pt_br/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html) e também este [Changelog](https://github.blog/changelog/2022-01-13-github-actions-update-on-oidc-based-deployments-to-aws/).

O próximo passo é criar o nosso policy document, dando permissão para os repositórios fazerem *assume role*:

```terraform
data "aws_iam_policy_document" "github_actions_assume_role" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }
    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }
    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values = [
        "repo:org1/*:*",
        "repo:org2/*:*"
      ]
    }
  }
```

No exemplo acima, qualquer repositório da org1 ou org2 terão permissão *sts:AssumeRoleWithWebIdentity* e consequentemente, assumir a nossa role que vamos criar.

É possível restringir essa permissão para alguns repositórios (e até mesmo branches específicos), para isso basta alterar a sua *condition* para:
 ```terraform 
    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values = [
        "repo:org/meu-repositorio:*",
      ]
    }
 ```

 Seguindo no nosso tutorial, agora precisamos criar a nossa role de fato e associá-la ao documento previamente criado:

 ```terraform
resource "aws_iam_role" "github_actions" {
  name               = "github-actions"
  assume_role_policy = data.aws_iam_policy_document.github_actions_assume_role.json
}
 ```

 Agora precisamos criar um outro documento de *policy*, dessa vez com as permissões que a nossa role terá. 

 Neste caso, iremos ter permissão para realizar algumas operações no nosso ECR, respeitando a única regra de que esse repositório tenha uma *tag* `permit-github-action=true`

 ```terraform
data "aws_iam_policy_document" "github_actions" {
  statement {
    actions = [
      "ecr:BatchGetImage",
      "ecr:BatchCheckLayerAvailability",
      "ecr:CompleteLayerUpload",
      "ecr:GetDownloadUrlForLayer",
      "ecr:InitiateLayerUpload",
      "ecr:PutImage",
      "ecr:UploadLayerPart",
    ]
    resources = ["*"]
    condition {
      test     = "StringEquals"
      variable = "aws:ResourceTag/permit-github-action"

      values = ["true"]
    }
  }
 ```

Reparem que no nosso exemplo estamos utilizando apenas ECR, mas nada impede que você conceda permissão para outros serviços dentro da AWS: S3, SQS, etc.

E por fim, precisamos criar a nossa *policy* baseada no documento que criamos e depois anexar ela à nossa role:

```terraform
resource "aws_iam_policy" "github_actions" {
  name        = "github-actions"
  description = "Grant Github Actions the ability to push to ECR"
  policy      = data.aws_iam_policy_document.github_actions.json
}

resource "aws_iam_role_policy_attachment" "github_actions" {
  role       = aws_iam_role.github_actions.name
  policy_arn = aws_iam_policy.github_actions.arn
}
```

Para finalizar o nosso exemplo, vamos criar um repositório e colocar uma *tag* nele:

```terraform
resource "aws_ecr_repository" "repo" {
  name                 = "meu/repositorio"
  image_tag_mutability = "IMMUTABLE"

  image_scanning_configuration {
    scan_on_push = true
  }

  tag = {
    "permit-github-action" = true
  }
}
```


## Github Actions

Basicamente, a configuração do lado do Github Actions é bem simples e não demanda muita mudança se você já está usando via *access key* e *secret key*

A primeira coisa que você precisa configurar, segundo a própria documentação do GitHub são as permissões:

```yaml
    permissions:
      id-token: write
      contents: read
```

E depois, na actions para configurar as credenciais, informar qual a role vai ser utilizada (a que criamos anteriormente via terraform)

```yaml
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@master
        with:
          role-to-assume: arn:aws:iam::XXXXXXXXXXXX:role/github-actions
          aws-region: eu-west-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
```

Aqui você pode ver o conteúdo final do arquivo YAML.

```yaml
name: Continuous Delivery

on: [push, pull_request]

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    permissions:
      id-token: write
      contents: read

    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@master
        with:
          role-to-assume: arn:aws:iam::XXXXXXXXXXXX:role/github-actions
          aws-region: eu-west-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Checkout
        uses: actions/checkout@v2

      - name: Build, tag, and push image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: meu/repositorio
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
```

Espero que tenham curtido! :)