
Não é incomum uma aplicação que roda no Google Kubernetes Engine ( GKE) precisar acessar APIs da Amazon Web Services ( AWS). Talvez ela precise rodar uma consulta analítica no Amazon Redshift, acessar dados em um bucket do Amazon S3, converter texto em fala com o Amazon Polly ou usar qualquer outro serviço da AWS. Esse cenário multi-cloud é comum hoje em dia, já que as empresas trabalham com vários provedores de nuvem.

O acesso entre nuvens traz um novo desafio: como gerenciar as credenciais necessárias para acessar, a partir de um provedor, serviços que rodam em outro. A abordagem ingênua de distribuir e armazenar segredos do provedor não é a mais segura; espalhar credenciais de longa duração por cada serviço que precisa acessar a AWS é difícil de gerenciar e representa um risco de segurança.
Soluções atuais
Cada nuvem tem sua própria solução para esse desafio e, se você trabalha com um único provedor, ela costuma ser mais que suficiente.
O Google Cloud lançou o Workload Identity, a forma recomendada para que aplicações no GKE se autentiquem e consumam outros serviços do Google Cloud. O Workload Identity vincula service accounts do Kubernetes a service accounts do Cloud IAM, permitindo que você use conceitos nativos do Kubernetes para definir quais workloads rodam sob quais identidades e que esses workloads acessem automaticamente outros serviços do Google Cloud — tudo sem precisar gerenciar segredos do Kubernetes nem chaves de service account do IAM. Confira o post da DoiT sobre Kubernetes GKE Workload Identity.
A Amazon Web Services oferece uma funcionalidade parecida com o recurso IAM Roles for Service Accounts. Em clusters Amazon EKS, você associa um IAM role a uma service account do Kubernetes. Essa service account passa a conceder permissões da AWS aos containers de qualquer pod que a utilize. Com isso, você não precisa mais dar permissões amplas ao IAM role do worker node só para que os pods consigam chamar APIs da AWS.
Mas e se você roda seu workload em um cluster GKE e quer acessar serviços da AWS sem abrir mão da segurança?
Definição do caso de uso
Vamos supor que você já tenha uma conta AWS, um cluster GKE e que sua empresa tenha decidido rodar uma aplicação baseada em microsserviços nesse cluster, mas ainda queira usar recursos na conta AWS (Amazon S3 e SNS) para integrar com outros sistemas implantados na AWS.
Por exemplo, o orchestration job (implantado como um Kubernetes Job) roda dentro de um cluster GKE e precisa enviar um arquivo de dados para um bucket S3 e publicar uma mensagem em um tópico do Amazon SNS. O equivalente em linha de comando seria algo assim:
https://gist.github.com/90682305b879a96d273284df5d20fdcb
Um exemplo bem simples. Para que esses comandos funcionem, o orchestration job precisa ter credenciais da AWS disponíveis, e elas precisam dar conta das chamadas de API necessárias.
A abordagem ingênua (e insegura): credenciais IAM de longa duração
Exporte a Access Key e a Secret Key de algum AWS IAM User e injete as credenciais da AWS no orchestration job, seja como arquivo de credenciais ou como variáveis de ambiente. Provavelmente não direto assim, mas usando o recurso Kubernetes Secrets protegido por uma política de autorização RBAC.
O risco é que essas credenciais nunca expiram. Elas precisam ser transportadas de alguma forma do ambiente AWS para o ambiente GCP e, na maioria dos casos, acabam guardadas em algum lugar para que dê para recriar o orchestration job mais tarde, se preciso.
Quando se usa credenciais AWS de longa duração, há várias formas de a sua conta AWS ser comprometida: commits acidentais com credenciais em um repositório do GitHub, anotações em um sistema de Wiki, reaproveitamento das mesmas credenciais entre serviços e aplicações diferentes, acesso irrestrito e por aí vai.
Até dá para projetar uma solução adequada de gestão dessas credenciais emitidas para um IAM User, mas nada disso será necessário se você simplesmente não criar credenciais de longa duração.
A abordagem proposta
A ideia é atribuir um AWS IAM Role a um Pod do GKE, do mesmo jeito que o Workload Identity e o EKS IAM Roles for Service Accounts fazem em cada nuvem.
Felizmente, a AWS permite criar um IAM role para provedores de identidade OpenID Connect Federation (OIDC) em vez de IAM users. Já o Google implementa um provedor OIDC e o integra fortemente ao GKE pelo recurso Workload Identity, fornecendo um token OIDC válido para um pod do GKE que rode sob uma Kubernetes Service Account vinculada a uma Google Cloud Service Account. Tudo isso entra em cena para implementar o acesso seguro do GKE à AWS.
Trocando o access token OIDC pelo ID token
Falta uma peça para fechar o quebra-cabeça. Com o Workflow Identity bem configurado, o Pod do GKE recebe um access token OIDC que dá acesso aos serviços do Google Cloud. Já para obter credenciais temporárias da AWS pelo AWS Security Token Service ( STS), você precisa fornecer um ID token OIDC válido.
O AWS SDK (e a ferramenta aws-cli) solicita automaticamente credenciais temporárias da AWS ao STS quando estas variáveis de ambiente estão configuradas corretamente:
AWS_WEB_IDENTITY_TOKEN_FILE- o caminho para o arquivo do web identity token (ID token OIDC)AWS_ROLE_ARN- o ARN do role a ser assumido pelos containers do PodAWS_ROLE_SESSION_NAME- o nome aplicado a essa sessão de assume-role
Pode parecer um pouco complexo, mas vou apresentar um guia passo a passo e o projeto open source de apoio dointl/gtoken para simplificar a configuração.
O Mutating Admission webhook do Kubernetes gtoken-webhook
O gtoken-webhook é um mutating admission webhook do Kubernetes que altera qualquer Pod do K8s rodando sob uma Kubernetes Service Account com a anotação específica (veja os detalhes abaixo).
Fluxo de mutação do gtoken-webhook
O gtoken-webhook injeta um gtoken``initContainer no Pod alvo, mais um container sidecar gtoken (para renovar o ID token OIDC pouco antes de expirar), monta o volume de token e injeta três variáveis de ambiente específicas da AWS. O container gtoken gera um GCP OIDC ID Token válido e o grava no volume de token. Ele também injeta as variáveis de ambiente necessárias da AWS.
O AWS SDK faz automaticamente as chamadas AssumeRoleWithWebIdentity ao AWS STS em seu nome. Ele cuida do cache em memória e da renovação das credenciais conforme necessário.

Guia do fluxo de configuração
Implantar o gtoken-webhook
- Para implantar o servidor
gtoken-webhook, precisamos criar um service de webhook e um deployment no nosso cluster Kubernetes. É bem direto, com exceção de um detalhe: a configuração TLS do servidor. Se você abrir o arquivo deployment.yaml, vai ver que o certificado e a chave privada correspondente são lidos a partir de argumentos de linha de comando, e que o caminho até esses arquivos vem de um volume mount apontando para um Kubernetes secret:
https://gist.github.com/b9fc2fe5acb556e3b48ee89af7db368e
O mais importante é lembrar de definir o certificado CA correspondente depois, na configuração do webhook, para que o apiserver saiba que ele deve ser aceito. Por ora, vamos reaproveitar o script originalmente escrito pelo time do Istio para gerar uma certificate signing request. Em seguida, enviamos a requisição para a API do Kubernetes, obtemos o certificado e criamos o secret necessário a partir do resultado.
Primeiro, rode o script webhook-create-signed-cert.sh e confira se o secret com o certificado e a chave foi criado:
https://gist.github.com/91d2bd8cddf116aa0fa8cefbf3208bcd
Com o secret criado, podemos criar um deployment e um service. São recursos padrão de deployment e service do Kubernetes. Até aqui, só produzimos um servidor HTTP que aceita requisições pelo service na porta 443:
https://gist.github.com/34ec56018700a610f37a09a9e846eecb
Configurar o Mutating Admission webhook
Agora que nosso servidor de webhook está rodando, ele já pode receber requisições do apiserver. Mas, antes, precisamos criar alguns recursos de configuração no Kubernetes. Vamos começar pelo validating webhook e depois configuramos o mutating webhook. Se você der uma olhada na configuração do webhook, vai notar que ela contém um placeholder para CA_BUNDLE:
https://gist.github.com/d40101fba8203cc77d62629dde4d6a8f
Existe um pequeno script que substitui o placeholder CA_BUNDLE na configuração por essa CA. Rode este comando antes de criar a validating webhook configuration:
https://gist.github.com/5e0ed44892fc7a21709ac18b381cb5f6
Crie uma mutating webhook configuration:
https://gist.github.com/a545e146e1168f3b88d04b3f4c490d68
Configurar RBAC para o gtoken-webhook
Crie a Kubernetes Service Account a ser usada com o gtoken-webhook:
https://gist.github.com/aabfa30bc005ae8654b360814523c9bc
Defina a permissão RBAC para a service account do webhook:
https://gist.github.com/5ceb8a6e1ca19d9be3b8ed2d810ddc96
Variáveis do fluxo
Algumas das variáveis a seguir devem ser fornecidas pelo usuário; outras são geradas automaticamente e reaproveitadas nos próximos passos.
PROJECT_ID- ID do projeto GCP (fornecido pelo usuário)CLUSTER_NAME- nome do cluster GKE (fornecido pelo usuário)GSA_NAME- nome da Google Cloud Service Account (fornecido pelo usuário)GSA_ID- ID único da Google Cloud Service Account (gerado pelo Google)KSA_NAME- nome da Kubernetes Service Account (fornecido pelo usuário)KSA_NAMESPACE- namespace do Kubernetes (fornecido pelo usuário)AWS_ROLE_NAME- nome do AWS IAM role (fornecido pelo usuário)AWS_POLICY_NAME- uma AWS IAM policy a ser atribuída ao IAM role (fornecida pelo usuário)AWS_ROLE_ARN- identificador ARN do AWS IAM Role (gerado pela AWS)
Google Cloud: habilitar o GKE Workload Identity
Crie um novo cluster GKE com o Workload Identity habilitado:
https://gist.github.com/0fcbc309cd1967edac19bac6795d651d
ou atualize um cluster existente:
https://gist.github.com/05a94c349aa4a274fc27daae45448be9
Google Cloud: criar uma Google Cloud Service Account
Crie uma Google Cloud Service Account:
https://gist.github.com/1790d9e61c63123937d01c8146c1a67d
Atualize a Google Service Account GSA_NAME com os seguintes roles:
roles/iam.workloadIdentityUser- representar service accounts a partir dos GKE Workloadsroles/iam.serviceAccountTokenCreator- representar service accounts para criar access tokens OAuth2, assinar blobs ou tokens JWT
https://gist.github.com/19570300e584798d4d5c3a234ceb0d62
AWS: criar AWS IAM Role com Google OIDC Federation
Prepare o documento de trust policy do role para o provedor OIDC do Google:
https://gist.github.com/d80514354a5270bd0f6766dee7947902
Crie o AWS IAM Role com Google Web Identity:
https://gist.github.com/94f0ce213cf60cfce6ac987af51afbed
Atribua as policies desejadas ao AWS Role:
https://gist.github.com/afd9ecc776cfba5148137769aaf71a5f
Obtenha o ARN do AWS Role para usar na anotação da SA do K8s:
https://gist.github.com/d407769dffd4eb276ed22f7b723cbdc4
GKE: criar uma Kubernetes Service Account
Crie um namespace do K8s:
https://gist.github.com/59dafb6baf1223552eb7d9fc3c3cbea3
Crie a K8s Service Account:
https://gist.github.com/58fd6924e79270857f39d924a0f12011
Adicione na K8s Service Account a anotação do GKE Workload Identity (e-mail da GCP Service Account):
https://gist.github.com/0be380a02ff228a38b2a9e5400b75dcb
Adicione na K8s Service Account a anotação com o ARN do AWS Role:
https://gist.github.com/01620978944525a8df6c3d76ddc1fb67
Rodar a demo
Suba um novo Pod do K8s usando a Service Account ``${KSA_NAME}``` do K8s:
https://gist.github.com/9c5a30f5dd3825d5c3bc6adc14b76fbc
Referências externas
- GitHub: Acesso seguro a serviços AWS a partir de um cluster GKE com doitintl/gtoken
- Documentação AWS: Creating a Role for Web Identity or OpenID Connect Federation
- Blog: Kubernetes GKE Workload Identity link
- Blog AWS: Introducing fine-grained IAM roles for service accounts link
- GitHub: autenticação na AWS via Web Identity Federation a partir do Google Cloud — projeto shrikant0013/gcp-aws-webidentityfederation no GitHub
- Blog: Using GCP Service Accounts to access AWS IAM Roles — post no blog de Colin Panisset
Resumo
Espero que este post seja útil para você. Fico no aguardo dos seus comentários e dúvidas.
Quer mais conteúdo? Confira nosso blog ou siga o Alexei no Twitter.