DEV Community

Magalu Cloud for Magalu Cloud

Posted on

Workload Identity Federation na prática: acessando Google Cloud sem chaves estáticas a partir do Kubernetes da Magalu Cloud

Como uma aplicação rodando no Kubernetes da Magalu Cloud pode acessar Gemini, Cloud Storage, Pub/Sub e outros serviços da Google Cloud sem credenciais estáticas.

1.Problema

Aplicações modernas estão cada vez mais conectadas a serviços externos: modelos de IA, buckets, filas, bancos analíticos, APIs internas e serviços gerenciados em diferentes clouds.

Em muitos casos, a primeira solução aparece de forma simples: criar uma API Key, ou uma Service Account Key quando o serviço exige identidade IAM, salvar isso em uma Secret e consumir em runtime.

Funciona? Sim

Mas essa simplicidade traz um problema importante: ainda existe uma credencial estática dentro do ambiente de execução.

Mesmo que essa chave esteja em um secret do Kubernetes, em um cofre ou em uma variável de ambiente protegida, ela continua sendo uma chave. Se vazar (seja via leaks de aplicações ou por pessoas), pode ser usada fora do contexto original, por alguém que não necessariamente é a aplicação esperada. Nesse momento paramos de refletir sobre "onde guardamos essa chave?" e passamos a refletir "como certifico que minha aplicação é quem ela é sem uma credencial estática?".

Uma forma moderna de responder a essa pergunta é Workload Identity Federation

Neste artigo, vamos explorar o conceito de Workload Identity Federation, como funciona e como implementamos em um cenário prático de uma aplicação rodando no Kubernetes de um provider (Magalu Cloud) e acessando serviços de outro provider (Google Cloud) sem distribuir Service Account Keys ou outras credenciais estáticas dentro do cluster.

2.Como funciona

Pense no Workload Identity Federation como o sistema de portaria de um condomínio. O Google Cloud seria o condomínio. Dentro dele existem áreas restritas, como garagem, academia, salão de festas e apartamentos — equivalentes aos recursos da GCP, como buckets, bancos, filas e modelos de IA.

Antes, para alguém entrar, era como entregar uma cópia fixa da chave da porta. Essa chave seria parecida com uma API Key: se alguém copia essa chave, consegue tentar entrar como se fosse autorizado.

Com Workload Identity Federation, a lógica muda. Em vez de entregar uma chave permanente, a portaria confia em uma identificação emitida por outro sistema confiável, como o cadastro da administradora do condomínio.

No caso do Kubernetes, o Pod apresenta um token emitido para a ServiceAccount associada a ele. Esse token carrega informações como namespace, nome da ServiceAccount e subject. A portaria valida essa identidade com base nas regras configuradas. Se bater com o que foi permitido, ela libera uma credencial temporária para acessar apenas aquilo que aquela aplicação pode acessar.

Ou seja: o pod não carrega uma chave fixa. Ele prova quem é, recebe um acesso temporário e limitado, e só consegue acessar os recursos autorizados.

Na prática, Workload Identity Federation evita espalhar chaves sensíveis, reduz o risco de vazamento e permite controlar melhor quais workloads podem assumir quais permissões dentro da GCP.

3.O cenário prático

Todo Pod no Kubernetes roda associado a uma ServiceAccount. Essa ServiceAccount representa a identidade da workload dentro do cluster.

O Kubernetes consegue emitir um token temporário para essa identidade. Esse token possui informações como namespace, nome da ServiceAccount e, dependendo da configuração, informações do Pod.

A Google Cloud valida esse token por meio de um Workload Identity Provider. Se o token for válido e atender às condições configuradas, a workload pode obter credenciais temporárias para acessar recursos da Google Cloud.

Em termos simples:

Kubernetes ServiceAccount = identidade da aplicação no cluster
Token projetado = prova temporária dessa identidade
Workload Identity Provider = relação de confiança na Google Cloud
GCP Service Account* = opcional, usada quando escolhemos o modelo de impersonation

  • Neste artigo usamos acesso direto. A Service Account da GCP só entra no modelo de impersonation.

A ServiceAccount é a identidade. O token é a prova temporária dessa identidade.

4.O que este artigo não vai cobrir

Para manter o foco em Workload Identity Federation, este artigo parte do princípio de que a aplicação já está containerizada e executando no Kubernetes da MGC.

Não vamos cobrir:

  • Criação da imagem Docker
  • Publicação da imagem em registry
  • Criação do cluster Kubernetes
  • Estratégia de rollout
  • Helm chart completo

5.Mãos a obra

5.1 Requisitos

5.2 Pool, attribute mapping e attribute conditions

O primeiro comando que vamos utilizar é necessário para configurar o pool, o pool nada mais é que uma entidade na Google Cloud que gerencia identidades externas e ambientes. Neste artigo, o pool representa o domínio externo de confiança usado na PoC: o ambiente Kubernetes da Magalu Cloud. Em ambientes corporativos, o pool costuma representar um domínio externo de confiança, como “AWS produção”, “GitHub Actions produção” ou “Kubernetes MGC produção”.

O attribute mapping é a etapa que “traduz” os dados do JWT do Kubernetes para atributos que a GCP consegue usar nas políticas IAM e nas validações. É a ponte que transforma as claims do JWT da identidade confiável em atributos que a GCP conhece.

Exemplo:
google.subject=assertion.sub,
attribute.namespace=assertion['kubernetes.io']['namespace'],
attribute.service_account_name=assertion['kubernetes.io']['serviceaccount']['name']

As attribute conditions são expressões CEL que decidem se um token deve ser aceito ou rejeitado. Então, mesmo que outro Pod consiga gerar um token Kubernetes válido, a GCP rejeita a federação se ele não cumprir essas condições. O pool define o ambiente confiável, o mapping extrai a identidade do token, e as conditions garantem que apenas workloads específicas possam federar com a Google Cloud.

Exemplo:
google.subject == 'system:serviceaccount:prod:minha-app-sa' &&
attribute.namespace == 'prod' &&
attribute.service_account_name == 'minha-app-sa'
Condições que validam se o subject é 'system:serviceaccount:prod:minha-app-sa'
se o namespace é 'prod' e o nome da service account é 'minha-app-sa'

Com os conceitos esclarecidos, vamos rodar o primeiro comando para gerar o pool:

Ajuste as variáveis

da maneira que for adequada para seu contexto

export PROJECT_ID="poc-workload-identity-501203"
export POOL_ID="poc-workload-identity"
gcloud iam workload-identity-pools create "${POOL_ID}" \
--project="${PROJECT_ID}" \
--location="global" \
--display-name="MGC Production"

5.3 Provider do pool

Agora vamos criar a segunda entidade necessária para configurar o Workload Identity Federation na GCP: o Provider do Pool. Ele descreve a relação de confiança entre a Google Cloud e o emissor externo dos tokens, como AWS, GitHub, GitLab, Kubernetes, Okta. Ele representa uma origem específica de tokens, como um cluster Kubernetes, uma conta AWS ou uma organização GitHub. É no Provider que configuramos como validar o token recebido, quais atributos extrair dele e quais condições ele precisa cumprir para ser aceito.

Pool = ambiente/domínio externo confiável
Provider = emissor específico dos tokens
Google Cloud
└── Pool: mgc-prod
├── Provider: k8s-prod-cluster-01
├── Provider: k8s-prod-cluster-02
└── Provider: k8s-prod-cluster-03
Google Cloud
└── Pool: github-prod
├── Provider: github-org-platform
└── Provider: github-org-data
Google Cloud
└── Pool: aws-prod
├── Provider: aws-account-payments
└── Provider: aws-account-data

Para configurar o provider precisamos de algumas informações do cluster Kubernetes, a primeira delas é a issuer URI:

kubectl get --raw /.well-known/openid-configuration | jq -r .issuer

No nosso exemplo o output foi: https://kubernetes.default.svc.cluster.local

A próxima informação de que precisamos é o JSON Web Key Set (JWKS) do cluster.

kubectl get --raw /openid/v1/jwks > jwks.json

O JWKS não é uma credencial sensível.

Ele contém chaves públicas usadas pela Google Cloud para validar

a assinatura dos tokens emitidos pelo Kubernetes.

Ainda assim, é um detalhe de infraestrutura e pode ser evitado

em repositórios ou artefatos apenas por higiene operacional.

Para manter o exemplo mais simples, vamos restringir o provider pelo namespace e pelo nome da Kubernetes ServiceAccount. Em ambientes produtivos, você pode endurecer essa validação adicionando também o UID da ServiceAccount, que evita reutilização acidental do mesmo nome após uma recriação.

Com esses valores, conseguimos criar o provider vinculado ao pool criado anteriormente:

Ajuste as variáveis

da maneira que for adequada para seu contexto

export PROJECT_ID="poc-workload-identity-501203"
export POOL_ID="poc-workload-identity"
export WORKLOAD_PROVIDER_ID="mgc-k8s-cluster"
export ISSUER_URI="https://kubernetes.default.svc.cluster.local"
export MAPPINGS="google.subject=assertion.sub,attribute.namespace=assertion['kubernetes.io']['namespace'],attribute.service_account_name=assertion['kubernetes.io']['serviceaccount']['name']"
export CONDITIONS="google.subject == 'system:serviceaccount:poc-wif:serviceaccount-poc-wif' && attribute.namespace == 'poc-wif' && attribute.service_account_name == 'serviceaccount-poc-wif'"
gcloud iam workload-identity-pools providers create-oidc ${WORKLOAD_PROVIDER_ID} \
--location="global" \
--workload-identity-pool="${POOL_ID}" \
--project="${PROJECT_ID}" \
--issuer-uri="${ISSUER_URI}" \
--attribute-mapping="${MAPPINGS}" \
--attribute-condition="${CONDITIONS}" \
--jwk-json-path="jwks.json"

Como estamos informando o JWKS manualmente para a GCP, o endpoint OIDC do cluster não precisa estar exposto publicamente. A GCP usará o issuer-uri para validar o claim iss do token e o JWKS enviado para validar a assinatura. Detalhe importante: se as chaves de assinatura do cluster forem rotacionadas, o JWKS configurado no Provider precisa ser atualizado.

5.4 Acesso direto vs impersonation
Depois que a Google Cloud passa a confiar nos tokens emitidos pelo Kubernetes, existem duas formas principais de conceder acesso aos recursos da GCP.

A primeira opção é o acesso direto. Nesse modelo, a própria identidade federada do Kubernetes recebe permissão no recurso da Google Cloud.

Kubernetes ServiceAccount => Workload Identity Federation => Recurso da GCP

A segunda opção é a impersonation de Service Account da GCP. Nesse modelo, a Kubernetes ServiceAccount não acessa o recurso diretamente. Em vez disso, ela recebe permissão para gerar credenciais temporárias de uma Service Account da GCP. A Service Account da GCP é quem possui as permissões no Cloud Storage, Vertex AI, Secret Manager ou outros serviços.

Kubernetes ServiceAccount => Workload Identity Federation => Impersona Service Account da GCP => Recurso da GCP

Ambos os modelos evitam utilizar Service Account Keys, o acesso direto facilita a gestão por ter menos identidades no fluxo. O impersonation é útil quando a organização prefere centralizar permissões em Service Accounts da GCP, quando algum serviço tem limitação com identidades federadas diretas, ou quando já existe um modelo operacional baseado em Service Accounts por aplicação.

A recomendação da GCP é utilizar acesso direto quando possível e é o que estamos utilizando nesse artigo. Mas boa parte da implementação de acesso direto ou impersonation são compartilhadas, ou seja, vocês podem utilizar impersonation também.

5.5 Criando a ServiceAccount Kubernetes e concedendo acesso na GCP

Nosso workload precisa executar com uma Kubernetes ServiceAccount dedicada. Essa ServiceAccount será a identidade da aplicação dentro do cluster e será usada como base para emitir o token projetado que a GCP validará via Workload Identity Federation. Para isso, vamos criar uma ServiceAccount nova, em vez de usar a ServiceAccount default.

Crie um arquivo service-account.yaml com os dados abaixo:

Ajuste o nome e namespace

da maneira que for adequada para seu contexto

apiVersion: v1
automountServiceAccountToken: false
kind: ServiceAccount
metadata:
name: serviceaccount-poc-wif
namespace: poc-wif

Agora rode o comando para criar a service account:

Cria o namespace poc-wif

kubectl create namespace poc-wif

Cria a service account serviceaccount-poc-wif

kubectl apply -f service-account.yaml

Feito isso, já temos a identidade da workload criada no namespace, agora precisamos conceder os acessos diretos à identidade.

Para isso vamos utilizar os comandos do gcloud, no exemplo do artigo vamos mesclar dois cenários para exemplificar: permissão para listar objeto dentro de um bucket e permissão para utilizar o Gemini via Vertex AI. Para simplificar a PoC, vamos usar roles/aiplatform.user. Em produção, ajuste o escopo conforme o menor privilégio necessário.

Ajuste as variáveis

da maneira que for adequada para seu contexto

export PROJECT_ID="poc-workload-identity-501203"
export PROJECT_NUMBER="630729803660"
export POOL_ID="poc-workload-identity"
export NAMESPACE="poc-wif"
export KSA_NAME="serviceaccount-poc-wif"
export BUCKET_NAME="poc-wif"
export MAPPED_SUBJECT="system:serviceaccount:${NAMESPACE}:${KSA_NAME}"
export FEDERATED_PRINCIPAL="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/subject/${MAPPED_SUBJECT}"

Permissão para listar objetos do bucket

gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
--role="roles/storage.objectViewer" \
--member="${FEDERATED_PRINCIPAL}"

Permissão para utilizar modelos do Gemini

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--role="roles/aiplatform.user" \
--member="${FEDERATED_PRINCIPAL}" \
--condition=None
5.6 Configurando o workload no Kubernetes
Até aqui, já fizemos as configurações necessárias na Google Cloud. Agora vamos finalizar o lado Kubernetes, projetando o token temporário dentro do Pod.
Primeiro precisamos criar o configMap que armazena o JSON de configurações do Workload Identity Federation dentro do Pod. Esse arquivo não contém chave privada nem segredo estático; ele apenas orienta a SDK da Google sobre qual Provider utilizar e onde encontrar o token projetado pelo Kubernetes.

Ajuste as variáveis

da maneira que for adequada para seu contexto

export NAMESPACE="poc-wif"
export CONFIGMAP_NAME="poc-wif-workload-identity"
export PROJECT_NUMBER="630729803660"
export POOL_ID="poc-workload-identity"
export PROVIDER_ID="mgc-k8s-cluster"
export TOKEN_PATH="/var/run/secrets/tokens/token"
export CREDENTIALS_FILE_NAME="credentials.json"
export AUDIENCE="//iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/providers/${PROVIDER_ID}"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: ${CONFIGMAP_NAME}
namespace: ${NAMESPACE}
data:
${CREDENTIALS_FILE_NAME}: |
{
"type": "external_account",
"audience": "${AUDIENCE}",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"credential_source": {
"file": "${TOKEN_PATH}"
}
}
EOF
Neste artigo criamos o arquivo manualmente para deixar explícito o que existe dentro dele. Em ambientes produtivos, você também pode gerar esse arquivo com gcloud iam workload-identity-pools create-cred-config.
Por último vamos aplicar o deployment que amarra todas as configurações anteriores, crie o arquivo deployment.yaml com o seguinte conteúdo:
apiVersion: apps/v1
kind: Deployment
metadata:
name: poc-wif
namespace: poc-wif
labels:
app.kubernetes.io/name: poc-wif
app.kubernetes.io/instance: release-name
spec:
selector:
matchLabels:
app.kubernetes.io/name: poc-wif
app.kubernetes.io/instance: release-name
template:
metadata:
labels:
app.kubernetes.io/name: poc-wif
app.kubernetes.io/instance: release-name
spec:
## associa o Pod à Kubernetes ServiceAccount criada anteriormente.##
serviceAccountName: serviceaccount-poc-wif
## evita montar automaticamente o token padrão do Kubernetes. ##
automountServiceAccountToken: false
volumes:
- name: workload-identity-token
projected:
sources:
## solicita ao Kubernetes um token temporário específico para o Workload Identity Federation. ##
- serviceAccountToken:
## precisa apontar para o Workload Identity Provider da GCP.##
audience: "//iam.googleapis.com/projects/630729803660/locations/global/workloadIdentityPools/poc-workload-identity/providers/mgc-k8s-cluster"
## tempo de expiração do token temporário ##
expirationSeconds: 3600
path: "token"
## monta o ConfigMap com o credentials.json ##
- name: workload-identity-credentials
configMap:
name: poc-wif-workload-identity
containers:
- name: poc-wif
image: "gcr.io/google.com/cloudsdktool/google-cloud-cli:alpine"
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
args:
- |
set -e

          echo "=== Autenticando via WIF ==="
gcloud auth login --cred-file="${GOOGLE_APPLICATION_CREDENTIALS}" --quiet >/dev/null 2>&1
echo "Autenticado com sucesso"
      echo "=== Validando Cloud Storage ==="
      gcloud storage ls "gs://${GCS_BUCKET_NAME}"

      echo "=== Validando access token ==="
      ACCESS_TOKEN="$(gcloud auth application-default print-access-token)"
      echo "Access token gerado com sucesso"

      echo "=== Validando Gemini ==="
      apk add --no-cache curl jq &gt;/dev/null

      curl -s \
        -X POST \
        -H "Authorization: Bearer ${ACCESS_TOKEN}" \
        -H "Content-Type: application/json" \
        "https://${GCP_LOCATION}-aiplatform.googleapis.com/v1/projects/${GCP_PROJECT_ID}/locations/${GCP_LOCATION}/publishers/google/models/${GEMINI_MODEL}:generateContent" \
        -d '{
          "contents": [
            {
              "role": "user",
              "parts": [
                {
                  "text": "Responda apenas: OK"
                }
              ]
            }
          ]
        }' | jq -r '.candidates[0].content.parts[0].text'

      echo "=== Teste finalizado. Mantendo container vivo para inspeção ==="
      sleep infinity
  ports:
    - name: http
      containerPort: 80
      protocol: TCP
  env:
      ## aponta o gcloud/SDK para o arquivo de configuração do WIF. ##
    - name: GOOGLE_APPLICATION_CREDENTIALS
      value: "/var/run/secrets/google-cloud/credentials.json"
    - name: GCP_PROJECT_ID
      value: poc-workload-identity-501203
    - name: GCP_LOCATION
      value: us-central1
    - name: GCS_BUCKET_NAME
      value: poc-wif
    - name: GEMINI_MODEL
      value: gemini-2.5-flash
  resources:
    limits:
      cpu: 250m
      memory: 256Mi
    requests:
      cpu: 100m
      memory: 128Mi
  volumeMounts:
      ## monta o token projetado em /var/run/secrets/tokens/* ##
    - name: workload-identity-token
      mountPath: "/var/run/secrets/tokens"
      readOnly: true
      ## monta o arquivo credentials.json em /var/run/secrets/google-cloud ##
    - name: workload-identity-credentials
      mountPath: "/var/run/secrets/google-cloud"
      readOnly: true
Enter fullscreen mode Exit fullscreen mode

Agora aplique o deployment no cluster. Esse deployment roda alguns comandos do gcloud usando a imagem docker google-cloud-cli:alpine; com o script conseguimos validar as permissões de listagem de objetos no bucket e comunicação com modelos do Gemini.

kubectl apply -f deployment.yaml
Com tudo configurado corretamente, conseguimos o output correto dentro do pod, a listagem dos objetos do bucket e a resposta do Gemini

kubectl logs deploy/poc-wif -n poc-wif

=== Autenticando via WIF ===
Autenticado com sucesso
=== Validando Cloud Storage ===
gs://poc-wif/primeiro-arquivo-poc-wif.txt
gs://poc-wif/segundo-arquivo-poc-wif.txt
=== Validando access token ===
Access token gerado com sucesso
=== Validando Gemini ===
OK
=== Teste finalizado. Mantendo container vivo para inspeção ===

6.Conclusão
Com essa abordagem conseguimos implementar uma workload que se conecta a serviços da Google Cloud sem API Key e sem Service Account Key. Apenas criamos um arquivo credentials.json montado no Pod que não contém chave privada; ele apenas orienta a SDK sobre qual Provider usar e onde encontrar o token projetado pelo Kubernetes.

O ganho principal dessa abordagem é eliminar credenciais estáticas reutilizáveis. O acesso passa a depender de tokens temporários, validações do Provider, condições sobre namespace/ServiceAccount e permissões IAM mínimas.

7.Referências

https://docs.cloud.google.com/iam/docs/workload-identity-federation-with-kubernetes

https://docs.cloud.google.com/gemini-enterprise-agent-platform/machine-learning/authentication#on-prem

https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#serviceaccount-token-volume-projection

https://docs.cloud.google.com/iam/docs/workload-identity-federation

https://cel.dev/overview/cel-overview#why_cel

https://github.com/brunosm013/poc-wif

Top comments (0)