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:
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.
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 e também este Changelog.
O próximo passo é criar o nosso policy document, dando permissão para os repositórios fazerem assume role:
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:
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:
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
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:
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:
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:
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)
- 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.
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! :)
Top comments (3)
Ótimos insights em volta do tema. Congrats!
Por que você usou o sts.amazonaws.com em vez do que foi passado na documentação do GitHub? Fiquei curioso.
Esse é o valor da "audiencia" que é setado no configure-aws-credentials actions (github.com/aws-actions/configure-a...)
A própria documentação do Github foi atualizada para esse novo valor, vou atualizar e retirar esse trecho do artigo visto que ele não é mais válido :)