Secrets e IAM¶
Gerenciamento de credenciais e permissões no GCP.
Visão Geral¶
O projeto usa:
- Secret Manager para armazenar credenciais sensíveis
- IAM para controle de acesso
- Workload Identity Federation para autenticação sem chaves
flowchart TB
subgraph "Identidade"
WIF[Workload Identity<br/>GitHub OIDC]
SA[Service Accounts]
IAM[IAM Policies]
end
subgraph "Secrets"
SM[Secret Manager]
S1[typesense-api-key]
S2[outras-secrets]
end
GH[GitHub Actions] -->|OIDC| WIF
WIF -->|Impersonate| SA
SA -->|Acesso| SM
IAM -->|Permissões| SA
Secret Manager¶
Secrets Armazenadas¶
| Secret | Descrição | Usado por |
|---|---|---|
typesense-api-key |
API Key do Typesense | Portal, Workflows |
umami-database-url |
Connection string PostgreSQL do Umami | Cloud Run destaquesgovbr-umami |
umami-app-secret |
Chave de criptografia de sessões do Umami | Cloud Run destaquesgovbr-umami |
growthbook-mongodb-uri |
Connection string MongoDB Atlas do GrowthBook | Cloud Run destaquesgovbr-growthbook, -api |
growthbook-encryption-key |
Chave de criptografia do GrowthBook | Cloud Run destaquesgovbr-growthbook, -api |
growthbook-jwt-secret |
Chave JWT do GrowthBook | Cloud Run destaquesgovbr-growthbook, -api |
Criar Secret¶
# Via gcloud
echo -n "minha-api-key" | gcloud secrets create typesense-api-key \
--data-file=- \
--replication-policy="automatic"
Via Terraform¶
resource "google_secret_manager_secret" "typesense_api_key" {
secret_id = "typesense-api-key"
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "typesense_api_key" {
secret = google_secret_manager_secret.typesense_api_key.id
secret_data = var.typesense_api_key
}
Acessar Secret¶
# Via gcloud
gcloud secrets versions access latest --secret=typesense-api-key
# Em código Python
from google.cloud import secretmanager
client = secretmanager.SecretManagerServiceClient()
response = client.access_secret_version(
name="projects/PROJECT/secrets/typesense-api-key/versions/latest"
)
secret = response.payload.data.decode("UTF-8")
No Cloud Run¶
env {
name = "TYPESENSE_API_KEY"
value_source {
secret_key_ref {
secret = google_secret_manager_secret.typesense_api_key.id
version = "latest"
}
}
}
Service Accounts¶
Listagem¶
| Service Account | Uso |
|---|---|
github-actions@PROJECT.iam.gserviceaccount.com |
Deploy via GitHub |
typesense-server@PROJECT.iam.gserviceaccount.com |
VM do Typesense |
portal-runner@PROJECT.iam.gserviceaccount.com |
Cloud Run Portal |
destaquesgovbr-umami@PROJECT.iam.gserviceaccount.com |
Cloud Run Umami |
destaquesgovbr-growthbook@PROJECT.iam.gserviceaccount.com |
Cloud Run GrowthBook |
Criar Service Account¶
gcloud iam service-accounts create github-actions \
--display-name="GitHub Actions"
Via Terraform¶
resource "google_service_account" "github_actions" {
account_id = "github-actions"
display_name = "GitHub Actions SA"
}
IAM Roles¶
Roles por Service Account¶
github-actions¶
# Artifact Registry
resource "google_project_iam_member" "github_ar" {
project = var.project_id
role = "roles/artifactregistry.writer"
member = "serviceAccount:${google_service_account.github_actions.email}"
}
# Cloud Run
resource "google_project_iam_member" "github_run" {
project = var.project_id
role = "roles/run.developer"
member = "serviceAccount:${google_service_account.github_actions.email}"
}
# Service Account User (para deploy)
resource "google_project_iam_member" "github_sa_user" {
project = var.project_id
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.github_actions.email}"
}
portal-runner¶
# Secret Manager (leitura)
resource "google_secret_manager_secret_iam_member" "portal_secret" {
secret_id = google_secret_manager_secret.typesense_api_key.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.portal_runner.email}"
}
Workload Identity Federation¶
Conceito¶
Permite que GitHub Actions autentique no GCP sem service account keys:
sequenceDiagram
participant GH as GitHub Actions
participant OIDC as GitHub OIDC Provider
participant WIF as GCP Workload Identity
participant SA as Service Account
participant GCP as GCP APIs
GH->>OIDC: Request OIDC token
OIDC-->>GH: JWT token
GH->>WIF: Exchange JWT for GCP token
WIF->>WIF: Validate JWT
WIF-->>GH: Short-lived GCP credentials
GH->>SA: Impersonate SA
SA-->>GH: SA credentials
GH->>GCP: API calls
Configuração¶
# Pool
resource "google_iam_workload_identity_pool" "github" {
workload_identity_pool_id = "github-pool"
}
# Provider
resource "google_iam_workload_identity_pool_provider" "github" {
workload_identity_pool_id = google_iam_workload_identity_pool.github.workload_identity_pool_id
workload_identity_pool_provider_id = "github-provider"
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.repository" = "assertion.repository"
}
# Restringir a repositórios da org
attribute_condition = "assertion.repository_owner == 'destaquesgovbr'"
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
}
# Permitir impersonation
resource "google_service_account_iam_binding" "workload_identity" {
service_account_id = google_service_account.github_actions.name
role = "roles/iam.workloadIdentityUser"
members = [
"principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/destaquesgovbr/portal"
]
}
Uso no GitHub Actions¶
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123456/locations/global/workloadIdentityPools/github-pool/providers/github-provider
service_account: github-actions@project.iam.gserviceaccount.com
GitHub Secrets¶
Configurar Secrets no Repositório¶
# Via GitHub CLI
gh secret set GCP_PROJECT --body "project-id"
gh secret set WIF_PROVIDER --body "projects/123/locations/global/workloadIdentityPools/github-pool/providers/github-provider"
gh secret set WIF_SERVICE_ACCOUNT --body "github-actions@project.iam.gserviceaccount.com"
Secrets por Repositório¶
portal¶
| Secret | Descrição |
|---|---|
GCP_PROJECT |
ID do projeto GCP |
WIF_PROVIDER |
Provider do Workload Identity |
WIF_SERVICE_ACCOUNT |
Service account para deploy |
TYPESENSE_HOST |
IP interno do Typesense |
TYPESENSE_PORT |
Porta (8108) |
TYPESENSE_API_KEY |
API Key do Typesense |
NEXT_PUBLIC_UMAMI_WEBSITE_ID |
Website ID do Umami |
NEXT_PUBLIC_UMAMI_SCRIPT_URL |
URL do script.js do Umami |
NEXT_PUBLIC_GROWTHBOOK_API_HOST |
URL da API GrowthBook |
NEXT_PUBLIC_GROWTHBOOK_CLIENT_KEY |
Client Key do SDK GrowthBook |
scraper¶
| Secret | Descrição |
|---|---|
HF_TOKEN |
Token HuggingFace (write) |
COGFY_API_KEY |
API Key do Cogfy |
COGFY_COLLECTION_ID |
ID da collection Cogfy |
infra¶
| Secret | Descrição |
|---|---|
TYPESENSE_HOST |
IP do Typesense |
TYPESENSE_PORT |
Porta |
TYPESENSE_API_KEY |
API Key |
Princípio do Menor Privilégio¶
Boas Práticas¶
- Conceder apenas permissões necessárias
- Usar roles predefinidos em vez de permissões individuais
- Evitar roles amplos como Owner ou Editor
- Revisar periodicamente as permissões
- Usar Workload Identity em vez de service account keys
Exemplo de Revisão¶
# Listar bindings de um projeto
gcloud projects get-iam-policy PROJECT_ID
# Listar permissões de uma service account
gcloud iam service-accounts get-iam-policy SA_EMAIL
Auditoria¶
Cloud Audit Logs¶
Habilitado automaticamente para:
- Admin Activity (sempre)
- Data Access (configurável)
Ver logs de acesso¶
gcloud logging read "protoPayload.serviceName=secretmanager.googleapis.com" --limit=10
Rotação de Secrets¶
Rotação Manual¶
# Criar nova versão
echo -n "nova-api-key" | gcloud secrets versions add typesense-api-key --data-file=-
# Desabilitar versão antiga
gcloud secrets versions disable OLD_VERSION --secret=typesense-api-key
Rotação Automática (Futuro)¶
resource "google_secret_manager_secret" "api_key" {
secret_id = "api-key"
rotation {
rotation_period = "2592000s" # 30 dias
next_rotation_time = "2024-01-01T00:00:00Z"
}
}
Troubleshooting¶
Erro de permissão no GitHub Actions¶
Error: Required 'run.services.get' permission
Solução: Adicionar role ao service account:
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:github-actions@PROJECT.iam.gserviceaccount.com" \
--role="roles/run.developer"
Secret não encontrado¶
Error: Secret [projects/PROJECT/secrets/NAME] not found
Solução: Verificar se secret existe e SA tem acesso:
gcloud secrets list
gcloud secrets get-iam-policy SECRET_NAME
Workload Identity falha¶
Error: Unable to exchange token
Solução:
- Verificar
attribute_conditionno provider - Verificar nome do repositório no binding
- Verificar permissões
roles/iam.workloadIdentityUser
Links Relacionados¶
- Arquitetura GCP - Visão geral
- Terraform Guide - IaC
- Deploy Portal - Uso do Workload Identity