Introdução
Ao fazer deploy de recursos AWS a partir do GitHub Actions, a abordagem tradicional tem sido armazenar chaves de acesso de usuários IAM nos Secrets do GitHub. No entanto, esse método tem vários desafios:
- Risco de vazamento de chaves de acesso
- Necessidade de trabalho de rotação regular
- Aumento de secrets a gerenciar
A autenticação OIDC (OpenID Connect) resolve esses desafios. Este artigo fornece uma explicação detalhada de como configurar a integração OIDC entre o GitHub Actions e a AWS.
O Que é OIDC?
OIDC é um protocolo de autenticação baseado em OAuth 2.0. O GitHub Actions emite um token que pode provar "Sou um workflow deste repositório" para a AWS, e a AWS o verifica e fornece credenciais temporárias.
Benefícios do OIDC
- Sem secrets necessários: Não é necessário armazenar chaves de acesso no GitHub
- Segurança aprimorada: Usa apenas credenciais temporárias
- Controle de permissões granular: Permissões podem ser restritas por repositório ou branch
- Custos de gerenciamento reduzidos: Sem necessidade de rotação de chaves
Configuração AWS
1. Criando um Provedor de Identidade IAM
Primeiro, crie um Provedor de Identidade IAM no AWS Management Console.
- Abra o console IAM
- Clique em "Provedores de identidade" → "Adicionar provedor"
- Insira as seguintes informações:
Tipo de provedor: OpenID Connect
URL do provedor: https://token.actions.githubusercontent.com
Audience: sts.amazonaws.com
Exemplo com Terraform
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"
client_id_list = [
"sts.amazonaws.com",
]
thumbprint_list = [
"6938fd4d98bab03faadb97b34396831e3780aea1"
]
}
Exemplo com AWS SAM
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: GitHub Actions OIDC Provider
Resources:
GitHubOIDCProvider:
Type: AWS::IAM::OIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- sts.amazonaws.com
ThumbprintList:
- 6938fd4d98bab03faadb97b34396831e3780aea1
2. Criando uma Função IAM
Em seguida, crie uma função IAM que o GitHub Actions irá assumir.
Configurando a Política de Confiança
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012: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:your-org/your-repo:*"
}
}
}
]
}
Detalhes das Condições
Você pode restringir o acesso usando o valor token.actions.githubusercontent.com:sub.
# Repositório específico inteiro
repo:your-org/your-repo:*
# Apenas branch específica
repo:your-org/your-repo:ref:refs/heads/main
# Apenas ambiente específico
repo:your-org/your-repo:environment:production
# Apenas pull requests
repo:your-org/your-repo:pull_request
Exemplo com Terraform
data "aws_iam_policy_document" "github_actions_assume_role" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.github_actions.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:your-org/your-repo:*"]
}
}
}
resource "aws_iam_role" "github_actions" {
name = "github-actions-deploy-role"
assume_role_policy = data.aws_iam_policy_document.github_actions_assume_role.json
}
# Anexar políticas de permissão necessárias
resource "aws_iam_role_policy_attachment" "deploy_policy" {
role = aws_iam_role.github_actions.name
policy_arn = "arn:aws:iam::aws:policy/AmazonECS_FullAccess"
}
Exemplo com AWS SAM
Resources:
GitHubActionsRole:
Type: AWS::IAM::Role
Properties:
RoleName: github-actions-deploy-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Federated: !GetAtt GitHubOIDCProvider.Arn
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
token.actions.githubusercontent.com:aud: sts.amazonaws.com
StringLike:
token.actions.githubusercontent.com:sub: repo:your-org/your-repo:*
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonECS_FullAccess
Outputs:
GitHubActionsRoleArn:
Description: ARN da Função IAM do GitHub Actions
Value: !GetAtt GitHubActionsRole.Arn
Export:
Name: GitHubActionsRoleArn
Para fazer deploy com SAM:
sam build
sam deploy --guided
Após o deploy inicial, registre o ARN da função de saída nos Secrets do GitHub como AWS_ROLE_ARN.
3. Anexando Políticas de Permissão
Anexe as permissões necessárias para o deploy à função. Por exemplo:
- Para deploy no S3:
AmazonS3FullAccess - Para deploy no ECS:
AmazonECS_FullAccess - Para Lambda:
AWSLambda_FullAccess
Para ambientes de produção, recomendamos criar políticas personalizadas com base no princípio do menor privilégio.
Configuração do GitHub Actions
Criando um Arquivo de Workflow
Crie .github/workflows/deploy.yml.
name: Deploy para AWS
on:
push:
branches:
- main
# Configurações de permissão para obter token OIDC (Importante!)
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout do código
uses: actions/checkout@v4
- name: Configurar credenciais AWS
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy-role
aws-region: ap-northeast-1
- name: Deploy para S3
run: |
aws s3 sync ./dist s3://your-bucket-name --delete
- name: Verificar deploy
run: |
aws s3 ls s3://your-bucket-name
Pontos Importantes
1. Configuração de permissions
permissions:
id-token: write # Necessário para obter token OIDC
contents: read # Necessário para fazer checkout do repositório
Sem essa configuração, você não conseguirá obter o token OIDC e receberá um erro.
2. Versão da Action configure-aws-credentials
Use v4 ou posterior. Versões anteriores podem não suportar OIDC.
uses: aws-actions/configure-aws-credentials@v4
Exemplo Prático: Fazendo Deploy de uma Aplicação SAM
Aqui está um exemplo de deploy de uma aplicação serverless usando AWS SAM.
name: Deploy da Aplicação SAM
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
env:
AWS_REGION: ap-northeast-1
SAM_STACK_NAME: my-sam-app
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configurar Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Configurar SAM CLI
uses: aws-actions/setup-sam@v2
with:
use-installer: true
- name: Configurar credenciais AWS
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: SAM Build
run: sam build --use-container
- name: SAM Deploy
run: |
sam deploy \
--stack-name ${{ env.SAM_STACK_NAME }} \
--capabilities CAPABILITY_IAM \
--resolve-s3 \
--no-fail-on-empty-changeset \
--no-confirm-changeset
- name: Obter Saídas da Stack
run: |
aws cloudformation describe-stacks \
--stack-name ${{ env.SAM_STACK_NAME }} \
--query 'Stacks[0].Outputs' \
--output table
Exemplo Prático: Deploy no ECS
Como exemplo mais prático, aqui está um workflow de deploy no ECS.
name: Deploy para ECS
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
env:
AWS_REGION: ap-northeast-1
ECR_REPOSITORY: my-app
ECS_SERVICE: my-service
ECS_CLUSTER: my-cluster
CONTAINER_NAME: my-container
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configurar credenciais AWS
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Login no Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build e push da imagem para o ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- name: Baixar definição de tarefa
run: |
aws ecs describe-task-definition \
--task-definition my-task \
--query taskDefinition > task-definition.json
- name: Atualizar definição de tarefa
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
- name: Deploy no ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
Solução de Problemas
Erro: "Not authorized to perform sts:AssumeRoleWithWebIdentity"
Causa: As condições da política de confiança não correspondem
Solução:
- Verifique o valor
token.actions.githubusercontent.com:subna política de confiança da função IAM - Verifique se o nome do repositório e o nome da branch estão corretos
- Verifique o valor real do claim
subnos logs do GitHub Actions
- name: Depurar token OIDC
run: |
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | \
jq -R 'split(".") | .[1] | @base64d | fromjson'
Erro: "Error: Credentials could not be loaded"
Causa: Configuração de permissions ausente
Solução: Adicione o seguinte ao seu arquivo de workflow
permissions:
id-token: write
contents: read
Autenticação Bem-Sucedida mas Erros de Permissão Ocorrem
Causa: Políticas de permissão necessárias não estão anexadas à função IAM
Solução: Anexe as políticas apropriadas à função IAM
aws iam attach-role-policy \
--role-name github-actions-deploy-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess
Melhores Práticas de Segurança
1. Princípio do Menor Privilégio
Conceda apenas as permissões mínimas necessárias.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::your-specific-bucket",
"arn:aws:s3:::your-specific-bucket/*"
]
}
]
}
2. Restringir por Branch ou Tag
Restrinja os deploys de produção a branches específicas apenas.
{
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
}
}
}
3. Funções Separadas por Ambiente
Use funções diferentes para desenvolvimento, staging e produção.
- name: Configurar credenciais AWS (Produção)
if: github.ref == 'refs/heads/main'
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_PROD }}
aws-region: ap-northeast-1
- name: Configurar credenciais AWS (Desenvolvimento)
if: github.ref == 'refs/heads/develop'
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_DEV }}
aws-region: ap-northeast-1
4. Auditoria com CloudTrail
Registre todas as chamadas de API e revise-as regularmente.
# Bucket S3 para CloudTrail
resource "aws_s3_bucket" "cloudtrail" {
bucket = "my-cloudtrail-logs-bucket"
}
resource "aws_s3_bucket_policy" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AWSCloudTrailAclCheck"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:GetBucketAcl"
Resource = aws_s3_bucket.cloudtrail.arn
},
{
Sid = "AWSCloudTrailWrite"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:PutObject"
Resource = "${aws_s3_bucket.cloudtrail.arn}/*"
Condition = {
StringEquals = {
"s3:x-amz-acl" = "bucket-owner-full-control"
}
}
}
]
})
}
# CloudTrail
resource "aws_cloudtrail" "github_actions_audit" {
name = "github-actions-audit"
s3_bucket_name = aws_s3_bucket.cloudtrail.id
include_global_service_events = true
is_multi_region_trail = true
enable_logging = true
depends_on = [aws_s3_bucket_policy.cloudtrail]
}
Conclusão
A integração OIDC entre GitHub Actions e AWS oferece os seguintes benefícios:
- Deploy seguro sem chaves de acesso
- Usa apenas credenciais temporárias
- Controle de permissões granular por repositório ou branch
- Custos de gerenciamento reduzidos
Embora a configuração inicial seja um pouco complexa, uma vez configurada, leva a melhorias de longo prazo em segurança e operabilidade. Recomendo fortemente considerar sua adoção.
