Pular para o conteúdo principal

Desafio 39: Autenticação e identidade para DevOps

Habilidades do exame abordadas

  • Escolher entre service principals do Microsoft Entra e identidades gerenciadas (atribuídas pelo sistema e atribuídas pelo usuário)
  • Implementar e gerenciar segredos, chaves e certificados usando o Azure Key Vault
  • Implementar autenticação sem segredos (workload identity federation/OIDC)

Cenário

A Contoso Ltd opera 40 microsserviços implantados por meio do Azure Pipelines e GitHub Actions. A equipe de plataforma descobriu recentemente que 12 pipelines se autenticam no Azure usando um service principal compartilhado cujo segredo é armazenado como uma variável de pipeline em texto simples visível para todos os contribuidores. Três equipes criaram independentemente seus próprios service principals com acesso de Contributor à assinatura de produção inteira. A auditoria de segurança também revelou que os segredos são rotacionados manualmente (a última rotação foi há 14 meses). Você deve migrar a Contoso para padrões modernos de identidade que eliminem segredos armazenados sempre que possível e imponham o acesso com privilégios mínimos.

Pré-requisitos

  • Assinatura Azure com função Owner ou User Access Administrator
  • Azure CLI 2.50+ instalado
  • Conta GitHub com um repositório para testes
  • Organização Azure DevOps com um projeto

Tarefas

Tarefa 1: Criar um service principal para autenticação de pipeline

Crie um service principal com um segredo de cliente, com escopo para um grupo de recursos específico.

# Create a resource group for the challenge
az group create --name rg-contoso-challenge39 --location eastus

# Create a service principal scoped to the resource group
az ad sp create-for-rbac \
--name "sp-contoso-pipeline-dev" \
--role "Contributor" \
--scopes "/subscriptions/<subscription-id>/resourceGroups/rg-contoso-challenge39" \
--years 1

Registre a saída (appId, password, tenant). Isso demonstra a abordagem tradicional com um segredo de cliente.

# Verify the service principal can authenticate
az login --service-principal \
--username <appId> \
--password <password> \
--tenant <tenantId>

# Confirm access scope
az group show --name rg-contoso-challenge39

Tarefa 2: Criar uma identidade gerenciada atribuída pelo usuário

Crie uma identidade gerenciada que pode ser compartilhada entre vários recursos Azure sem gerenciar segredos.

# Create a user-assigned managed identity
az identity create \
--name id-contoso-pipeline \
--resource-group rg-contoso-challenge39 \
--location eastus

# Get the principal ID and client ID
IDENTITY_PRINCIPAL_ID=$(az identity show \
--name id-contoso-pipeline \
--resource-group rg-contoso-challenge39 \
--query principalId -o tsv)

IDENTITY_CLIENT_ID=$(az identity show \
--name id-contoso-pipeline \
--resource-group rg-contoso-challenge39 \
--query clientId -o tsv)

# Assign a role to the managed identity
az role assignment create \
--assignee-object-id $IDENTITY_PRINCIPAL_ID \
--assignee-principal-type ServicePrincipal \
--role "Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/rg-contoso-challenge39"

Tarefa 3: Implementar workload identity federation para GitHub Actions

Configure autenticação baseada em OIDC para que o GitHub Actions possa se autenticar no Azure sem armazenar segredos.

# Create an app registration for GitHub Actions
az ad app create --display-name "sp-contoso-github-oidc"

APP_ID=$(az ad app list --display-name "sp-contoso-github-oidc" --query "[0].appId" -o tsv)
OBJECT_ID=$(az ad app list --display-name "sp-contoso-github-oidc" --query "[0].id" -o tsv)

# Create a service principal from the app registration
az ad sp create --id $APP_ID

# Add federated credential for the main branch
az ad app federated-credential create \
--id $OBJECT_ID \
--parameters '{
"name": "github-main-branch",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:contoso/webapp:ref:refs/heads/main",
"description": "GitHub Actions main branch",
"audiences": ["api://AzureADTokenExchange"]
}'

# Add federated credential for the production environment
az ad app federated-credential create \
--id $OBJECT_ID \
--parameters '{
"name": "github-production-env",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:contoso/webapp:environment:production",
"description": "GitHub Actions production environment",
"audiences": ["api://AzureADTokenExchange"]
}'

# Assign role to the service principal
az role assignment create \
--assignee $APP_ID \
--role "Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/rg-contoso-challenge39"

Crie o workflow do GitHub Actions usando OIDC:

# .github/workflows/deploy.yml
name: Deploy with OIDC
on:
push:
branches: [main]

permissions:
id-token: write
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4

- name: Azure Login with OIDC
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Deploy resources
run: |
az group show --name rg-contoso-challenge39

Tarefa 4: Implementar workload identity federation para Azure Pipelines

Configure workload identity federation para service connections do Azure DevOps.

# Create federated credential for Azure DevOps
az ad app federated-credential create \
--id $OBJECT_ID \
--parameters '{
"name": "azdo-contoso-project",
"issuer": "https://vstoken.dev.azure.com/<org-id>",
"subject": "sc://contoso-org/contoso-project/azure-production",
"description": "Azure DevOps service connection",
"audiences": ["api://AzureADTokenExchange"]
}'

No Azure DevOps, crie uma service connection:

  1. Project Settings > Service connections > New service connection
  2. Selecione "Azure Resource Manager"
  3. Selecione "Workload Identity federation (manual)"
  4. Insira a Issuer URL, Service Principal Client ID e Tenant ID
  5. Nomeie a conexão como azure-production

YAML do pipeline usando a conexão federada:

# azure-pipelines.yml
trigger:
branches:
include:
- main

pool:
vmImage: 'ubuntu-latest'

steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'azure-production'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az group show --name rg-contoso-challenge39

Tarefa 5: Comparar métodos de autenticação (tabela de decisão)

CritérioService principal + segredoIdentidade gerenciadaWorkload identity federation
Gerenciamento de segredosRequer armazenamento e rotação de segredosSem segredos para gerenciarSem segredos para gerenciar
Rotação necessáriaSim (expiração 1-2 anos)NãoNão (troca de token)
Funciona a partir deQualquer lugarApenas recursos hospedados no AzureGitHub Actions, Azure Pipelines, provedores OIDC externos
Controle de escopoRBAC personalizadoRBAC personalizadoRBAC personalizado
Trilha de auditoriaLogs de sign-inLogs de sign-inLogs de sign-in com claims federadas
Melhor paraSistemas legados, agentes on-premisesComputação hospedada no Azure (VMs, App Service, AKS)Pipelines CI/CD (GitHub, ADO)
Risco se comprometidoSegredo pode ser usado de qualquer lugar até ser rotacionadoNão pode ser usado fora do recurso atribuídoToken válido apenas para repo/branch/environment específico

Tarefa 6: Configurar credenciais federadas para repos, branches e environments específicos

# Federated credential for pull requests
az ad app federated-credential create \
--id $OBJECT_ID \
--parameters '{
"name": "github-pull-request",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:contoso/webapp:pull_request",
"description": "GitHub Actions pull requests",
"audiences": ["api://AzureADTokenExchange"]
}'

# Federated credential for a specific tag pattern
az ad app federated-credential create \
--id $OBJECT_ID \
--parameters '{
"name": "github-release-tags",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:contoso/webapp:ref:refs/tags/v*",
"description": "GitHub Actions release tags",
"audiences": ["api://AzureADTokenExchange"]
}'

# List all federated credentials
az ad app federated-credential list --id $OBJECT_ID

Exercícios de quebra e conserto

Cenário de quebra 1: Incompatibilidade no subject da credencial federada

Um desenvolvedor relata que o workflow do GitHub Actions falha com "AADSTS70021: No matching federated identity record found."

Causa: O claim subject na credencial federada não corresponde ao token emitido pelo GitHub. Causas comuns incluem nome de repositório errado, branch errado ou configuração de environment ausente no workflow.

Diagnóstico:

# List federated credentials to check subjects
az ad app federated-credential list --id $OBJECT_ID --query "[].{name:name, subject:subject}"
Mostrar solução

Correção: Verifique se o trigger do workflow corresponde ao subject da credencial federada. Se o workflow é executado em refs/heads/develop mas a credencial especifica refs/heads/main, adicione uma nova credencial:

az ad app federated-credential create \
--id $OBJECT_ID \
--parameters '{
"name": "github-develop-branch",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:contoso/webapp:ref:refs/heads/develop",
"audiences": ["api://AzureADTokenExchange"]
}'

Cenário de quebra 2: Identidade gerenciada sem atribuição de função

Uma aplicação usando uma identidade gerenciada retorna "AuthorizationFailed" ao tentar acessar uma conta de armazenamento.

Diagnóstico:

# Check role assignments for the managed identity
az role assignment list --assignee $IDENTITY_PRINCIPAL_ID --all
Mostrar solução

Correção:

az role assignment create \
--assignee-object-id $IDENTITY_PRINCIPAL_ID \
--assignee-principal-type ServicePrincipal \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/rg-contoso-challenge39"

Verificação de conhecimento

1. Qual método de autenticação você deve recomendar para um pipeline executando em um agente self-hosted do Azure DevOps hospedado em uma VM Azure que precisa implantar recursos no Azure?

2. Um workflow do GitHub Actions usa workload identity federation mas falha com "AADSTS700024: Client assertion is not within its valid time range." Qual é a causa mais provável?

3. Ao configurar workload identity federation para Azure Pipelines, qual formato o claim 'subject' deve usar?

4. A Contoso possui três environments (dev, staging, production) e deseja um único registro de aplicativo com credenciais federadas que limitem qual branch do GitHub pode implantar em cada environment. Qual é a abordagem correta?

Limpeza

# Delete the resource group
az group delete --name rg-contoso-challenge39 --yes --no-wait

# Delete the service principal
az ad sp delete --id $(az ad sp list --display-name "sp-contoso-pipeline-dev" --query "[0].id" -o tsv)

# Delete the app registration and its federated credentials
az ad app delete --id $OBJECT_ID

# Delete the managed identity (deleted with resource group)
az identity delete --name id-contoso-pipeline --resource-group rg-contoso-challenge39