Acessar secrets armazenados fora dos clusters do GKE usando bibliotecas de cliente


Neste tutorial, mostramos como armazenar os dados sensíveis usados pelos clusters do Google Kubernetes Engine (GKE) no Secret Manager e acessar os dados dos pods com mais segurança usando a federação de identidade da carga de trabalho do GKE e bibliotecas de cliente do Google Cloud. Este tutorial é destinado a administradores de segurança que querem mover dados confidenciais do armazenamento no cluster.

Armazenar seus dados confidenciais fora do armazenamento do cluster reduz o risco de acesso não autorizado aos dados em caso de ataque. O uso da federação de identidade da carga de trabalho para acessar os dados evita os riscos associados ao gerenciamento de chaves de contas de serviço de longa duração e permite controlar o acesso aos secrets usando o Identity and Access Management (IAM) em vez das regras do RBAC no cluster. Use qualquer provedor de armazenamento de secrets externos, como Secret Manager ou HashiCorp Vault.

Este tutorial usa um cluster do Autopilot do GKE. Para executar as etapas usando o GKE Standard, é necessário ativar manualmente a federação de identidade da carga de trabalho para o GKE.

É possível usar a federação de identidade da carga de trabalho para o GKE para acessar qualquer API do Google Cloud a partir de cargas de trabalho do GKE sem precisar usar abordagens menos seguras, como arquivos de chave de conta de serviço estáticos. Neste tutorial, usamos o Secret Manager como exemplo, mas é possível usar as mesmas etapas para acessar outras APIs do Google Cloud. Para saber mais, consulte Federação de identidade da carga de trabalho para o GKE.

Objetivos

  • Crie um secret no Secret Manager do Google Cloud.
  • Crie e configure contas de serviço do IAM para acessar o secret.
  • Criar um cluster do GKE Autopilot, namespaces do Kubernetes e contas de serviço do Kubernetes.
  • Use aplicativos de teste para verificar o acesso à conta de serviço.
  • Execute um app de amostra que acesse a chave secreta usando a API Secret Manager.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Antes de começar

  1. Faça login na sua conta do Google Cloud. Se você começou a usar o Google Cloud agora, crie uma conta para avaliar o desempenho de nossos produtos em situações reais. Clientes novos também recebem US$ 300 em créditos para executar, testar e implantar cargas de trabalho.
  2. Instale a CLI do Google Cloud.
  3. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  4. Crie ou selecione um projeto do Google Cloud.

    • Crie um projeto do Google Cloud:

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto do Google Cloud que você está criando.

    • Selecione o projeto do Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud.

  5. Verifique se a cobrança está ativada para o seu projeto do Google Cloud.

  6. Ative as APIs Kubernetes Engine and Secret Manager:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  7. Instale a CLI do Google Cloud.
  8. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  9. Crie ou selecione um projeto do Google Cloud.

    • Crie um projeto do Google Cloud:

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto do Google Cloud que você está criando.

    • Selecione o projeto do Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud.

  10. Verifique se a cobrança está ativada para o seu projeto do Google Cloud.

  11. Ative as APIs Kubernetes Engine and Secret Manager:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  12. Atribua os papéis à sua Conta do Google. Execute uma vez o seguinte comando para cada um dos seguintes papéis do IAM: roles/secretmanager.admin, roles/container.clusterAdmin, roles/iam.serviceAccountAdmin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:EMAIL_ADDRESS" --role=ROLE
    • Substitua PROJECT_ID pela ID do seu projeto.
    • Substitua EMAIL_ADDRESS pelo seu endereço de e-mail.
    • Substitua ROLE por cada papel individual.

Prepare o ambiente

Clone o repositório do GitHub que contém os arquivos de amostra deste tutorial:

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
cd ~/kubernetes-engine-samples/security/wi-secrets

Criar um secret no Secret Manager

  1. No exemplo a seguir, mostramos os dados que você usará para criar um secret:

    key=my-api-key
  2. Crie um secret para armazenar os dados de amostra:

    gcloud secrets create bq-readonly-key \
        --data-file=manifests/bq-readonly-key \
        --ttl=3600s
    

    Este comando faz o seguinte:

    • Crie um novo secret do Secret Manager com a chave de amostra na região us-central1 do Google Cloud.
    • Define o secret para expirar uma hora depois da execução do comando.

Configurar contas de serviço do IAM

  1. Crie duas novas contas de serviço do IAM para acesso somente leitura e acesso de leitura e gravação:

    gcloud iam service-accounts create readonly-secrets --display-name="Read secrets"
    gcloud iam service-accounts create readwrite-secrets --display-name="Read write secrets"
    
  2. Conceda à conta de serviço do IAM readonly-secrets acesso somente leitura para o secret:

    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=serviceAccount:readonly-secrets@PROJECT_ID.iam.gserviceaccount.com \
        --role='roles/secretmanager.secretAccessor'
    
  3. Conceda às contas de serviço do IAM readwrite-secrets acesso de leitura e gravação ao secret:

    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=serviceAccount:readwrite-secrets@PROJECT_ID.iam.gserviceaccount.com \
        --role='roles/secretmanager.secretAccessor'
    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=serviceAccount:readwrite-secrets@PROJECT_ID.iam.gserviceaccount.com \
        --role='roles/secretmanager.secretVersionAdder'
    

Crie o cluster e os recursos do Kubernetes

Crie um cluster do GKE, namespaces do Kubernetes e contas de serviço do Kubernetes. Crie dois namespaces, um para acesso somente leitura e outro para acesso de leitura e gravação ao secret. Crie também uma conta de serviço do Kubernetes em cada namespace para usar com a federação de identidade da carga de trabalho do GKE.

  1. Crie um cluster do GKE Autopilot:

    gcloud container clusters create-auto secret-cluster \
        --region=us-central1
    

    A implantação do cluster pode levar cerca de cinco minutos. Os clusters do Autopilot sempre têm a federação de identidade da carga de trabalho ativada do GKE. Se você quiser usar um cluster do GKE Standard, ative manualmente a federação de identidade da carga de trabalho para o GKE antes de continuar.

  2. Crie um namespace readonly-ns e um namespace admin-ns:

    kubectl create namespace readonly-ns
    kubectl create namespace admin-ns
    
  3. Crie uma conta de serviço do Kubernetes readonly-sa e uma conta de serviço do Kubernetes admin-sa:

    kubectl create serviceaccount readonly-sa --namespace=readonly-ns
    kubectl create serviceaccount admin-sa --namespace=admin-ns
    
  4. Vincule as contas de serviço do IAM às contas de serviço do Kubernetes:

    gcloud iam service-accounts add-iam-policy-binding readonly-secrets@PROJECT_ID.iam.gserviceaccount.com \
        --member=serviceAccount:PROJECT_ID.svc.id.goog[readonly-ns/readonly-sa] \
        --role='roles/iam.workloadIdentityUser'
    gcloud iam service-accounts add-iam-policy-binding readwrite-secrets@PROJECT_ID.iam.gserviceaccount.com \
        --member=serviceAccount:PROJECT_ID.svc.id.goog[admin-ns/admin-sa] \
        --role='roles/iam.workloadIdentityUser'
    
  5. Anote as contas de serviço do Kubernetes com os nomes das contas de serviço do IAM vinculadas:

    kubectl annotate serviceaccount readonly-sa \
        --namespace=readonly-ns \
        iam.gke.io/gcp-service-account=readonly-secrets@PROJECT_ID.iam.gserviceaccount.com
    kubectl annotate serviceaccount admin-sa \
        --namespace=admin-ns \
        iam.gke.io/gcp-service-account=readwrite-secrets@PROJECT_ID.iam.gserviceaccount.com
    

Agora você tem um cluster configurado para acessar o secret dos pods usando a federação de identidade da carga de trabalho para o GKE.

Verificar acesso do secret

Implante pods de teste em cada namespace para verificar o acesso somente leitura e gravação.

  1. Revise o manifesto do pod somente leitura:

    # Copyright 2022 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: readonly-test
      namespace: readonly-ns
    spec:
      containers:
      - image: google/cloud-sdk:slim
        name: workload-identity-test
        command: ["sleep","infinity"]
        resources:
          requests:
            cpu: "150m"
            memory: "150Mi"
      serviceAccountName: readonly-sa

    Esse pod usa a conta de serviço readonly-sa no namespace readonly-ns.

  2. Revise o manifesto de pod de leitura/gravação:

    # Copyright 2022 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: admin-test
      namespace: admin-ns
    spec:
      containers:
      - image: google/cloud-sdk:slim
        name: workload-identity-test
        command: ["sleep","infinity"]
        resources:
          requests:
            cpu: "150m"
            memory: "150Mi"
      serviceAccountName: admin-sa

    Esse pod usa a conta de serviço admin-sa no namespace admin-ns.

  3. Implante os pods de teste:

    kubectl apply -f manifests/admin-pod.yaml
    kubectl apply -f manifests/readonly-pod.yaml
    

    Os pods podem levar alguns minutos para começar a ser executados. Para monitorar o progresso, execute o seguinte comando:

    watch kubectl get pods -n readonly-ns
    

    Quando o status do pod mudar para RUNNING, pressione Ctrl+C para retornar à linha de comando.

Testar o acesso somente leitura

  1. Abra um shell no pod readonly-test:

    kubectl exec -it readonly-test --namespace=readonly-ns -- /bin/bash
    
  2. Tente ler o secret:

    gcloud secrets versions access 1 --secret=bq-readonly-key
    

    A resposta é key=my-api-key.

  3. Tente gravar novos dados no secret:

    printf "my-second-api-key" | gcloud secrets versions add bq-readonly-key --data-file=-
    

    O resultado será assim:

    ERROR: (gcloud.secrets.versions.add) PERMISSION_DENIED: Permission 'secretmanager.versions.add' denied for resource 'projects/PROJECT_ID/secrets/bq-readonly-key' (or it may not exist).
    

    O pod que usa a conta de serviço somente leitura só pode ler o secret e não pode gravar novos dados.

  4. Saia do pod:

    exit
    

Testar o acesso de leitura/gravação

  1. Abra um shell no pod admin-test:

    kubectl exec -it admin-test --namespace=admin-ns -- /bin/bash
    
  2. Tente ler o secret:

    gcloud secrets versions access 1 --secret=bq-readonly-key
    

    A resposta é key=my-api-key.

  3. Tente gravar novos dados no secret:

    printf "my-second-api-key" | gcloud secrets versions add bq-readonly-key --data-file=-
    

    O resultado será assim:

    Created version [2] of the secret [bq-readonly-key].
    
  4. Leia a nova versão do secret:

    gcloud secrets versions access 2 --secret=bq-readonly-key
    

    A resposta é my-second-api-key.

  5. Saia do pod:

    exit
    

Os pods só recebem o nível de acesso que você concedeu à conta de serviço do IAM que está vinculada à conta de serviço do Kubernetes usada no manifesto do pod. Qualquer pod que use a conta do Kubernetes admin-sa no namespace admin-ns pode gravar novas versões do secret, mas qualquer pod no namespace readonly-ns que use a conta de serviço do Kubernetes readonly-sa só pode ler o secret.

Acessar secrets do seu código

Nesta seção, você realizará as ações a seguir:

  1. Implante um aplicativo de amostra que leia o secret no Secret Manager usando bibliotecas de cliente.

  2. Verifique se o app pode acessar o secret.

Acesse os secrets do Secret Manager do código do seu aplicativo sempre que possível, usando a API Secret Manager.

  1. Veja o exemplo de código-fonte do aplicativo:

    // Copyright 2022 Google LLC
    //
    // Licensed under the Apache License, Version 2.0 (the "License");
    // you may not use this file except in compliance with the License.
    // You may obtain a copy of the License at
    //
    //     http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS,
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    // See the License for the specific language governing permissions and
    // limitations under the License.
    
    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    	"os"
    
    	secretmanager "cloud.google.com/go/secretmanager/apiv1"
    	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
    )
    
    func main() {
    
            // Get environment variables from Pod spec.
            projectID := os.Getenv("PROJECT_ID")
            secretId := os.Getenv("SECRET_ID")
            secretVersion := os.Getenv("SECRET_VERSION")
    
            // Create the Secret Manager client.
            ctx := context.Background()
            client, err := secretmanager.NewClient(ctx)
            if err != nil {
                    log.Fatalf("failed to setup client: %v", err)
            }
            defer client.Close()
    
            // Create the request to access the secret.
            accessSecretReq := &secretmanagerpb.AccessSecretVersionRequest{
                    Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", projectID, secretId, secretVersion),
            }
    
            secret, err := client.AccessSecretVersion(ctx, accessSecretReq)
            if err != nil {
                    log.Fatalf("failed to access secret: %v", err)
            }
    
            // Print the secret payload.
            //
            // WARNING: Do not print the secret in a production environment - this
            // snippet is showing how to access the secret material.
            log.Printf("Welcome to the key store, here's your key:\nKey: %s", secret.Payload.Data)
    }
    

    Este aplicativo chama a API Secret Manager para tentar ler o secret.

  2. Revise o exemplo de manifesto do pod do aplicativo:

    # Copyright 2022 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: readonly-secret-test
      namespace: readonly-ns
    spec:
      containers:
      - image: us-docker.pkg.dev/google-samples/containers/gke/wi-secret-store:latest
        name: secret-app
        env:
          - name: PROJECT_ID
            value: "YOUR_PROJECT_ID"
          - name: SECRET_ID
            value: "bq-readonly-key"
          - name: SECRET_VERSION
            value: "latest"
        resources:
          requests:
            cpu: "125m"
            memory: "64Mi"
      serviceAccountName: readonly-sa

    Esse manifesto faz o seguinte:

    • Cria um pod no namespace readonly-ns que usa a conta de serviço readonly-sa.
    • Extrai um aplicativo de amostra de um registro de imagem do Google. Este aplicativo chama a API Secret Manager usando as bibliotecas de cliente do Google Cloud. É possível ver o código do aplicativo em /main.go no repositório.
    • Define as variáveis de ambiente que o aplicativo de amostra deve usar.
  3. Substitua as variáveis de ambiente no aplicativo de amostra:

    sed -i "s/YOUR_PROJECT_ID/PROJECT_ID/g" "manifests/secret-app.yaml"
    
  4. Implante o app de amostra:

    kubectl apply -f manifests/secret-app.yaml
    

    O pod pode levar alguns minutos para começar a funcionar. Se o pod precisar de um novo nó no cluster, talvez você veja eventos do tipo CrashLoopBackOff enquanto o GKE provisiona o nó. As falhas são interrompidas quando o nó é provisionado com sucesso.

  5. Verifique o acesso do secret:

    kubectl logs readonly-secret-test -n readonly-ns
    

    A resposta é my-second-api-key. Se a saída estiver em branco, é possível que o pod ainda não esteja em execução. Aguarde alguns minutos e tente novamente.

Abordagens alternativas

Se for necessário ativar os dados sensíveis nos pods, use o complemento do Secret Manager para o GKE (pré-lançamento). Esse complemento implanta e gerencia o provedor do Google Cloud Secret Manager para o driver CSI do Kubernetes Secret Store nos clusters do GKE. Para instruções, consulte Usar o complemento do Secret Manager com o GKE.

Fornecer secrets como volumes montados tem os seguintes riscos:

  1. Os volumes montados são suscetíveis a ataques de travessia de diretórios.
  2. As variáveis de ambiente podem ser comprometidas devido a configurações incorretas, como abrir um endpoint de depuração.

Sempre que possível, recomendamos que você acesse os secrets programaticamente por meio da API Secret Manager. Para instruções, use o aplicativo de amostra neste tutorial ou consulte as bibliotecas de cliente do Secret Manager.

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

Excluir recursos individuais

  1. Exclua o cluster:

    gcloud container clusters delete secret-cluster \
        --region=us-central1
    
  2. Exclua as contas de serviço do IAM:

    gcloud iam service-accounts delete readonly-secrets@PROJECT_ID.iam.gserviceaccount.com
    gcloud iam service-accounts delete readwrite-secrets@PROJECT_ID.iam.gserviceaccount.com
    
  3. Opcional: exclua o secret no Secret Manager:

    gcloud secrets delete bq-readonly-key
    

    Se você não fizer isso, o secret vai expirar automaticamente porque você definiu a sinalização --ttl durante a criação.

Excluir o projeto

    Exclua um projeto do Google Cloud:

    gcloud projects delete PROJECT_ID

A seguir