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.
  • Criar um cluster do GKE Autopilot, namespaces do Kubernetes e contas de serviço do Kubernetes.
  • Crie políticas de permissão do IAM para conceder acesso às contas de serviço do Kubernetes no secret.
  • 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. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Enable the Kubernetes Engine and Secret Manager APIs:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  7. Install the Google Cloud CLI.
  8. To initialize the gcloud CLI, run the following command:

    gcloud init
  9. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  10. Make sure that billing is enabled for your Google Cloud project.

  11. Enable the Kubernetes Engine and Secret Manager APIs:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  12. Grant roles to your user account. Run the following command once for each of the following IAM roles: roles/secretmanager.admin, roles/container.clusterAdmin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:myemail@example.com.

    • Replace ROLE with each individual role.

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.

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
    

Criar políticas de permissão do IAM

  1. Conceda à conta de serviço do readonly-sa acesso somente leitura para o secret:

    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/readonly-ns/sa/readonly-sa \
        --role='roles/secretmanager.secretAccessor' \
        --condition=None
    

    Substitua:

    • PROJECT_NUMBER: o número numérico do projeto do Google Cloud.
    • PROJECT_ID: é seu ID do projeto no Google Cloud.
  2. Conceda à conta de serviço do admin-sa acesso de leitura e gravação ao secret:

    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/admin-ns/sa/admin-sa \
        --role='roles/secretmanager.secretAccessor' \
        --condition=None
    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/admin-ns/sa/admin-sa \
        --role='roles/secretmanager.secretVersionAdder' \
        --condition=None
    

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 recebem apenas o nível de acesso concedido à conta de serviço do Kubernetes usada no manifesto do pod. Qualquer pod que use a conta do Kubernetes admin-sa no namespace admin-nspode 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. 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

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

A seguir