Build sécurisé Déployer avec Cloud Build, Artifact Registry et GKE

1. Introduction

Container Analysis fournit une analyse des failles et un stockage de métadonnées pour les conteneurs. Le service d'analyse effectue des analyses des failles sur les images dans Artifact Registry et Container Registry, puis stocke les métadonnées obtenues et les met à disposition via une API. Le stockage de métadonnées vous permet de stocker des informations provenant de différentes sources, y compris l'analyse des failles, les services Google Cloud et les fournisseurs tiers.

L'analyse des failles peut s'effectuer automatiquement ou à la demande:

  • Lorsque l'analyse automatique est activée, l'analyse se déclenche automatiquement chaque fois que vous transférez une nouvelle image vers Artifact Registry ou Container Registry. Les informations sur les failles sont mises à jour en permanence lorsque de nouvelles failles sont découvertes.
  • Lorsque l'analyse à la demande est activée, vous devez exécuter une commande pour analyser une image locale ou une image dans Artifact Registry ou Container Registry. L'analyse à la demande vous offre une certaine flexibilité concernant l'analyse des conteneurs. Par exemple, vous pouvez analyser une image créée localement et corriger les failles avant de la stocker dans un registre. Les résultats sont disponibles jusqu'à 48 heures après la fin de l'analyse. Les informations sur les failles ne sont pas mises à jour après l'analyse.

Grâce à Container Analysis intégré à votre pipeline CI/CD, vous pouvez prendre des décisions en fonction de ces métadonnées. Par exemple, vous pouvez utiliser l'autorisation binaire pour créer des stratégies de déploiement qui n'autorisent que les déploiements d'images conformes provenant de registres de confiance.

Points abordés

  • Activer l'analyse automatique
  • Effectuer une analyse à la demande
  • Intégrer l'analyse dans un pipeline de compilation
  • Signer les images approuvées
  • Utiliser les contrôleurs d'admission GKE pour bloquer les images
  • Configurer GKE pour n'autoriser que les images approuvées signées

2. Préparation

Configuration de l'environnement d'auto-formation

  1. Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. (Si vous ne possédez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.)

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères non utilisée par les API Google. Vous pouvez le modifier à tout moment.
  • L'ID du projet est unique parmi tous les projets Google Cloud et non modifiable une fois défini. La console Cloud génère automatiquement une chaîne unique. généralement, vous ne vous souciez pas de ce que c’est. Dans la plupart des ateliers de programmation, vous devrez référencer l'ID du projet (il est généralement identifié comme PROJECT_ID). Si l'ID généré ne vous convient pas, vous pouvez en générer un autre au hasard. Vous pouvez également essayer la vôtre pour voir si elle est disponible. Il ne peut pas être modifié après cette étape et restera actif pendant toute la durée du projet.
  • Pour votre information, il existe une troisième valeur, le numéro de projet, utilisé par certaines API. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
  1. Vous devez ensuite activer la facturation dans la console Cloud pour utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour arrêter les ressources afin d'éviter que des frais ne vous soient facturés au-delà de ce tutoriel, vous pouvez supprimer les ressources que vous avez créées ou l'ensemble du projet. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai gratuit pour bénéficier d'un crédit de 300 $.

Démarrer l'éditeur Cloudshell

Cet atelier a été conçu et testé pour être utilisé avec l'éditeur Google Cloud Shell. Pour accéder à l'éditeur,

  1. Accédez à votre projet Google à l'adresse https://console.cloud.google.com.
  2. En haut à droite, cliquez sur l'icône de l'éditeur Cloud Shell.

8560cc8d45e8c112.png

  1. Un nouveau volet s'ouvre au bas de la fenêtre.

Configuration de l'environnement

Dans Cloud Shell, définissez l'ID et le numéro de votre projet. Enregistrez-les en tant que variables PROJECT_ID et PROJECT_ID.

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

Activer les services

Activez tous les services nécessaires :

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 

Créer un dépôt Artifact Registry

Dans cet atelier, vous allez stocker et analyser vos images à l'aide d'Artifact Registry. Créez le dépôt à l'aide de la commande suivante.

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

Configurez Docker pour utiliser vos identifiants gcloud lorsque vous accédez à Artifact Registry.

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

3. Analyse automatisée

L'analyse des artefacts se déclenche automatiquement chaque fois que vous transférez une nouvelle image vers Artifact Registry ou Container Registry. Les informations sur les failles sont mises à jour en permanence lorsque de nouvelles failles sont découvertes. Dans cette section, vous allez transférer une image vers Artifact Registry et explorer les résultats.

Créer un répertoire professionnel et y accéder

mkdir vuln-scan && cd vuln-scan

Définir un exemple d'image

Créez un fichier nommé Dockerfile avec le contenu suivant.

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

Créez un fichier nommé main.py avec le contenu suivant :

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

Créer l'image et la transférer en RA

Utilisez Cloud Build pour créer votre conteneur et le transférer automatiquement vers Artifact Registry. Notez le tag bad sur l'image. Cela vous aidera à l'identifier pour les étapes suivantes.

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

Examiner les détails de l'image

Une fois le processus de compilation terminé, examinez l'image et les résultats de failles dans le tableau de bord Artifact Registry.

  1. Ouvrez Artifact Registry dans la console Cloud
  2. Cliquez sur artifact-scanning-repo pour afficher le contenu
  3. Cliquez sur les détails de l'image.
  4. Cliquez sur le dernier condensé de votre image.
  5. Une fois l'analyse terminée, cliquez sur l'onglet des failles de l'image.

Dans l'onglet des failles, vous pouvez voir les résultats de l'analyse automatique de l'image que vous venez de créer.

361be7b3bf293fca.png

L'automatisation de l'analyse est activée par défaut. Explorez les paramètres d'Artifact Registry pour découvrir comment activer et désactiver l'analyse automatique.

4. On-Demand Scanning

Il existe différents scénarios dans lesquels vous pouvez avoir besoin d'exécuter une analyse avant de transférer l'image vers un dépôt. Par exemple, un développeur de conteneurs peut analyser une image et résoudre les problèmes avant de transmettre du code au contrôle source. Dans l'exemple ci-dessous, vous allez créer et analyser l'image localement avant d'agir sur les résultats.

Créer une image

Au cours de cette étape, vous allez utiliser un Dockerfile local pour créer l'image dans votre cache local.

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

Scanner l'image

Une fois l'image créée, demandez une analyse. Les résultats de l'analyse sont stockés sur un serveur de métadonnées. La tâche se termine avec un emplacement des résultats dans le serveur de métadonnées.

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

Examiner le fichier de sortie

Prenez quelques instants pour examiner le résultat de l'étape précédente, stocké dans le fichier scan_id.txt. Notez l'emplacement du rapport contenant les résultats de l'analyse sur le serveur de métadonnées.

cat scan_id.txt

Examiner les résultats d'analyse détaillés

Pour afficher les résultats réels de l'analyse, utilisez la commande list-vulnerabilities à l'emplacement du rapport indiqué dans le fichier de sortie.

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

Le résultat contient une quantité importante de données sur toutes les failles présentes dans l'image.

Signaler des problèmes critiques

Il est rare que les humains utilisent directement les données stockées dans le rapport. Les résultats sont généralement utilisés par un processus automatisé. Utilisez les commandes ci-dessous pour lire les détails du rapport et consigner les failles CRITICAL détectées.

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

Le résultat de cette commande sera

Failed vulnerability check for CRITICAL level

5. Analyse du pipeline de compilation

Dans cette section, vous allez créer un pipeline de compilation automatisé qui va compiler votre image de conteneur, l'analyser, puis évaluer les résultats. Si aucune faille CRITICAL n'est détectée, l'image est transférée dans le dépôt. Si des failles CRITICAL sont détectées, la compilation échouera et se fermera.

Fournir un accès au compte de service Cloud Build

Cloud Build requiert des droits pour accéder à l'API d'analyse à la demande. Accordez l'accès à l'aide des commandes suivantes.

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"

Créer le pipeline Cloud Build

La commande suivante crée un fichier cloudbuild.yaml dans votre répertoire qui sera utilisé par le processus automatisé. Dans cet exemple, les étapes sont limitées au processus de compilation du conteneur. En pratique, vous devez toutefois inclure des tests et des instructions spécifiques à l'application en plus des étapes relatives au conteneur.

Créez le fichier à l'aide de la commande suivante.

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

Exécuter le pipeline CI

Envoyez la compilation pour traitement afin de vérifier qu'elle ne fonctionne plus lorsqu'une faille de gravité critique est détectée.

gcloud builds submit

Examiner l'échec de la compilation

La compilation que vous venez d'envoyer échouera, car l'image contient des failles CRITICAL.

Examinez l'échec de la compilation sur la page Historique Cloud Build.

Corriger la faille

Mettez à jour le Dockerfile pour utiliser une image de base ne contenant pas de failles CRITICAL.

Écrasez le Dockerfile pour utiliser l'image Debian 10 avec la commande suivante :

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

Exécuter le processus CI avec la bonne image

Envoyez la compilation pour traitement afin de vérifier qu'elle aboutit si aucune faille de gravité critique n'est détectée.

gcloud builds submit

Évaluer le succès de la compilation

Le build que vous venez d'envoyer va réussir, car l'image mise à jour ne présente aucune faille CRITICAL.

Examinez la réussite de la compilation sur la page Historique Cloud Build.

Examiner les résultats de l'analyse

Examiner l'image correcte dans Artifact Registry

  1. Ouvrez Artifact Registry dans la console Cloud
  2. Cliquez sur artifact-scanning-repo pour afficher le contenu
  3. Cliquez sur les détails de l'image.
  4. Cliquez sur le dernier condensé de votre image.
  5. Cliquez sur l'onglet des failles de l'image.

6. Signature d'images

Créer une note du certificateur

Une note du certificateur est simplement un petit bit de données qui sert d’étiquette pour le type de signature appliqué. Par exemple, une note peut indiquer une analyse des failles, tandis qu'une autre peut être utilisée pour l'approbation du contrôle qualité. La note sera référencée lors du processus de signature.

Créer une note

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

Stocker la note

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}"

Vérifier la note

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

Créer un certificateur

Les certificateurs sont utilisés pour effectuer le processus de signature d'image et attachent une occurrence de la note à l'image pour une vérification ultérieure. Créez le certificateur pour une utilisation ultérieure.

Créer un certificateur

ATTESTOR_ID=vulnz-attestor

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

Valider le certificateur

gcloud container binauthz attestors list

Notez que la dernière ligne indique NUM_PUBLIC_KEYS: 0. Vous fournirez des clés lors d'une prochaine étape

Notez également que Cloud Build crée automatiquement le certificateur built-by-cloud-build dans votre projet lorsque vous exécutez une compilation qui génère des images. La commande ci-dessus renvoie donc deux certificateurs, vulnz-attestor et built-by-cloud-build. Une fois les images créées, Cloud Build signe et crée automatiquement des attestations pour celles-ci.

Ajouter un rôle IAM

Le compte de service de l'autorisation binaire doit disposer de droits pour afficher les notes d'attestation. Accordez l'accès à l'aide de l'appel d'API suivant :

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

Utiliser le fichier pour créer la stratégie 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"

Ajouter une clé KMS

Le certificateur a besoin de clés cryptographiques pour joindre la note et fournir des signatures vérifiables. Au cours de cette étape, vous allez créer et stocker des clés dans KMS pour que Cloud Build puisse y accéder ultérieurement.

Commencez par ajouter des variables d'environnement pour décrire la nouvelle clé

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

Créer un trousseau pour contenir un ensemble de clés

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

Créer une paire de clés de signature asymétrique pour le certificateur

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

Votre clé doit apparaître sur la page KMS de la console Google Cloud.

À présent, associez la clé à votre certificateur via la commande 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 vous imprimez à nouveau la liste des autorités, vous devriez maintenant voir une clé enregistrée:

gcloud container binauthz attestors list

Créer une attestation signée

À ce stade, vous disposez des fonctionnalités configurées qui vous permettent de signer des images. Utilisez le certificateur que vous avez créé précédemment pour signer l'image de conteneur avec laquelle vous travaillez

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

Vous pouvez maintenant utiliser gcloud pour créer votre attestation. La commande récupère simplement les détails de la clé que vous souhaitez utiliser pour la signature, ainsi que l'image de conteneur spécifique que vous souhaitez approuver.

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}"

Dans le cas de Container Analysis, cela crée une occurrence et l'associe à la note du certificateur. Pour vous assurer que tout a fonctionné comme prévu, vous pouvez lister vos attestations

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

7. Signer avec Cloud Build

Vous avez activé la signature d'image et utilisé manuellement le certificateur pour signer votre exemple d'image. En pratique, vous devrez appliquer des attestations au cours de processus automatisés tels que les pipelines CI/CD.

Dans cette section, vous allez configurer Cloud Build pour attester automatiquement les images.

Rôles

Attribuez le rôle "Lecteur des certificateurs de l'autorisation binaire" au compte de service Cloud Build :

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

Attribuez le rôle "Signataire/Validateur de CryptoKeys Cloud KMS" au compte de service Cloud Build (signature basée sur KMS) :

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

Attribuez le rôle "Agent d'association de notes Container Analysis" au compte de service Cloud Build :

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

Préparer l'étape de compilation personnalisée Cloud Build

Vous allez utiliser une étape de compilation personnalisée dans Cloud Build pour simplifier le processus d'attestation. Google propose cette étape de compilation personnalisée qui contient des fonctions d'assistance pour simplifier le processus. Avant d'être utilisé, le code de l'étape de compilation personnalisée doit être intégré à un conteneur et transféré vers Cloud Build. Pour ce faire, exécutez les commandes suivantes:

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

Ajouter une étape de signature à votre fichier cloudbuild.yaml

Au cours de cette étape, vous allez ajouter l'étape d'attestation au pipeline Cloud Build que vous avez créé précédemment.

  1. Examinez la nouvelle étape que vous allez ajouter.

Avis uniquement. Ne pas copier

#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. Remplacez le fichier cloudbuild.yaml par le pipeline complet mis à jour.
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

Exécuter le build

gcloud builds submit

Examiner la compilation dans l'historique Cloud Build

Ouvrez la console Cloud sur la page Historique Cloud Build, puis examinez la dernière compilation et l'exécution réussie des étapes de compilation.

8. Règles concernant le contrôle des admissions

L'autorisation binaire est une fonctionnalité de GKE et de Cloud Run qui permet de valider des règles avant qu'une image de conteneur ne soit autorisée à s'exécuter. La validation s'exécute sur n'importe quelle requête d'exécution d'une image, que ce soit à partir d'un pipeline CI/CD approuvé ou lorsqu'un utilisateur tente manuellement de déployer une image. Cette fonctionnalité vous permet de sécuriser vos environnements d'exécution plus efficacement que les seules vérifications de pipeline CI/CD.

Pour comprendre cette fonctionnalité, vous allez modifier la stratégie GKE par défaut afin d'appliquer une règle d'autorisation stricte.

Créer le cluster GKE

Créez le cluster GKE :

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

Autorisez Cloud Build à déployer sur ce cluster:

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

Règle "Tout autoriser"

Vérifiez d'abord l'état de la règle par défaut et votre capacité à déployer n'importe quelle image

  1. Examiner les règles existantes
gcloud container binauthz policy export
  1. Notez que la règle d'application est définie sur ALWAYS_ALLOW

evaluationMode: ALWAYS_ALLOW

  1. Déployez un exemple pour vérifier que vous pouvez déployer tout ce que vous voulez
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. Vérifier que le déploiement a fonctionné
kubectl get pods

La sortie suivante s'affiche :

161db370d99ffb13.png

  1. Supprimer le déploiement
kubectl delete pod hello-server

Règle "Tout refuser"

Modifiez maintenant la stratégie pour interdire toutes les images.

  1. Exporter la stratégie actuelle dans un fichier modifiable
gcloud container binauthz policy export  > policy.yaml
  1. Modifier les règles

Dans un éditeur de texte, remplacez la valeur du mode d'évaluation ALWAYS_ALLOW par ALWAYS_DENY.

edit policy.yaml

Le fichier YAML de stratégie doit ressembler à ceci :

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_DENY
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. Ouvrez le terminal, appliquez la nouvelle règle et attendez quelques secondes pour que la modification soit appliquée.
gcloud container binauthz policy import policy.yaml
  1. Tentative de déploiement de l'exemple de charge de travail
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. Échec du déploiement avec le message suivant
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

Rétablissez la règle pour tout autoriser

Avant de passer à la section suivante, assurez-vous d'annuler les modifications apportées aux règles

  1. Modifier les règles

Dans un éditeur de texte, remplacez la valeur du mode d'évaluation ALWAYS_DENY par ALWAYS_ALLOW.

edit policy.yaml

Le fichier YAML de stratégie doit ressembler à ceci :

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_ALLOW
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. Appliquer la règle annulée
gcloud container binauthz policy import policy.yaml

9. Bloquer les failles dans GKE

Dans cette section, vous allez combiner ce que vous avez appris jusqu'à présent en implémentant un pipeline CI/CD avec Cloud Build qui analyse les images, puis recherche les failles avant de signer l'image et de tenter de la déployer. GKE utilisera l'autorisation binaire pour vérifier que l'image possède une signature issue de l'analyse des failles avant d'autoriser l'exécution de l'image.

d5c41bb89e22fd61.png

Mettre à jour la stratégie GKE pour exiger une attestation

Exiger que les images soient signées par votre certificateur en ajoutant clusterAdmissionRules à votre règle GKE BinAuth

Remplacez la règle par la configuration mise à jour à l'aide de la commande ci-dessous.

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

Appliquer la règle

gcloud beta container binauthz policy import binauth_policy.yaml

Tentative de déploiement de l'image non signée

Créez un descripteur de déploiement pour l'application que vous avez créée précédemment à l'aide de la commande suivante. L'image utilisée ici est l'image que vous avez créée précédemment. Elle contient des failles critiques et NE contient PAS l'attestation signée.

Les contrôleurs d'admission GKE doivent connaître l'image exacte à déployer pour valider la signature de manière cohérente. Pour ce faire, vous devez utiliser le condensé de l'image et un simple tag.

Obtenir le condensé de l'image incorrecte

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

Utiliser le condensé dans la configuration 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

Essayer de déployer l'application sur GKE

kubectl apply -f deploy.yaml

Examinez la charge de travail dans la console et notez l'erreur indiquant que le déploiement a été refusé:

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

Déployer une image signée

Obtenir le condensé de l'image incorrecte

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

Utiliser le condensé dans la configuration 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

Déployer l'application sur GKE

kubectl apply -f deploy.yaml

Examinez la charge de travail dans la console et notez le déploiement de l'image.

10. Félicitations !

Félicitations, vous avez terminé cet atelier de programmation.

Points abordés

  • Activer l'analyse automatique
  • Effectuer une analyse à la demande
  • Intégrer l'analyse dans un pipeline de compilation
  • Signer les images approuvées
  • Utiliser les contrôleurs d'admission GKE pour bloquer les images
  • Configurer GKE pour n'autoriser que les images approuvées signées

Étapes suivantes :

Effectuer un nettoyage

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez chaque ressource individuellement.

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.