Accede a los Secrets almacenados fuera de los clústeres de GKE mediante bibliotecas cliente


En este instructivo, se muestra cómo almacenar los datos sensibles que usan tus clústeres de Google Kubernetes Engine (GKE) en Secret Manager y acceder a los datos desde tus Pods de forma más segura mediante la federación de identidades para cargas de trabajo para GKE y las bibliotecas cliente de Google Cloud. Este instructivo está dirigido a los administradores de seguridad que desean mover datos sensibles fuera del almacenamiento dentro del clúster.

Almacenar los datos sensibles fuera del almacenamiento de tu clúster reduce el riesgo de acceso no autorizado a los datos si se produce un ataque. Usar la federación de identidades para cargas de trabajo para GKE para acceder a los datos te permite evitar los riesgos asociados con la administración de claves de cuentas de servicio de larga duración y te permite controlar el acceso a tus Secrets mediante Identity and Access Management (IAM) en lugar de reglas de RBAC en el clúster. Puedes usar cualquier proveedor de almacén de Secrets externo, como Secret Manager o HashiCorp Vault.

En este instructivo, se usa un clúster de GKE Autopilot. Para realizar los pasos con GKE Standard, debes habilitar la federación de identidades para cargas de trabajo para GKE de forma manual.

Puedes usar la federación de identidades para cargas de trabajo para GKE a fin de acceder a cualquier API de Google Cloud desde cargas de trabajo de GKE sin tener que usar enfoques menos seguros, como los archivos de claves de cuentas de servicio estáticos. En este instructivo, se usa Secret Manager como ejemplo, pero puedes seguir los mismos pasos para acceder a otras APIs de Google Cloud. Si deseas obtener más información, consulta Federación de identidades para cargas de trabajo para GKE.

Objetivos

  • Crear un Secret en Google Cloud Secret Manager.
  • Crear y configurar cuentas de servicio de IAM para acceder al Secret.
  • Crear un clúster de Autopilot de GKE, espacios de nombres de Kubernetes y cuentas de servicio de Kubernetes.
  • Usar aplicaciones de prueba para verificar el acceso de la cuenta de servicio.
  • Ejecutar una app de muestra que acceda al Secret con la API de Secret Manager.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. Instala Google Cloud CLI.
  3. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  4. Crea o selecciona un proyecto de Google Cloud.

    • Crea un proyecto de Google Cloud:

      gcloud projects create PROJECT_ID

      Reemplaza PROJECT_ID por un nombre para el proyecto de Google Cloud que estás creando.

    • Selecciona el proyecto de Google Cloud que creaste:

      gcloud config set project PROJECT_ID

      Reemplaza PROJECT_ID por el nombre del proyecto de Google Cloud.

  5. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  6. Habilita las APIs de Kubernetes Engine and Secret Manager:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  7. Instala Google Cloud CLI.
  8. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  9. Crea o selecciona un proyecto de Google Cloud.

    • Crea un proyecto de Google Cloud:

      gcloud projects create PROJECT_ID

      Reemplaza PROJECT_ID por un nombre para el proyecto de Google Cloud que estás creando.

    • Selecciona el proyecto de Google Cloud que creaste:

      gcloud config set project PROJECT_ID

      Reemplaza PROJECT_ID por el nombre del proyecto de Google Cloud.

  10. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  11. Habilita las APIs de Kubernetes Engine and Secret Manager:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  12. Otorga roles a tu Cuenta de Google. Ejecuta el siguiente comando una vez para cada uno de los siguientes roles de IAM: roles/secretmanager.admin, roles/container.clusterAdmin, roles/iam.serviceAccountAdmin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:EMAIL_ADDRESS" --role=ROLE
    • Reemplaza PROJECT_ID con el ID del proyecto.
    • Reemplaza EMAIL_ADDRESS por tu dirección de correo electrónico.
    • Reemplaza ROLE por cada rol individual.

Prepare el entorno

Clona el repositorio de GitHub que contiene los archivos de muestra para este instructivo:

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

Crea un Secret en Secret Manager

  1. En el siguiente ejemplo, se muestran los datos que usarás para crear un Secret:

    key=my-api-key
  2. Crea un Secret para almacenar los datos de muestra:

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

    Este comando realiza las siguientes acciones:

    • Crea un nuevo Secret de Secret Manager con la clave de muestra en la región us-central1 de Google Cloud.
    • Configura el Secret para que venza una hora después de ejecutar el comando.

Configura cuentas de servicio de IAM

  1. Crea dos cuentas de servicio de IAM nuevas para el acceso de solo lectura y el acceso de lectura y escritura:

    gcloud iam service-accounts create readonly-secrets --display-name="Read secrets"
    gcloud iam service-accounts create readwrite-secrets --display-name="Read write secrets"
    
  2. Otorga a la cuenta de servicio de IAM readonly-secrets acceso de solo lectura al Secret:

    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=serviceAccount:readonly-secrets@PROJECT_ID.iam.gserviceaccount.com \
        --role='roles/secretmanager.secretAccessor'
    
  3. Otorga a las cuentas de servicio de IAM readwrite-secrets acceso de lectura y escritura al 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'
    

Crea el clúster y los recursos de Kubernetes

Crea un clúster de GKE, espacios de nombres de Kubernetes y cuentas de servicio de Kubernetes. Deberás crear dos espacios de nombres, uno para el acceso de solo lectura y otro para el acceso de lectura y escritura al Secret. También deberás crear una cuenta de servicio de Kubernetes en cada espacio de nombres a fin de usarla con la federación de identidades para cargas de trabajo para GKE.

  1. Crea un clúster de GKE Autopilot:

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

    La implementación del clúster puede tomar unos cinco minutos. Los clústeres de Autopilot siempre tienen habilitada la federación de identidades para cargas de trabajo para GKE. Si, en cambio, deseas usar un clúster de GKE Standard, debes habilitar de forma manual la federación de identidades para cargas de trabajo para GKE antes de continuar.

  2. Crea un espacio de nombres readonly-ns y un espacio de nombres admin-ns:

    kubectl create namespace readonly-ns
    kubectl create namespace admin-ns
    
  3. Crea una cuenta de servicio de Kubernetes readonly-sa y una cuenta de servicio de Kubernetes admin-sa:

    kubectl create serviceaccount readonly-sa --namespace=readonly-ns
    kubectl create serviceaccount admin-sa --namespace=admin-ns
    
  4. Vincula las cuentas de servicio de IAM a las cuentas de servicio de 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. Anota las cuentas de servicio de Kubernetes con los nombres de las cuentas de servicio de 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
    

Ahora tienes un clúster configurado a fin de acceder al Secret desde los Pods mediante la federación de identidades para cargas de trabajo para GKE.

Verifica el acceso al Secret

Implementa Pods de prueba en cada espacio de nombres para verificar el acceso de solo lectura y el de lectura y escritura.

  1. Revisa el manifiesto del Pod de solo lectura:

    # 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 la cuenta de servicio readonly-sa en el espacio de nombres readonly-ns.

  2. Revisa el manifiesto del Pod de lectura y escritura:

    # 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 la cuenta de servicio admin-sa en el espacio de nombres admin-ns.

  3. Implementa los Pods de prueba:

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

    Los Pods pueden tardar unos minutos en comenzar a ejecutarse. Para supervisar el progreso, ejecuta el siguiente comando:

    watch kubectl get pods -n readonly-ns
    

    Cuando el estado del Pod cambie a RUNNING, presiona Ctrl+C para volver a la línea de comandos.

Prueba el acceso de solo lectura

  1. Abre una shell en el Pod readonly-test:

    kubectl exec -it readonly-test --namespace=readonly-ns -- /bin/bash
    
  2. Intenta leer el Secret:

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

    El resultado es key=my-api-key.

  3. Intenta escribir datos nuevos en el Secret:

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

    El resultado es similar a este:

    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).
    

    El pod que usa la cuenta de servicio de solo lectura solo puede leer el Secret y no puede escribir datos nuevos.

  4. Sal del pod:

    exit
    

Prueba el acceso de lectura y escritura

  1. Abre una shell en el Pod admin-test:

    kubectl exec -it admin-test --namespace=admin-ns -- /bin/bash
    
  2. Intenta leer el Secret:

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

    El resultado es key=my-api-key.

  3. Intenta escribir datos nuevos en el Secret:

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

    El resultado es similar a este:

    Created version [2] of the secret [bq-readonly-key].
    
  4. Lee la versión nueva del Secret:

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

    El resultado es my-second-api-key.

  5. Sal del pod:

    exit
    

Los pods solo obtienen el nivel de acceso que otorgaste a la cuenta de servicio de IAM vinculada a la cuenta de servicio de Kubernetes que se usó en el manifiesto del pod. Cualquier pod que use la cuenta de Kubernetes admin-sa en el espacio de nombres admin-ns puede escribir versiones nuevas del Secret, pero cualquier pod en el espacio de nombres readonly-ns que use la cuenta de servicio de Kubernetes readonly-sa solo puede leer el Secret.

Accede a los Secrets desde tu código

En esta sección, harás lo siguiente:

  1. Implementa una aplicación de muestra que lea tu Secret en Secret Manager mediante bibliotecas cliente.

  2. Verifica que la aplicación pueda acceder a tu Secret.

Debes acceder a los Secrets de Secret Manager desde el código de tu aplicación siempre que sea posible mediante la API de Secret Manager.

  1. Revisa el código fuente de la aplicación de muestra:

    // 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 aplicación llama a la API de Secret Manager para intentar leer el Secret.

  2. Revisa el manifiesto del Pod de la aplicación de muestra:

    # 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 manifiesto hace lo siguiente:

    • Crea un Pod en el espacio de nombres readonly-ns que usa la cuenta de servicio readonly-sa.
    • Extrae una aplicación de muestra de un registro de imágenes de Google. Esta aplicación llama a la API de Secret Manager mediante las bibliotecas cliente de Google Cloud. Puedes ver el código de la aplicación en /main.go en el repositorio.
    • Configura las variables de entorno para que use la aplicación de muestra.
  3. Reemplaza las variables de entorno en la aplicación de muestra:

    sed -i "s/YOUR_PROJECT_ID/PROJECT_ID/g" "manifests/secret-app.yaml"
    
  4. Implementa la app de muestra:

    kubectl apply -f manifests/secret-app.yaml
    

    Es posible que el Pod tarde unos minutos en comenzar a trabajar. Si el Pod necesita un nodo nuevo en tu clúster, es posible que notes eventos de tipo CrashLoopBackOff mientras a través de GKE se proporciona el nodo. Las fallas se detienen cuando el nodo aprovisiona correctamente.

  5. Verifica el acceso al Secret:

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

    El resultado es my-second-api-key. Si el resultado está en blanco, es posible que el Pod aún no se esté ejecutando. Espere unos minutos y vuelva a intentarlo.

Enfoques alternativos

Si necesitas activar tus datos sensibles en tus Pods, usa el complemento de Secret Manager para GKE (Vista previa). Este complemento implementa y administra el proveedor de Google Cloud Secret Manager para el controlador de CSI de Kubernetes Secret Store en tus clústeres de GKE. Para obtener instrucciones, consulta Usa el complemento de Secret Manager con GKE.

Proporcionar Secrets como volúmenes activados tiene los siguientes riesgos:

  1. Los volúmenes activados son susceptibles a los ataques de recorrido del directorio.
  2. Las variables de entorno pueden verse comprometidas debido a configuraciones incorrectas, como cuando se abre un extremo de depuración.

Siempre que sea posible, te recomendamos que accedas a los Secrets de manera programática a través de la API de Secret Manager. Para obtener instrucciones, usa la aplicación de muestra de este instructivo o consulta Bibliotecas cliente de Secret Manager.

Realiza una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

Borra los recursos individuales

  1. Borra el clúster:

    gcloud container clusters delete secret-cluster \
        --region=us-central1
    
  2. Borra las cuentas de servicio de 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. Borra el Secret en Secret Manager.

    gcloud secrets delete bq-readonly-key
    

    Si no realizas este paso, el Secret vencerá automáticamente porque configuraste la marca --ttl durante su creación.

Borra el proyecto

    Borra un proyecto de Google Cloud:

    gcloud projects delete PROJECT_ID

¿Qué sigue?