Workload Identity GKE - Service account k8s com permissões na GCP
Tags: kubernetes, cloud, gcp, security
Published: 2023-06-27 21:20:25

O Workload Identity é um recurso do GKE (Google Kubernetes Engine) que nos permite linkar uma service account do Kubernetes com uma service account da GCP, fazendo assim, com que os Pods que utilizem esta service account, possam fazer ações permitidas na service account da GCP.

Por exemplo, temos um microserviço que faz operações em um Bucket. Então podemos criar uma service account na GCP com permissões para fazer ações em buckets, criar uma service account no Kubernetes, linkar as duas através do Workload Identity e a aplicação será automaticamente autenticada como a service account da GCP que definirmos.


![fluxo do usuario utilizando Workload Identity na GCP](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkr5hu052fot3zh7awsj.png)


## Habilitando Workload Identity

Antes de começarmos a utilizar, precisamos habilitar esta funcionalidade no cluster:

![habilitando workload identity no control plane](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zacax8atadh86j124gl.png)

Depois, precisamos habilitar o GKE Metadata Server nos node pools que possuem os worker nodes que queremos alocar os pods que irão usar a funcionalidade de Workload Identity.

![habilitando gke metadata server nos worker nodes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mukfq7c6hfw1urf7wrhv.png)


## Criando service account's

Criamos uma service account na GCP, que terá as permissões que o nosso Pod irá utilizar. Então criamos a service account do Kubernetes que será linkada com esta criada na GCP:

```
kubectl create sa -n <namespace> workload-identity-test
```

Então rodamos comandos com gcloud no terminal, primeiro conectamos ao project na GCP que a service account que criamos está, e depois adicionamos a role de workloadIdentityUser na service da GCP para conseguirmos linkar as duas:

```
gcloud config set project <nome_projeto>
gcloud iam service-accounts add-iam-policy-binding \
    <email_da_service_account_GCP> \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:production-261711.svc.id.goog[<namespace>/workload-identity-test]"
```

## Possíveis erros:

**Falta de permissão**: Precisa mudar o project default do gcloud para o que você está tentando mexer: `gcloud config set project <id_do_project>`

**gcloud crashed**: Rodar gcloud components update para atualizar as dependências.

## Linkar service accounts

Agora para linkar as duas service accounts, adicionamos uma annotation na do Kubernetes (k edit sa -n <namespace> workload-identity-test):

```
apiVersion: v1
kind: ServiceAccount
metadata:
  # Adiciona esta annotation
  annotations:
    iam.gke.io/gcp-service-account: <email_service_account_GCP>
  name: workload-identity-test
  namespace: <namespace>
```

Para pegar achar o email da service account criada na GCP, basta ir até a service account:


![mostrando email da service account na GCP](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iiccu06xmddnerwvp78p.png)


## Hands on

Criei um código de exemplo em NodeJS que é uma API com apenas uma rota que lista os buckets quando chamada. Para isto, eu uso a SDK da Google Cloud e a autenticação é esperada através de variáveis de ambiente.

O código está no meu Github:
https://github.com/ThiagoFelippi/workload-identity-example

Para subir no kubernetes, usei o private registry do Gitlab, e os seguintes manifestos:

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: workload-identity-example
  namespace: pocs
spec:
  selector:
    matchLabels:
      app: workload-identity-example
  template:
    metadata:
      labels:
        app: workload-identity-example
    spec:
      imagePullSecrets:
      - name: workload-identity-example
      containers:
      - name: workload-identity-example
        image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        imagePullPolicy: IfNotPresent
        envFrom:
          - configMapRef:
              name: workload-identity-example
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: workload-identity-example
  namespace: pocs
spec:
  type: ClusterIP
  selector:
    app: workload-identity-example
  ports:
    - port: 80
      targetPort: 3000
```

Ao subir, podemos rodar um port-forward para não precisar expor a poc através de um ingress: k port-forward svc/workload-identity-example -n pocs 3001:80

Ao acessar a rota /buckets, temos o seguinte resultado:


![mensagem de erro antes do workload identity](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bulfq62idm4xiplijkca.png)


E podemos ver os logs do container apontando que não estamos autenticados:


![logs do container mostrando erro de autenticação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/etz2lndx7843n7d68a1b.png)

Agora vamos colocar a service account que criamos para gerenciar o Deployment e ver qual o resultado. Para isto, a única coisa que muda no deployment é adicionarmos o campo serviceAccoutName dentro de spec:

```
...
spec:
  selector:
    matchLabels:
      app: workload-identity-example
  template:
    metadata:
      labels:
        app: workload-identity-example
    spec:
      serviceAccountName: workload-identity-test
...
```

E ao rodar a aplicação e entrar na rota /buckets de novo, conseguimos ver os buckets sendo listados:


![lista de buckets após aplicar workload identity](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwcp5w9apscxbs1ubz77.png)

## Conclusões

Desta forma, aumentamos a segurança das nossas aplicações, não precisando expor dados sensíveis com a aplicação. Ensinarei em outros posts como usar isto na prática, com Helm Charts, por exemplo, com Vault, ArgoCD, etc.

Caso tenha alguma dúvida, deixe um comentário ou me chame no Linkedin: https://www.linkedin.com/in/thiago-crespo-felippi/