EKS Access Entries com Terraform: Controle de Acesso sem o ConfigMap aws-auth

Por muito tempo, dar acesso a um cluster EKS significava editar o ConfigMap aws-auth dentro do próprio cluster. Um erro de indentação nesse YAML trancava todo mundo para fora. Com EKS access entries, o acesso passa a ser um recurso da API do EKS: você cria, lista e remove pelo AWS CLI ou pelo Terraform, sem tocar no cluster.

Este é o terceiro artigo da série artigos-eks. O primeiro já criou o cluster no modo API, e o segundo separou as cargas em node groups On-Demand e Spot. Agora vamos definir quem entra no cluster e com qual permissão: um time de plataforma com admin, um time de produto restrito ao próprio namespace e um pipeline de deploy com RBAC próprio.

Segurança: os ARNs de IAM role deste guia usam a conta fictícia 111122223333. Passe os ARNs reais por variável, nunca fixos no código de um repositório público, e prefira roles assumidas via SSO a usuários IAM com access key.

Sumário

Como funcionam as EKS access entries

Uma access entry liga uma identidade IAM (role ou usuário) ao cluster. Sozinha, ela só diz “essa identidade é conhecida”. A permissão dentro do Kubernetes vem de uma de duas formas:

  • Access policy: uma política gerenciada pela AWS, associada à entry, com escopo do cluster inteiro ou de namespaces específicos.
  • Grupos do Kubernetes (kubernetes_groups): a identidade entra no cluster como membro de um grupo, e você define o que esse grupo pode fazer com Role e RoleBinding comuns.

Dá para combinar as duas na mesma entry. O resultado é a soma das permissões.

Modo de autenticação do cluster

O cluster precisa estar num modo que aceite access entries. São três:

  • CONFIG_MAP: só o aws-auth. É o comportamento antigo.
  • API_AND_CONFIG_MAP: os dois ao mesmo tempo. É o modo de transição.
  • API: só access entries. O aws-auth é ignorado.

A mudança só anda num sentido: de CONFIG_MAP para API_AND_CONFIG_MAP e daí para API. Não há volta. No ambiente deste artigo o modo vem de uma variável, com API como padrão:

module "eks" {
  source = "../../terraform-eks-modules"

  name               = var.cluster_name
  kubernetes_version = var.kubernetes_version
  subnet_ids         = module.vpc.private_subnet_ids

  authentication_mode = var.authentication_mode

  public_access_cidrs = var.public_access_cidrs
}

Os nós dos managed node groups não precisam de entry escrita à mão: no modo API, o EKS cria sozinho a access entry da IAM role dos nós quando o node group é criado.

Access policies gerenciadas

As políticas mais usadas espelham os papéis padrão do Kubernetes:

  • AmazonEKSClusterAdminPolicy: equivalente ao cluster-admin. Tudo, em todo o cluster.
  • AmazonEKSAdminPolicy: equivalente ao admin. Administra recursos, inclusive RBAC dentro do escopo.
  • AmazonEKSEditPolicy: equivalente ao edit. Cria e altera a maioria dos objetos, sem mexer em RBAC.
  • AmazonEKSViewPolicy: equivalente ao view. Só leitura, sem ver Secrets.

A lista completa, com os ARNs, sai pelo AWS CLI:

aws eks list-access-policies --query 'accessPolicies[].name' --output table

EKS access entries com Terraform

O módulo terraform-eks-access-entry-modules recebe um mapa de perfis. Cada perfil tem o ARN da identidade, os grupos do Kubernetes e as access policies com o escopo:

module "access" {
  source = "../../terraform-eks-access-entry-modules"

  cluster_name = module.eks.name

  access_entries = {
    plataforma = {
      principal_arn = var.platform_admin_role_arn
      policies = {
        admin = {
          policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
        }
      }
    }

    time-loja = {
      principal_arn = var.team_role_arn
      policies = {
        edit = {
          policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy"
          scope_type = "namespace"
          namespaces = ["loja"]
        }
      }
    }

    deploy-loja = {
      principal_arn     = var.deploy_role_arn
      kubernetes_groups = ["loja-deployers"]
    }
  }
}
  • plataforma: admin do cluster inteiro. Escopo cluster é o padrão do módulo.
  • time-loja: pode criar e alterar objetos, mas só no namespace loja. Fora dele, não enxerga nada.
  • deploy-loja: nenhuma access policy. A permissão vem do RBAC do grupo loja-deployers, que veremos a seguir.

Dentro do módulo, cada par perfil e política vira um aws_eks_access_policy_association. O plan mostra a associação do time da loja com o escopo de namespace:

  # module.access.aws_eks_access_policy_association.this["time-loja/edit"] will be created
  + resource "aws_eks_access_policy_association" "this" {
      # (atributos calculados omitidos)
      + cluster_name  = "eks-blog-castilho"
      + policy_arn    = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy"
      + principal_arn = "arn:aws:iam::111122223333:role/time-loja"

      + access_scope {
          + namespaces = [
              + "loja",
            ]
          + type       = "namespace"
        }
    }

Grupos do Kubernetes com RBAC próprio

Access policies cobrem os papéis genéricos. Quando o pipeline de deploy precisa de algo bem específico, como atualizar Deployments e só ler o resto, o caminho é um grupo do Kubernetes com RBAC próprio. Salve como rbac-loja-deployers.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: loja
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: loja
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "update", "patch"]
  - apiGroups: [""]
    resources: ["pods", "services", "configmaps"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: loja-deployers
  namespace: loja
subjects:
  - kind: Group
    name: loja-deployers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: deployer
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f rbac-loja-deployers.yaml

O nome do grupo na access entry e no RoleBinding precisa ser idêntico. Grupos que começam com system: são reservados e não podem ser usados em access entries.

Migrando do aws-auth para EKS access entries

Num cluster antigo, que ainda usa o aws-auth, a migração é feita em três passos sem derrubar ninguém:

  1. Mude o cluster para API_AND_CONFIG_MAP (authentication_mode = "API_AND_CONFIG_MAP"). O aws-auth continua valendo.
  2. Crie uma access entry para cada identidade que hoje está no aws-auth e teste o acesso de cada uma.
  3. Com todas as identidades cobertas, mude para API. A partir daí o aws-auth é ignorado e pode ser apagado.

Antes do passo 3, liste o que já existe para não esquecer ninguém:

kubectl get configmap aws-auth -n kube-system -o yaml
aws eks list-access-entries --cluster-name eks-blog-castilho

terraform plan

As três roles são obrigatórias. No exemplo abaixo os ARNs usam a conta fictícia 111122223333; troque pelos da sua conta e pelo IP da sua rede:

terraform init
terraform validate
terraform plan \
  -var 'public_access_cidrs=["SEU_IP/32"]' \
  -var 'platform_admin_role_arn=arn:aws:iam::111122223333:role/plataforma-admin' \
  -var 'team_role_arn=arn:aws:iam::111122223333:role/time-loja' \
  -var 'deploy_role_arn=arn:aws:iam::111122223333:role/pipeline-deploy-loja' \
  -out=tfplan

O código foi validado com terraform validate e terraform plan contra uma conta AWS real, sem apply. São 33 recursos: os 28 do cluster base mais três access entries e duas associações de policy.

  # module.access.aws_eks_access_entry.this["deploy-loja"] will be created
  # module.access.aws_eks_access_entry.this["plataforma"] will be created
  # module.access.aws_eks_access_entry.this["time-loja"] will be created
  # module.access.aws_eks_access_policy_association.this["plataforma/admin"] will be created
  # module.access.aws_eks_access_policy_association.this["time-loja/edit"] will be created
  ...
Plan: 33 to add, 0 to change, 0 to destroy.

Outputs do Terraform

O ambiente tem três outputs. Como não houve apply, os valores abaixo vêm do bloco Changes to Outputs do plan; os ARNs das access entries só existem depois do apply:

Changes to Outputs:
  + access_entry_arns  = {
      + deploy-loja = (known after apply)
      + plataforma  = (known after apply)
      + time-loja   = (known after apply)
    }
  + cluster_name       = "eks-blog-castilho"
  + kubeconfig_command = "aws eks update-kubeconfig --region us-east-1 --name eks-blog-castilho"
  • cluster_name: nome do cluster EKS, para usar nos comandos aws eks.
  • access_entry_arns: o ARN de cada access entry, por perfil. Serve para auditoria e para referenciar a entry em outras automações. Veja com terraform output access_entry_arns.
  • kubeconfig_command: o comando pronto para configurar o kubectl. Rode com $(terraform output -raw kubeconfig_command).

Conferindo o acesso

Depois do apply, estes comandos mostram o que cada identidade recebeu:

aws eks list-access-entries --cluster-name eks-blog-castilho

aws eks list-associated-access-policies \
  --cluster-name eks-blog-castilho \
  --principal-arn arn:aws:iam::111122223333:role/time-loja

Para testar de verdade, assuma a role do time da loja, gere o kubeconfig e pergunte ao cluster o que ela pode fazer:

kubectl auth can-i create deployments -n loja      # esperado: yes
kubectl auth can-i create deployments -n default   # esperado: no

O custo extra deste artigo é zero: access entries e access policies não são cobradas. O cluster, o NAT Gateway e os nós custam o mesmo do primeiro artigo, então destrua o lab com terraform destroy ao terminar.

Conclusão

Com EKS access entries, o acesso ao cluster sai do YAML frágil do aws-auth e vira um recurso da API do EKS, versionado no Terraform como qualquer outro. Access policies resolvem os papéis comuns, inclusive restritos a um namespace, e grupos do Kubernetes com RBAC cobrem os casos sob medida.

Este artigo cuidou de quem acessa o cluster. O próximo da série trata do outro lado: como os pods, rodando dentro do cluster, ganham permissão para acessar serviços da AWS.

Referências oficiais

Interessado em saber mais sobre esse assunto?

Deixe uma resposta

Descubra mais sobre Blog do Castilho - Tecnologia | FinOps | DevOps | Cloud

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo