Cómo proteger un sistema de detección de seguridad en el lugar de trabajo

1. Introducción

En este codelab, crearás una aplicación de demostración básica que ilustra cómo integrar varios servicios de Google Cloud para un servicio ubicado en un proyecto con restricciones de cumplimiento. Este proyecto usa las siguientes capacidades de seguridad:

Este codelab está dirigido a desarrolladores de todos los niveles, incluidos principiantes. Usarás la interfaz de línea de comandos en Google Cloud Shell y el código de Python. No es necesario que seas un experto en Python, pero una comprensión básica de cómo leer el código te ayudará a comprender los conceptos.

Nota: Esta es una prueba de concepto simplificada, no una aplicación de producción. Aplica precauciones adicionales, como el acceso externo autenticado y seguro a este servicio en una situación del mundo real.

2. Antes de comenzar

Configuración del proyecto

../shared/_project-setup.md

Iniciar Cloud Shell

Cloud Shell es un entorno de línea de comandos que se ejecuta en Google Cloud y que viene precargado con las herramientas necesarias.

  1. Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud:

404e4cce0f23e5c5.png

  1. Una vez que te conectes a Cloud Shell, ejecuta este comando para verificar tu autenticación en Cloud Shell:
gcloud auth list
  1. Ejecuta el siguiente comando para confirmar que tu proyecto esté configurado para usarse con gcloud:
gcloud config get project
  1. Confirma que el proyecto sea el esperado y, luego, ejecuta el siguiente comando para configurar el ID del proyecto:
export PROJECT_ID=$(gcloud config get project)

Permisos de IAM obligatorios

La cuenta que usas para este codelab debe tener los siguientes roles de IAM. Estos permisos son necesarios para crear los recursos de Google Cloud requeridos (proyectos, carpetas, clústeres de GKE, claves de KMS, cuentas de servicio, etcétera) y configurar la carga de trabajo de Assured Workloads.

A nivel de la organización:

  • roles/assuredworkloads.admin (Administrador de Assured Workloads): Para crear y administrar el recurso de Assured Workloads, lo que garantiza la configuración de cumplimiento.

A nivel de la cuenta de facturación (en tu cuenta de facturación):

  • roles/billing.accountUser (Usuario de cuentas de facturación): Para vincular la cuenta de facturación especificada.

Configura las variables del codelab

Para crear la infraestructura necesaria, debes proporcionar las siguientes variables de entorno:

# The ID of the Billing Account in the format (XXXXXX-XXXXXX-XXXXXX).
# This value will be used to attach in the projects created using Assured Workloads
export BILLING_ACCOUNT=

# The ID of a Google Cloud Platform organization
# Run `gcloud organizations list` to check all your available organizations
export GCP_ORGANIZATION=

# The numeric ID of a folder where the Assured Workloads will create the resources.
export FOLDER_ID=

# Region where the application will be deployed.
# Since you are using Assured Workloads, you MUST use one of the valid locations as described here: <https://docs.cloud.google.com/assured-workloads/docs/locations>
export REGION="us-central1"

# The ID of an existing Google Cloud project to be used for API quota and billing purposes.
# This project will only be used to enable the Assured Workloads API and create an Assured Workload.
export QUOTA_PROJECT_ID=

# Random suffix used to avoid naming collisions when creating the GCP projects.
export RANDOM_SUFFIX=$(cat /dev/urandom | tr -dc 'a-z0-9' | head -c 5 ; echo)

# The ID of the projects that will be created using Assured Workloads.
# You can modify this value if you want a custom id.
export PROJECT_ID="il5-gemini-vision-aw-${RANDOM_SUFFIX}"
export KMS_PROJECT_ID="il5-gemini-vision-kms-${RANDOM_SUFFIX}"

3. Crea la base de Assured Workloads

Ahora puedes sentar las bases de tu aplicación en un entorno regulado. Usa Assured Workloads para ayudar a aplicar los requisitos de cumplimiento mediante la creación de un entorno controlado para tus recursos.

Configura tu proyecto de cuota

Habilita la API de Assured Workloads en tu proyecto de cuota. Esta API es necesaria para crear y administrar Assured Workloads.

gcloud services enable assuredworkloads.googleapis.com \
  --project="${QUOTA_PROJECT_ID}"

Crea el entorno de Assured Workloads

El siguiente comando crea una “zona de destino” segura para tu demostración. Crea dos proyectos nuevos de Google Cloud en la carpeta y la cuenta de facturación especificadas.

  • Un proyecto aloja tu clúster y aplicación de GKE.
  • El otro proyecto administra tus claves de encriptación administradas por el cliente (CMEK).

Aplica automáticamente los controles de cumplimiento de IL5 especificados a ambos proyectos desde el momento en que se crean.

Ejecuta lo siguiente para crear el entorno de carga de trabajo.

export ASSURED_WORKLOAD_ID=$(gcloud assured workloads create \
    --project="${QUOTA_PROJECT_ID}" \
    --display-name="DoD IL5 Gemini Vision Demo" \
    --compliance-regime="IL5" \
    --billing-account="billingAccounts/${BILLING_ACCOUNT}" \
    --location="${REGION}" \
    --organization="${GCP_ORGANIZATION}" \
    --provisioned-resources-parent="folders/${FOLDER_ID}" \
    --resource-settings="consumer-project-id=${PROJECT_ID},consumer-project-name=DoD IL5 Workloads,encryption-keys-project-id=${KMS_PROJECT_ID},encryption-keys-project-name=DoD IL5 KMS" \
    --labels="codelab=gemini-vision-demo" \
    --format="value(name)")

echo "Assured Workload created: ${ASSURED_WORKLOAD_ID}"

export WORKLOAD_FOLDER_ID=$(gcloud assured workloads describe ${ASSURED_WORKLOAD_ID} \
    --location="${REGION}" \
    --project="${QUOTA_PROJECT_ID}" \
    --format="json" | grep -B 1 "CONSUMER_FOLDER" | grep -oE "[0-9]{10,}")

echo "Assured Workload folder created: ${WORKLOAD_FOLDER_ID}"

gcloud projects create "${PROJECT_ID}" \
    --folder="${WORKLOAD_FOLDER_ID}" \
    --name="DoD IL5 Workloads"

gcloud billing projects link "${PROJECT_ID}" \
    --billing-account="${BILLING_ACCOUNT}"

export PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")

Habilita las APIs de Google Cloud requeridas

Antes de compilar, habilita las APIs requeridas para los servicios necesarios.

El siguiente comando activa todos los servicios necesarios para este codelab en tu proyecto de carga de trabajo principal.

gcloud services enable \
    aiplatform.googleapis.com \
    artifactregistry.googleapis.com \
    cloudkms.googleapis.com \
    compute.googleapis.com \
    container.googleapis.com \
    iam.googleapis.com \
    logging.googleapis.com \
    monitoring.googleapis.com \
    --project="${PROJECT_ID}"

4. Configuración de la infraestructura de GKE

Compila la infraestructura principal que admite tu clúster de GKE. Esto implica configurar una red dedicada para tu clúster y configurar tus propias claves de encriptación para proteger los datos de tus nodos.

Configura la red de VPC

Crea una nube privada virtual (VPC) y una subred personalizadas. Este enfoque te brinda control total sobre el rango de direcciones IP y garantiza que tu clúster esté aislado según lo previsto.

Ejecuta los siguientes comandos para crear la red de VPC y una subred dentro de la región especificada.

export GKE_NETWORK_NAME="il5-gke-network"
export GKE_SUBNETWORK_NAME="il5-gke-subnet"

gcloud compute networks create "${GKE_NETWORK_NAME}" \
    --description="VPC network for GKE cluster in DoD IL5 Assured Workload" \
    --subnet-mode="custom" \
    --project="${PROJECT_ID}"

gcloud compute networks subnets create "${GKE_SUBNETWORK_NAME}" \
    --network="${GKE_NETWORK_NAME}" \
    --range="10.10.0.0/20" \
    --region="${REGION}" \
    --description="Subnet for GKE cluster nodes in DoD IL5 Assured Workload" \
    --project="${PROJECT_ID}"

Configura la encriptación con Cloud KMS

Para cumplir con los estrictos requisitos de cumplimiento de los datos en reposo, usa claves de encriptación administradas por el cliente (CMEK). Esto te brinda control directo sobre las claves que se usan para encriptar los discos de arranque de tus nodos de GKE.

export KMS_KEYRING_NAME="il5_gke_key_ring"
export KMS_KEY_NAME="il5_gke_key"

gcloud kms keyrings create "${KMS_KEYRING_NAME}" \
    --location="$REGION" \
    --project="${KMS_PROJECT_ID}"

gcloud kms keys create "${KMS_KEY_NAME}" \
    --keyring="${KMS_KEYRING_NAME}" \
    --location="${REGION}" \
    --purpose="encryption" \
    --project="${KMS_PROJECT_ID}"

Debido a que la clave de encriptación existe en un proyecto de KMS independiente del servicio que necesita usarla, el agente de servicio de Google Compute Engine en el proyecto de carga de trabajo necesita acceso a la clave.

Debes otorgar explícitamente al agente de servicio de GCE del proyecto de carga de trabajo el permiso para usar esta clave. El siguiente comando agrega una vinculación de política de IAM a la clave, lo que otorga el rol necesario al agente de servicio.

gcloud kms keys add-iam-policy-binding "${KMS_KEY_NAME}" \
    --location="${REGION}" \
    --keyring="${KMS_KEYRING_NAME}" \
    --member="serviceAccount:service-${PROJECT_NUMBER}@compute-system.iam.gserviceaccount.com" \
    --role="roles/cloudkms.cryptoKeyEncrypterDecrypter" \
    --project="${KMS_PROJECT_ID}"

Configura una cuenta de servicio de nodo de GKE

export GKE_NODE_SA=gke-node-sa

gcloud iam service-accounts create "${GKE_NODE_SA}" \
    --display-name="GKE Node Service Account" \
    --project="${PROJECT_ID}"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/logging.logWriter"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/monitoring.metricWriter"

5. Crea y configura el clúster de GKE

Ahora puedes crear el clúster de GKE. El siguiente comando aprovisiona un clúster de GKE con varias funciones de seguridad habilitadas.

Esto tarda varios minutos en completarse, ya que Google Cloud aprovisiona los nodos y el plano de control.

export GKE_CLUSTER=ppe-app

gcloud beta container clusters create "${GKE_CLUSTER}" \
    --project="$PROJECT_ID" \
    --region="$REGION" \
    --service-account="${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --release-channel="regular" \
    --machine-type="n2d-standard-4" \
    --image-type="COS_CONTAINERD" \
    --disk-type="pd-ssd" \
    --disk-size="50" \
    --boot-disk-kms-key="projects/${KMS_PROJECT_ID}/locations/${REGION}/keyRings/${KMS_KEYRING_NAME}/cryptoKeys/${KMS_KEY_NAME}" \
    --metadata disable-legacy-endpoints=true \
    --num-nodes="1" \
    --network="projects/${PROJECT_ID}/global/networks/${GKE_NETWORK_NAME}" \
    --subnetwork="projects/${PROJECT_ID}/regions/${REGION}/subnetworks/${GKE_SUBNETWORK_NAME}" \
    --security-posture="standard" \
    --workload-vulnerability-scanning="disabled" \
    --workload-pool="${PROJECT_ID}.svc.id.goog" \
    --workload-metadata=GKE_METADATA \
    --addons="HorizontalPodAutoscaling,HttpLoadBalancing,NodeLocalDNS,GcePersistentDiskCsiDriver" \
    --max-surge-upgrade=1 \
    --max-unavailable-upgrade=0 \
    --binauthz-evaluation-mode="DISABLED" \
    --no-enable-basic-auth \
    --enable-autoupgrade \
    --enable-autorepair \
    --enable-confidential-nodes \
    --confidential-node-type=sev \
    --enable-ip-access \
    --enable-ip-alias \
    --enable-managed-prometheus \
    --enable-dns-access \
    --enable-shielded-nodes \
    --shielded-integrity-monitoring \
    --shielded-secure-boot

Conéctate al clúster nuevo

Para interactuar con tu clúster nuevo, configura tu herramienta de línea de comandos kubectl local.

Este comando recupera las credenciales y el extremo del clúster, y configura automáticamente tu archivo kubeconfig local. Después de ejecutarlo, cualquier comando kubectl que ejecutes se dirigirá a tu nuevo clúster de GKE.

gcloud container clusters get-credentials "${GKE_CLUSTER}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --dns-endpoint

6. Configuración de la identidad de la aplicación

Para conectar una cuenta de servicio de Kubernetes a una cuenta de servicio de Cloud IAM de Google Cloud, configura Workload Identity.

Crear cuentas de servicio

Crea una cuenta de servicio de Kubernetes (KSA) dedicada dentro de tu clúster.

export GKE_NAMESPACE=default
export GKE_SA=ppe-sa

kubectl create sa "${GKE_SA}" --namespace="${GKE_NAMESPACE}"

A continuación, tu aplicación necesita una identidad en Google Cloud. Crea una cuenta de servicio de Cloud IAM de Google Cloud para la aplicación. Después de crear la cuenta de servicio, otórgale los roles necesarios.

# Create GCP service account
gcloud iam service-accounts create "${GKE_SA}" \
    --project="${PROJECT_ID}"
# Grant necessary roles
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${GKE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/aiplatform.user"

Permite que la cuenta de servicio de Kubernetes use la identidad de la de IAM

Con ambas cuentas de servicio creadas, el último paso es crear el vínculo entre ellas. Este es un proceso de dos partes. Primero, agrega una política de IAM a tu cuenta de servicio de Google Cloud.

# Allow the Kubernetes service account to act as GCP service account by using Workload Identity
gcloud iam service-accounts add-iam-policy-binding "${GKE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --project="${PROJECT_ID}" \
    --role="roles/iam.workloadIdentityUser" \
    --member="serviceAccount:${PROJECT_ID}.svc.id.goog[${GKE_NAMESPACE}/${GKE_SA}]"

En segundo lugar, anota tu cuenta de servicio de Kubernetes.

kubectl annotate --namespace="${GKE_NAMESPACE}" serviceaccount "$GKE_SA" \
    iam.gke.io/gcp-service-account="${GKE_SA}@${PROJECT_ID}.iam.gserviceaccount.com"

Una vez que se complete, cualquier pod que se ejecute en tu clúster con la cuenta de servicio de Kubernetes ahora podrá acceder a la API de Vertex.

Nota: Es posible eliminar la identidad temporal como cuenta de servicio para simplificar esta configuración. Consulta aquí para obtener detalles y limitaciones.

7. Compila e implementa la aplicación

Es hora de empaquetar tu aplicación, almacenarla e implementarla en tu clúster de GKE.

Crea el repositorio de Artifact Registry

Antes de que puedas ejecutar la app, empaquétala como un contenedor de Docker y almacénala dentro de un repositorio de Artifact Registry. Usa el siguiente comando para crear este repositorio.

export REPOSITORY_ID=ppe-repo

gcloud artifacts repositories create "${REPOSITORY_ID}" \
  --repository-format=docker \
  --location="${REGION}" \
  --project="${PROJECT_ID}" \
  --description="Regional Docker repo for PPE App"

Debes otorgar explícitamente a la cuenta de servicio que usan los nodos de GKE la capacidad de leer desde tu repositorio nuevo.

gcloud artifacts repositories add-iam-policy-binding "${REPOSITORY_ID}" \
    --location="${REGION}" \
    --role="roles/artifactregistry.reader" \
    --project="${PROJECT_ID}" \
    --member="serviceAccount:${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com"

Compila y envía la imagen de Docker

Compila y envía la imagen de Docker al repositorio. Primero, clona el código fuente y compila la imagen del contenedor. Luego, etiquétala con su ruta de acceso completa de Artifact Registry y envíala al repositorio que creaste antes.

git clone https://github.com/GoogleCloudPlatform/next-26-sessions.git

cd BRK3-034-workplace-safety

cd ppe

export IMAGE_TAG="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_ID}/ppe-app:v15"
docker build -t "${IMAGE_TAG}" .
docker push "${IMAGE_TAG}"

Implementar en GKE

Con la imagen del contenedor disponible en Artifact Registry, el último paso es indicarle a GKE que la extraiga y la ejecute. Para ello, define los recursos de tu aplicación en un archivo de manifiesto de Kubernetes y aplícalo al clúster. Este comando crea los objetos de Deployment y Service definidos en el archivo.

export GKE_DEPLOY=ppe-detector

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${GKE_DEPLOY}
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ${GKE_DEPLOY}
  template:
    metadata:
      labels:
        app: ${GKE_DEPLOY}
    spec:
      serviceAccountName: ${GKE_SA}
      containers:
      - name: ${GKE_DEPLOY}
        image: ${IMAGE_TAG}
        env:
        - name: PROJECT_ID
          value: ${PROJECT_ID}
        - name: LOCATION
          value: ${REGION}
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "500m"
            memory: "1Gi"
---
apiVersion: v1
kind: Service
metadata:
  name: ${GKE_DEPLOY}
spec:
  type: LoadBalancer
  selector:
    app: ${GKE_DEPLOY}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
EOF

8. Prueba la aplicación

El último paso es acceder a la aplicación de demostración y probarla. Esto implica recuperar la dirección IP externa asignada al servicio e interactuar con ella a través de un frontend simple.

Recupera la IP externa del servicio

Obtén la IP externa del servicio expuesto (tarda aproximadamente 30 segundos en aprovisionarse).

export IP_ADDRESS=$(kubectl get service "${GKE_DEPLOY}" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $IP_ADDRESS

Ejecuta el frontend de forma local

Para esta demostración, el frontend es una página simple de HTML/JavaScript que no forma parte de la implementación de GKE. Está diseñado para ejecutarse en tu máquina local.

El frontend debe publicarse desde GCP en una aplicación de producción. En la máquina local:

# Update the index.html file with the server IP address
cd frontend

# For Linux
sed -i "s#\(const BACKEND_URL = \"http://\)[^/]\+\(\/analyze\";\)#\1${IP_ADDRESS}\2#g" "index.html"

# For MacOS
#sed -i '' "s#\(const BACKEND_URL = #\"http://\)[^/]*\(\/analyze\";\)#\1${IP_ADDRESS}\2#g" "index.html"

python3 -m http.server 8001

Abre http://localhost:8001/index.html en Chrome.

9. Limpieza

Para evitar cargos continuos, borra los recursos creados en este codelab.

Destruye el clúster de GKE

Para borrar toda la aplicación, solo debes borrar el clúster de GKE. Para ello, ejecuta lo siguiente:

gcloud container clusters delete "${GKE_CLUSTER}" \
    --region="$REGION" \
    --project="${PROJECT_ID}"

Borra las cargas de trabajo de Assured Workloads

Ejecuta los siguientes comandos para borrar todos los recursos relacionados con las cargas de trabajo de Assured Workloads.

# Workload project deletion
gcloud billing projects unlink "${PROJECT_ID}"
gcloud projects delete "${PROJECT_ID}"

# KMS project deletion
gcloud billing projects unlink "${KMS_PROJECT_ID}"
gcloud projects delete "${KMS_PROJECT_ID}"

# Assured Workload folder deletion
gcloud resource-manager folders delete ${WORKLOAD_FOLDER_ID} --quiet

# Assured Workload deletion
gcloud assured workloads delete "${ASSURED_WORKLOAD_ID}" \
    --location="${REGION}" \
    --organization="${GCP_ORGANIZATION}" \
    --project="${QUOTA_PROJECT_ID}"

10. Felicitaciones

¡Misión cumplida! Creaste correctamente un sistema de detección de seguridad en el lugar de trabajo para industrias reguladas que usa Gemini para detectar cascos de seguridad.

Logros:

  • Protección y privacidad de datos: Aprovisionaste un nodo confidencial de GKE con CMEK.
  • Límites de datos: Habilitaste los controles de cumplimiento de la plataforma para un entorno regulado.

Documentos de referencia