Aceda a segredos armazenados fora dos clusters do GKE através de bibliotecas de cliente

Este tutorial mostra como armazenar os dados confidenciais usados pelos seus clusters do Google Kubernetes Engine (GKE) no Secret Manager. Saiba como aceder aos dados dos seus pods de forma mais segura através da Workload Identity Federation para o GKE e dasGoogle Cloud bibliotecas de cliente.

Armazenar os seus dados confidenciais fora do armazenamento do cluster reduz o risco de acesso não autorizado aos dados se ocorrer um ataque. A utilização da Workload Identity Federation para o GKE para aceder aos dados permite-lhe evitar os riscos associados à gestão de chaves de contas de serviço de longa duração e controlar o acesso aos seus segredos através da gestão de identidade e acesso (IAM) em vez de regras RBAC no cluster. Pode usar qualquer fornecedor de armazenamento secreto externo, como o Secret Manager ou o HashiCorp Vault.

Esta página destina-se a especialistas em segurança que pretendam mover dados confidenciais para fora do armazenamento no cluster. Para saber mais acerca das funções comuns e das tarefas de exemplo que referimos no Google Cloud conteúdo, consulte o artigo Funções e tarefas comuns de utilizadores do GKE.

Este tutorial usa um cluster do GKE Autopilot. Para realizar os passos com o GKE Standard, tem de ativar manualmente a Workload Identity Federation para o GKE.

Pode usar a federação de identidades da carga de trabalho para o GKE para aceder a quaisquer Google Cloud APIs a partir de cargas de trabalho do GKE sem ter de usar abordagens menos seguras, como ficheiros de chaves de contas de serviço estáticos. Este tutorial usa o Secret Manager como exemplo, mas pode usar os mesmos passos para aceder a outras Google Cloud APIs. Para saber mais, consulte o artigo Federação de identidades de cargas de trabalho para o GKE.

Prepare o ambiente

Clone o repositório do GitHub que contém os ficheiros de exemplo para este tutorial:

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

Crie um Secret no Secret Manager

  1. O exemplo seguinte mostra os dados que vai usar para criar um segredo:

    key=my-api-key
  2. Crie um segredo 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:

    • Cria um novo Secret do Secret Manager com a chave de exemplo na região us-central1 Google Cloud .
    • Define o segredo para expirar uma hora após a execução do comando.

Crie o cluster e os recursos do Kubernetes

Crie um cluster do GKE, espaços de nomes do Kubernetes e contas de serviço do Kubernetes. Cria dois espaços de nomes, um para acesso só de leitura e outro para acesso de leitura/escrita ao segredo. Também cria uma conta de serviço do Kubernetes em cada espaço de nomes para usar com a Workload Identity Federation para o GKE.

  1. Crie um cluster do GKE Autopilot:

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

    A implementação do cluster pode demorar cerca de cinco minutos. Os clusters do Autopilot têm sempre a federação de identidade da carga de trabalho para o GKE ativada. Se quiser usar um cluster padrão do GKE, tem de ativar manualmente a Workload Identity Federation para o GKE antes de continuar.

  2. Crie um espaço de nomes readonly-ns e um espaço de nomes admin-ns:

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

    kubectl create serviceaccount readonly-sa --namespace=readonly-ns
    kubectl create serviceaccount admin-sa --namespace=admin-ns
    

Crie políticas de autorização da IAM

  1. Conceda à conta de serviço readonly-sa acesso só de leitura ao segredo:

    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 o seguinte:

    • PROJECT_NUMBER: o número Google Cloud do projeto.
    • PROJECT_ID: o ID do seu Google Cloud projeto.
  2. Conceda à conta de serviço admin-sa acesso de leitura/escrita ao segredo:

    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
    

Valide o acesso secreto

Implemente pods de teste em cada espaço de nomes para validar o acesso de leitura e leitura/escrita.

  1. Reveja o manifesto do agrupamento que publica só de 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

    Este pod usa a conta de serviço readonly-sa no espaço de nomes readonly-ns.

  2. Reveja o manifesto do agrupamento de leitura/escrita:

    # 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

    Este pod usa a conta de serviço admin-sa no espaço de nomes admin-ns.

  3. Implemente os pods de teste:

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

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

    watch kubectl get pods -n readonly-ns
    

    Quando o estado do pod mudar para RUNNING, prima Ctrl+C para regressar à linha de comandos.

Teste o acesso só de leitura

  1. Abra uma shell no readonly-test pod:

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

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

    O resultado é key=my-api-key.

  3. Tente escrever novos dados no segredo:

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

    O resultado é semelhante ao seguinte:

    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 só de leitura só pode ler o segredo e não pode escrever novos dados.

  4. Saia do Pod:

    exit
    

Teste o acesso de leitura/escrita

  1. Abra uma shell no admin-test pod:

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

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

    O resultado é key=my-api-key.

  3. Tente escrever novos dados no segredo:

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

    O resultado é semelhante ao seguinte:

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

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

    O resultado é my-second-api-key.

  5. Saia do Pod:

    exit
    

Os pods só recebem o nível de acesso que concedeu à conta de serviço do Kubernetes usada no manifesto do pod. Todos os pods que usam a conta do Kubernetes no espaço de nomes admin-ns podem escrever novas versões do segredo, mas todos os pods no espaço de nomes readonly-ns que usam a conta de serviço do Kubernetes readonly-sa só podem ler o segredo.admin-sa

Aceda a segredos a partir do seu código

Nesta secção, faz o seguinte:

  1. Implemente uma aplicação de exemplo que leia o seu segredo no Secret Manager através de bibliotecas de cliente.

  2. Verifique se a aplicação consegue aceder ao seu segredo.

Sempre que possível, deve aceder aos Secrets do Secret Manager a partir do código da sua aplicação, usando a API Secret Manager.

  1. Reveja o código-fonte da aplicação de exemplo:

    // 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)
    }
    

    Esta aplicação chama a API Secret Manager para tentar ler o segredo.

  2. Reveja o manifesto do agrupamento da aplicação de exemplo:

    # 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

    Este manifesto faz o seguinte:

    • Cria um pod no espaço de nomes readonly-ns que usa a conta de serviço readonly-sa.
    • Extrai uma aplicação de amostra de um registo de imagens da Google. Esta aplicação chama a API Secret Manager através dasGoogle Cloud bibliotecas cliente. Pode ver o código da aplicação em /main.go no repositório.
    • Define as variáveis de ambiente que a aplicação de exemplo vai usar.
  3. Substitua as variáveis de ambiente na aplicação de exemplo:

    sed -i "s/YOUR_PROJECT_ID/PROJECT_ID/g" "manifests/secret-app.yaml"
    
  4. Implemente a app de exemplo:

    kubectl apply -f manifests/secret-app.yaml
    

    O Pod pode demorar alguns minutos a começar a funcionar. Se o pod precisar de um novo nó no cluster, pode reparar em eventos do tipo CrashLoopBackOff enquanto o GKE aprovisiona o nó. As falhas de sistema param quando o nó é aprovisionado com êxito.

  5. Valide o acesso secreto:

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

    O resultado é my-second-api-key. Se a saída estiver em branco, o pod pode ainda não estar em execução. Aguarde alguns minutos e tente novamente.

Abordagens alternativas

Se precisar de montar os seus dados confidenciais nos seus pods, use o suplemento Secret Manager para o GKE. Este suplemento implementa e gere o fornecedor do Google Cloud Secret Manager para o controlador CSI da Secret Store nos seus clusters do GKE. Para obter instruções, consulte o artigo Use o suplemento Secret Manager com o GKE.

Fornecer segredos 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 a abertura de um ponto final de depuração.

Sempre que possível, recomendamos que aceda aos segredos de forma programática através da API Secret Manager. Para ver instruções, use a aplicação de exemplo neste tutorial ou consulte as bibliotecas de cliente do Secret Manager.