Sécuriser les builds de conteneurs

1. Introduction

ead1609267034bf7.png

Les failles logicielles sont des points faibles qui peuvent entraîner une défaillance accidentelle du système ou permettre à des personnes malintentionnées de compromettre votre logiciel. Container Analysis propose deux types d'analyse de l'OS pour détecter les failles dans les conteneurs :

  • L'API On-Demand Scanning vous permet d'analyser manuellement les images de conteneurs pour détecter les failles de l'OS, en local sur votre ordinateur ou à distance dans Container Registry ou Artifact Registry.
  • L'API Container Scanning vous permet d'automatiser la détection des failles de l'OS en analysant chaque image que vous transférez vers Container Registry ou Artifact Registry. L'activation de cette API active également les analyses des packages de langage pour les failles Go et Java.

L'API On-Demand Scanning vous permet d'analyser les images stockées en local sur votre ordinateur, ou à distance dans Container Registry ou Artifact Registry. Vous pouvez ainsi contrôler précisément les conteneurs que vous souhaitez analyser pour détecter les failles. Vous pouvez utiliser On-Demand Scanning pour analyser les images de votre pipeline CI/CD avant de décider de les stocker ou non dans un registre.

Points abordés

Dans cet atelier, vous allez apprendre à réaliser les opérations suivantes :

  • Créer des images avec Cloud Build
  • Utiliser Artifact Registry pour les conteneurs
  • Utiliser l'analyse automatique des failles
  • Configurer On-Demand Scanning
  • Ajouter l'analyse des images de votre pipeline CI/CD dans Cloud Build

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 (en général, vous n'y accordez d'importance particulière). Dans la plupart des ateliers de programmation, vous devrez indiquer l'ID du projet (généralement identifié par PROJECT_ID). Si l'ID généré ne vous convient pas, vous pouvez en générer un autre de manière aléatoire. Vous pouvez également en spécifier un et voir s'il est disponible. Après cette étape, l'ID n'est plus modifiable et restera donc le même pour toute la durée du projet.
  • Pour information, il existe une troisième valeur (le numéro de projet) que certaines API utilisent. 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 désactiver les ressources et éviter ainsi que des frais ne vous soient facturés après ce tutoriel, vous pouvez supprimer le projet ou les ressources que vous avez créées. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300$.

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 

3. Créer des images avec Cloud Build

Dans cette section, vous allez créer un pipeline de compilation automatisé qui compilera votre image de conteneur, l'analysera, puis évaluera les résultats. Si aucune faille CRITICAL n'est détectée, l'image est transférée vers le dépôt. Si des failles CRITICAL sont détectées, la compilation échoue et se ferme.

Configurer l'accès pour le compte de service Cloud Build

Cloud Build aura besoin d'autorisations pour accéder à l'API On-Demand Scanning. 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éez un répertoire de travail et accédez-y.

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 le pipeline Cloud Build

La commande suivante crée un fichier cloudbuild.yaml dans votre répertoire, qui sera utilisé pour le processus automatisé. Pour cet exemple, les étapes sont limitées au processus de compilation du conteneur. En pratique, vous incluez des instructions et des tests spécifiques à l'application en plus des étapes du 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: ['-']


EOF

Exécuter le pipeline CI

Envoyer la compilation pour traitement

gcloud builds submit

Examiner les détails de la compilation

Une fois le processus de compilation lancé, examinez sa progression dans le tableau de bord Cloud Build.

  1. Ouvrez Cloud Build dans la console Cloud.
  2. Cliquez sur la compilation pour afficher son contenu.

4. Artifact Registry pour les conteneurs

Créer un dépôt Artifact Registry

Dans cet atelier, vous allez utiliser Artifact Registry pour stocker et analyser vos images. 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 qu'il utilise vos identifiants gcloud lorsque vous accédez à Artifact Registry.

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

Mettre à jour le pipeline Cloud Build

Modifiez votre pipeline de compilation pour transférer l'image obtenue vers Artifact Registry.

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: ['-']

# push 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']

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

Exécuter le pipeline CI

Envoyer la compilation pour traitement

gcloud builds submit

5. Analyse automatique des failles

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 continu lorsque de nouvelles failles sont découvertes. Dans cette section, vous allez examiner l'image que vous venez de créer et de transférer vers Artifact Registry, et explorer les résultats de l'analyse des failles.

Examiner les détails de l'image

Une fois le processus de compilation précédent terminé, examinez les résultats de l'image et des 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 "Failles" correspondant à l'image.

Dans l'onglet "Failles", vous verrez 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 ou désactiver l'analyse automatique.

6. On-Demand Scanning

Dans différents scénarios, vous devrez peut-être effectuer une analyse avant de transférer l'image vers un dépôt. Par exemple, un développeur de conteneurs peut vouloir analyser une image et corriger les problèmes qu'il trouve avant de transférer le code vers le contrôle de source. Dans l'exemple ci-dessous, vous allez créer et analyser l'image localement avant d'agir en fonction des résultats.

Créer une image

Dans cette étape, vous allez utiliser Docker en 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 son analyse. Les résultats de l'analyse sont stockés dans un serveur de métadonnées. La tâche se termine en indiquant l'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 le temps d'examiner le résultat de l'étape précédente, qui a été stocké dans le fichier scan_id.txt. Notez l'emplacement du rapport des résultats de l'analyse dans le serveur de métadonnées.

cat scan_id.txt

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

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) 

La sortie contient une quantité importante de données sur toutes les failles de l'image.

Signaler des problèmes critiques

Les humains utilisent rarement les données stockées directement 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 éventuelles 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

7. Analyse dans la CI/CD avec Cloud Build

Configurer l'accès pour le compte de service Cloud Build

Cloud Build aura besoin d'autorisations pour accéder à l'API On-Demand Scanning. 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"

Mettre à jour le pipeline Cloud Build

La commande suivante crée un fichier cloudbuild.yaml dans votre répertoire, qui sera utilisé pour le processus automatisé. Pour cet exemple, les étapes sont limitées au processus de compilation du conteneur. En pratique, vous incluez des instructions et des tests spécifiques à l'application en plus des étapes du 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 échoue lorsqu'une faille de gravité CRITICAL 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 fichier Dockerfile de façon à utiliser une image de base qui ne contient pas de failles CRITICAL.

Écrasez le fichier Dockerfile de façon à 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 d'intégration continue avec la bonne image

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

gcloud builds submit

Examiner la réussite de la compilation

La compilation que vous venez d'envoyer réussira, car l'image mise à jour ne contient aucune faille CRITICAL.

Vérifiez que la compilation a réussi sur la page Historique Cloud Build.

Examiner les résultats de l'analyse

Vérifier 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 "Failles" de l'image.

8. Félicitations !

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

Points abordés

  • Créer des images avec Cloud Build
  • Artifact Registry pour les conteneurs
  • Analyse automatique des failles
  • On-Demand Scanning
  • Analyse dans la CI/CD avec Cloud Build

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

Dernière mise à jour : 21/03/2023