Compilación segura y Implementar con Cloud Build, Artifact Registry y GKE

1. Introducción

Container Analysis proporciona análisis de vulnerabilidades y almacenamiento de metadatos para contenedores. El servicio de análisis realiza análisis de vulnerabilidades en imágenes de Artifact Registry y Container Registry. Luego, almacena los metadatos resultantes y los pone a disposición para su consumo a través de una API. El almacenamiento de metadatos te permite almacenar información de diferentes fuentes, incluidos el análisis de vulnerabilidades, los servicios de Google Cloud y los proveedores externos.

El análisis de vulnerabilidades puede realizarse automáticamente o on demand:

  • Cuando el análisis automático está habilitado, el análisis se activa automáticamente cada vez que envías una imagen nueva a Artifact Registry o Container Registry. La información sobre vulnerabilidades se actualiza de forma continua cuando se descubren nuevas vulnerabilidades.
  • Cuando el análisis on demand está habilitado, debes ejecutar un comando para analizar una imagen local o una imagen en Artifact Registry o Container Registry. El análisis a pedido te brinda flexibilidad en cuanto al momento en que analizas los contenedores. Por ejemplo, puedes analizar una imagen compilada de forma local y solucionar las vulnerabilidades antes de almacenarla en un registro. Los resultados del análisis están disponibles hasta 48 horas después de que se completa el análisis, y la información de vulnerabilidad no se actualiza después de este.

Con Container Analysis integrado en tu canalización de CI/CD, puedes tomar decisiones basadas en esos metadatos. Por ejemplo, puedes usar la Autorización binaria para crear políticas de implementación que solo permitan implementaciones de imágenes que cumplan con los requisitos de registros de confianza.

Qué aprenderás

  • Cómo habilitar el análisis automático
  • Cómo realizar un análisis On-Demand Scanning
  • Cómo integrar el análisis en una canalización de compilación
  • Cómo firmar imágenes aprobadas
  • Cómo usar los controladores de admisión de GKE para bloquear imágenes
  • Cómo configurar GKE para permitir solo imágenes aprobadas firmadas

2. Configuración y requisitos

Cómo configurar el entorno a tu propio ritmo

  1. Accede a Google Cloud Console y crea un proyecto nuevo o reutiliza uno existente. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • El Nombre del proyecto es el nombre visible de los participantes de este proyecto. Es una cadena de caracteres que no se utiliza en las APIs de Google. Puedes actualizarla en cualquier momento.
  • El ID del proyecto es único en todos los proyectos de Google Cloud y es inmutable (no se puede cambiar después de configurarlo). La consola de Cloud genera automáticamente una cadena única. Por lo general, no importa cuál sea. En la mayoría de los codelabs, deberás hacer referencia al ID del proyecto (suele identificarse como PROJECT_ID). Si no te gusta el ID que se generó, podrías generar otro aleatorio. También puedes probar uno propio y ver si está disponible. No se puede cambiar después de este paso y se mantendrá durante todo el proyecto.
  • Recuerda que hay un tercer valor, un número de proyecto, que usan algunas APIs. Obtén más información sobre estos tres valores en la documentación.
  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar las APIs o los recursos de Cloud. Ejecutar este codelab no debería costar mucho, tal vez nada. Para cerrar recursos y evitar que se generen cobros más allá de este instructivo, puedes borrar los recursos que creaste o borrar todo el proyecto. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de USD 300.

Inicia el editor de Cloudshell

Este lab se diseñó y probó para usarse con el editor de Google Cloud Shell. Para acceder al editor, haz lo siguiente:

  1. Accede a tu proyecto de Google en https://console.cloud.google.com.
  2. En la esquina superior derecha, haz clic en el ícono del editor de Cloud Shell.

8560cc8d45e8c112.png

  1. Se abrirá un panel nuevo en la parte inferior de la ventana.

Configuración del entorno

En Cloud Shell, establece el ID y el número de tu proyecto. Guárdalos como variables PROJECT_ID y PROJECT_ID.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

Habilita los servicios

Habilita todos los servicios necesarios con el siguiente comando:

gcloud services enable \
  cloudkms.googleapis.com \
  cloudbuild.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  artifactregistry.googleapis.com \
  containerscanning.googleapis.com \
  ondemandscanning.googleapis.com \
  binaryauthorization.googleapis.com 

Crea un repositorio de Artifact Registry

En este lab, usarás Artifact Registry para almacenar y analizar tus imágenes. Crea el repositorio con el siguiente comando.

gcloud artifacts repositories create artifact-scanning-repo \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository"

Configura Docker para que use tus credenciales de gcloud cuando accedas a Artifact Registry.

gcloud auth configure-docker us-central1-docker.pkg.dev

3. Análisis automatizado

El análisis de artefactos se activa automáticamente cada vez que envías una imagen nueva a Artifact Registry o Container Registry. La información sobre vulnerabilidades se actualiza de forma continua cuando se descubren nuevas vulnerabilidades. En esta sección, enviarás una imagen a Artifact Registry y explorarás los resultados.

Crea un directorio de trabajo y cámbialo

mkdir vuln-scan && cd vuln-scan

Define una imagen de muestra

Crea un archivo llamado Dockerfile con el siguiente contenido.

cat > ./Dockerfile << EOF
FROM gcr.io/google-appengine/debian9@sha256:ebffcf0df9aa33f342c4e1d4c8428b784fc571cdf6fbab0b31330347ca8af97a

# System
RUN apt update && apt install python3-pip -y

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==1.1.4
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

EOF

Crea un archivo llamado main.py con el siguiente contenido:

cat > ./main.py << EOF
import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    name = os.environ.get("NAME", "Worlds")
    return "Hello {}!".format(name)

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
EOF

Compila y envía la imagen a AR

Usa Cloud Build para compilar y enviar automáticamente tu contenedor a Artifact Registry. Observa la etiqueta bad en la imagen. Esto te ayudará a identificarlo para los pasos posteriores.

gcloud builds submit . -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad

Revisa los detalles de la imagen

Una vez que se complete el proceso de compilación, revisa la imagen y los resultados de vulnerabilidades en el panel de Artifact Registry.

  1. Abre Artifact Registry en la consola de Cloud.
  2. Haz clic en artifact-scanning-repo para ver el contenido.
  3. Haz clic en los detalles de la imagen.
  4. Haz clic en el resumen más reciente de tu imagen.
  5. Una vez que finalice el análisis, haz clic en la pestaña de vulnerabilidades de la imagen.

En la pestaña Vulnerabilidades, verás los resultados del análisis automático de la imagen que acabas de compilar.

361be7b3bf293fca.png

La automatización del análisis está habilitada de forma predeterminada. Explora la configuración de Artifact Registry para ver cómo puedes activar o desactivar el análisis automático.

4. On-Demand Scanning

Existen varias situaciones en las que es posible que debas ejecutar un análisis antes de enviar la imagen a un repositorio. A modo de ejemplo, un desarrollador de contenedores puede analizar una imagen y corregir los problemas antes de enviar el código al control de código fuente. En el siguiente ejemplo, compilarás y analizarás la imagen de forma local antes de actuar en función de los resultados.

Compila una imagen

En este paso, usarás Docker local para compilar la imagen en tu caché local.

docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image .

Escanea la imagen

Una vez que se haya compilado la imagen, solicita un análisis de la imagen. Los resultados del análisis se almacenan en un servidor de metadatos. La tarea se completa con una ubicación de los resultados en el servidor de metadatos.

gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --format="value(response.scan)" > scan_id.txt

Revisa el archivo de salida

Tómate un momento para revisar el resultado del paso anterior, que se almacenó en el archivo scan_id.txt. Observa la ubicación del informe de los resultados del análisis en el servidor de metadatos.

cat scan_id.txt

Revisa los resultados detallados del análisis

Para ver los resultados reales del análisis, usa el comando list-vulnerabilities en la ubicación del informe que se indica en el archivo de salida.

gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) 

El resultado contiene una cantidad significativa de datos sobre todas las vulnerabilidades de la imagen.

Cómo marcar problemas críticos

Las personas rara vez usan directamente los datos almacenados en el informe. Por lo general, un proceso automatizado usa los resultados. Usa los siguientes comandos para leer los detalles del informe y registrar si se encontraron vulnerabilidades CRÍTICAS.

export SEVERITY=CRITICAL

gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq ${SEVERITY}; then echo "Failed vulnerability check for ${SEVERITY} level"; else echo "No ${SEVERITY} Vulnerabilities found"; fi

El resultado de este comando será

Failed vulnerability check for CRITICAL level

5. Análisis de la canalización de compilación

En esta sección, crearás una canalización de compilación automatizada que compilará tu imagen de contenedor, la analizará y, luego, evaluará los resultados. Si no se encuentran vulnerabilidades CRÍTICAS, se enviará la imagen al repositorio. Si se encuentran vulnerabilidades CRÍTICAS, la compilación fallará y se finalizará.

Proporciona acceso a la cuenta de servicio de Cloud Build

Cloud Build necesitará derechos para acceder a la API de análisis a pedido. Proporciona acceso con los siguientes comandos.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/iam.serviceAccountUser"
        
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/ondemandscanning.admin"

Crea la canalización de Cloud Build

El siguiente comando creará un archivo cloudbuild.yaml en tu directorio que se usará para el proceso automatizado. En este ejemplo, los pasos se limitan al proceso de compilación del contenedor. Sin embargo, en la práctica, incluirías instrucciones y pruebas específicas de la aplicación, además de los pasos del contenedor.

Crea el archivo con el siguiente comando:

cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']

images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image
EOF

Ejecuta la canalización de CI

Envía la compilación para su procesamiento y verifica que se interrumpa cuando se encuentre una vulnerabilidad de gravedad CRITICAL.

gcloud builds submit

Revisa la compilación fallida

La compilación que acabas de enviar fallará porque la imagen contiene vulnerabilidades CRITÍCAS.

Revisa el error de compilación en la página Historial de Cloud Build.

Corrige la vulnerabilidad

Actualiza el Dockerfile para usar una imagen base que no contenga vulnerabilidades CRÍTICAS.

Reemplaza el Dockerfile para usar la imagen de Debian 10 con el siguiente comando:

cat > ./Dockerfile << EOF
from python:3.8-slim  

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==2.1.0
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :\$PORT --workers 1 --threads 8 main:app

EOF

Ejecuta el proceso de CI con la imagen correcta.

Envía la compilación para su procesamiento y verifica que se realice correctamente cuando no se encuentren vulnerabilidades de gravedad CRITICAL.

gcloud builds submit

Revisa el éxito de la compilación

La compilación que acabas de enviar se realizará correctamente porque la imagen actualizada no contiene vulnerabilidades CRÍTICAS.

Revisa el éxito de la compilación en la página Historial de Cloud Build.

Revisa los resultados del análisis

Revisa la imagen correcta en Artifact Registry

  1. Abre Artifact Registry en la consola de Cloud.
  2. Haz clic en artifact-scanning-repo para ver el contenido.
  3. Haz clic en los detalles de la imagen.
  4. Haz clic en el resumen más reciente de tu imagen.
  5. Haz clic en la pestaña de vulnerabilidades de la imagen.

6. Firma de imágenes

Crea una nota de certificador

Una nota de certificación es simplemente un pequeño dato que actúa como una etiqueta para el tipo de firma que se aplica. Por ejemplo, una nota puede indicar un análisis de vulnerabilidades, mientras que otra puede usarse para la firma de QA. Se hará referencia a la nota durante el proceso de firma.

Cómo crear una nota

cat > ./vulnz_note.json << EOM
{
  "attestation": {
    "hint": {
      "human_readable_name": "Container Vulnerabilities attestation authority"
    }
  }
}
EOM

Almacena la nota

NOTE_ID=vulnz_note

curl -vvv -X POST \
    -H "Content-Type: application/json"  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
    --data-binary @./vulnz_note.json  \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/?noteId=${NOTE_ID}"

Verifica la nota

curl -vvv  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}"

Crea un certificador

Los certificadores se usan para realizar el proceso de firma de imágenes en sí y adjuntarán una ocurrencia de la nota a la imagen para su verificación posterior. Crea el certificador para usarlo más adelante.

Crear certificador

ATTESTOR_ID=vulnz-attestor

gcloud container binauthz attestors create $ATTESTOR_ID \
    --attestation-authority-note=$NOTE_ID \
    --attestation-authority-note-project=${PROJECT_ID}

Verifica el certificador

gcloud container binauthz attestors list

Ten en cuenta que la última línea indica que NUM_PUBLIC_KEYS: 0 proporcionarás claves en un paso posterior.

Ten en cuenta también que Cloud Build crea automáticamente el certificador built-by-cloud-build en el proyecto cuando ejecutas una compilación que genera imágenes. Por lo tanto, el comando anterior muestra dos certificadores, vulnz-attestor y built-by-cloud-build. Una vez que las imágenes se compilan correctamente, Cloud Build firma y crea certificaciones de forma automática para ellas.

Agrega un rol de IAM

La cuenta de servicio de autorización binaria necesitará derechos para ver las notas de certificación. Proporciona el acceso con la siguiente llamada a la API

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

BINAUTHZ_SA_EMAIL="service-${PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"


cat > ./iam_request.json << EOM
{
  'resource': 'projects/${PROJECT_ID}/notes/${NOTE_ID}',
  'policy': {
    'bindings': [
      {
        'role': 'roles/containeranalysis.notes.occurrences.viewer',
        'members': [
          'serviceAccount:${BINAUTHZ_SA_EMAIL}'
        ]
      }
    ]
  }
}
EOM

Usa el archivo para crear la política de IAM

curl -X POST  \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    --data-binary @./iam_request.json \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}:setIamPolicy"

Agrega una clave de KMS

El certificador necesita claves criptográficas para adjuntar la nota y proporcionar firmas verificables. En este paso, crearás y almacenarás claves en KMS para que Cloud Build pueda acceder a ellas más adelante.

Primero, agrega algunas variables de entorno para describir la clave nueva.

KEY_LOCATION=global
KEYRING=binauthz-keys
KEY_NAME=codelab-key
KEY_VERSION=1

Crea un llavero para contener un conjunto de llaves

gcloud kms keyrings create "${KEYRING}" --location="${KEY_LOCATION}"

Crea un nuevo par de claves de firma asimétrica para el certificador

gcloud kms keys create "${KEY_NAME}" \
    --keyring="${KEYRING}" --location="${KEY_LOCATION}" \
    --purpose asymmetric-signing   \
    --default-algorithm="ec-sign-p256-sha256"

Deberías ver tu clave en la página de KMS de la consola de Google Cloud.

Ahora, asocia la clave con tu certificador a través del comando gcloud binauthz:

gcloud beta container binauthz attestors public-keys add  \
    --attestor="${ATTESTOR_ID}"  \
    --keyversion-project="${PROJECT_ID}"  \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

Si vuelves a imprimir la lista de autoridades, ahora deberías ver una clave registrada:

gcloud container binauthz attestors list

Cómo crear una certificación firmada

En este punto, tienes configuradas las funciones que te permiten firmar imágenes. Usa el certificador que creaste anteriormente para firmar la imagen de contenedor con la que trabajaste.

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image

DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:latest \
    --format='get(image_summary.digest)')

Ahora, puedes usar gcloud para crear tu certificación. El comando simplemente toma los detalles de la clave que deseas usar para la firma y la imagen de contenedor específica que deseas aprobar.

gcloud beta container binauthz attestations sign-and-create  \
    --artifact-url="${CONTAINER_PATH}@${DIGEST}" \
    --attestor="${ATTESTOR_ID}" \
    --attestor-project="${PROJECT_ID}" \
    --keyversion-project="${PROJECT_ID}" \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

En términos de Container Analysis, esto creará un caso nuevo y lo adjuntará a la nota de tu certificador. Para asegurarte de que todo funcione según lo esperado, puedes enumerar tus certificaciones

gcloud container binauthz attestations list \
   --attestor=$ATTESTOR_ID --attestor-project=${PROJECT_ID}

7. Firma con Cloud Build

Habilitaste la firma de imágenes y usaste el certificador de forma manual para firmar tu imagen de ejemplo. En la práctica, querrás aplicar las certificaciones durante los procesos automatizados, como las canalizaciones de CI/CD.

En esta sección, configurarás Cloud Build para que certifique imágenes automáticamente.

Funciones

Agrega la función de visualizador de certificadores de autorización binaria a la cuenta de servicio de Cloud Build:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/binaryauthorization.attestorsViewer

Agrega la función de verificador y firmante de CryptoKey de Cloud KMS a la cuenta de servicio de Cloud Build (firma basada en KMS):

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/cloudkms.signerVerifier

Agrega la función de vinculador de notas de Container Analysis a la cuenta de servicio de Cloud Build:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/containeranalysis.notes.attacher

Prepara el paso de compilación personalizada de Cloud Build

Usarás un paso de compilación personalizada en Cloud Build para simplificar el proceso de certificación. Google proporciona este paso de compilación personalizada, que contiene funciones auxiliares para optimizar el proceso. Antes de usarlo, el código para el paso de compilación personalizado se debe compilar en un contenedor y enviar a Cloud Build. Para ello, ejecuta los siguientes comandos:

git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
cd cloud-builders-community/binauthz-attestation
gcloud builds submit . --config cloudbuild.yaml
cd ../..
rm -rf cloud-builders-community

Agrega un paso de firma a tu archivo cloudbuild.yaml

En este paso, agregarás el paso de certificación a la canalización de Cloud Build que compilaste antes.

  1. Revisa el nuevo paso que agregarás.

Solo revisión. No copiar

#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'
  1. Reemplaza el archivo cloudbuild.yaml por la canalización completa actualizada.
cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'



images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good
EOF

Ejecuta la compilación

gcloud builds submit

Revisa la compilación en el historial de Cloud Build

Abre la consola de Cloud en la página Historial de Cloud Build y revisa la compilación más reciente y la ejecución correcta de los pasos de compilación.

8. Políticas de control de admisión

La autorización binaria es una función de GKE y Cloud Run que permite validar reglas antes de que se pueda ejecutar una imagen de contenedor. La validación se ejecuta en cualquier solicitud para ejecutar una imagen, ya sea desde una canalización de CI/CD de confianza o un usuario que intenta implementar una imagen de forma manual. Esta función te permite proteger tus entornos de tiempo de ejecución de manera más eficaz que solo con las verificaciones de canalización de CI/CD.

Para comprender esta función, modificarás la política predeterminada de GKE para aplicar una regla de autorización estricta.

Crea el clúster de GKE

Crea el clúster de GKE:

gcloud beta container clusters create binauthz \
    --zone us-central1-a  \
    --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE

Permite que Cloud Build realice la implementación en este clúster:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/container.developer"

Política de permitir todo

Primero, verifica el estado de la política predeterminada y tu capacidad para implementar cualquier imagen.

  1. Revisa la política existente
gcloud container binauthz policy export
  1. Ten en cuenta que la política de aplicación se establece en ALWAYS_ALLOW.

evaluationMode: ALWAYS_ALLOW

  1. Implementa una muestra para verificar que puedes implementar cualquier cosa
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. Verifica que la implementación haya funcionado
kubectl get pods

Verás el siguiente resultado:

161db370d99ffb13.png

  1. Borrar implementación
kubectl delete pod hello-server

Política de denegación de todo

Ahora, actualiza la política para inhabilitar todas las imágenes.

  1. Exporta la política actual a un archivo editable
gcloud container binauthz policy export  > policy.yaml
  1. Cambiar la política.

En un editor de texto, cambia evaluationMode de ALWAYS_ALLOW a ALWAYS_DENY.

edit policy.yaml

El archivo de políticas en formato YAML debe aparecer de la siguiente manera:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_DENY
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. Abre Terminal, aplica la nueva política y espera unos segundos a que se propague el cambio.
gcloud container binauthz policy import policy.yaml
  1. Intenta implementar una carga de trabajo de muestra
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. La implementación falla con el siguiente mensaje
Error from server (VIOLATES_POLICY): admission webhook "imagepolicywebhook.image-policy.k8s.io" denied the request: Image gcr.io/google-samples/hello-app:1.0 denied by Binary Authorization default admission rule. Denied by always_deny admission rule

Revierte la política para permitir todo

Antes de pasar a la siguiente sección, asegúrate de revertir los cambios en la política.

  1. Cambiar la política.

En un editor de texto, cambia evaluationMode de ALWAYS_DENY a ALWAYS_ALLOW.

edit policy.yaml

El archivo de políticas en formato YAML debe aparecer de la siguiente manera:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_ALLOW
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. Aplica la política revertida
gcloud container binauthz policy import policy.yaml

9. Bloquea vulnerabilidades en GKE

En esta sección, combinarás lo que aprendiste hasta ahora implementando una canalización de CI/CD con Cloud Build que analice las imágenes y, luego, verifique si hay vulnerabilidades antes de firmar la imagen y de intentar implementarla. GKE usará la autorización binaria para validar que la imagen tenga una firma del análisis de vulnerabilidades antes de permitir que se ejecute.

d5c41bb89e22fd61.png

Actualiza la política de GKE para que requiera certificación

Agrega clusterAdmissionRules a tu política de BinAuth de GKE para exigir que tu certificador firme las imágenes.

Reemplaza la política con la configuración actualizada con el siguiente comando.

COMPUTE_ZONE=us-central1-a

cat > binauth_policy.yaml << EOM
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: ALWAYS_DENY
globalPolicyEvaluationMode: ENABLE
clusterAdmissionRules:
  ${COMPUTE_ZONE}.binauthz:
    evaluationMode: REQUIRE_ATTESTATION
    enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
    requireAttestationsBy:
    - projects/${PROJECT_ID}/attestors/vulnz-attestor
EOM

Aplica la política

gcloud beta container binauthz policy import binauth_policy.yaml

Intenta implementar la imagen sin firmar

Crea un descriptor de implementación para la aplicación que compilaste antes con el siguiente comando. La imagen que se usa aquí es la que compilaste antes, que contiene vulnerabilidades críticas y NO contiene la certificación firmada.

Los controladores de admisión de GKE deben conocer la imagen exacta que se implementará para validar la firma de forma coherente. Para ello, deberás usar el resumen de la imagen y una etiqueta simple.

Obtén el resumen de la imagen de mala calidad

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:bad \
    --format='get(image_summary.digest)')

Usa el resumen en la configuración de Kubernetes

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

Intenta implementar la app en GKE

kubectl apply -f deploy.yaml

Revisa la carga de trabajo en la consola y observa el error que indica que se rechazó la implementación:

No attestations found that were valid and signed by a key trusted by the attestor

Implementa una imagen firmada

Obtén el resumen de la imagen de mala calidad

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:good \
    --format='get(image_summary.digest)')

Usa el resumen en la configuración de Kubernetes

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

Implementar la app en GKE.

kubectl apply -f deploy.yaml

Revisa la carga de trabajo en la consola y observa que la imagen se implementó correctamente.

10. ¡Felicitaciones!

¡Felicitaciones! Completaste el codelab.

Temas abordados:

  • Cómo habilitar el análisis automático
  • Cómo realizar un análisis On-Demand Scanning
  • Cómo integrar el análisis en una canalización de compilación
  • Cómo firmar imágenes aprobadas
  • Cómo usar los controladores de admisión de GKE para bloquear imágenes
  • Cómo configurar GKE para permitir solo imágenes aprobadas firmadas

¿Qué sigue?

Limpia

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 el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.