Sécuriser les builds de conteneurs

1. Introduction

ead1609267034bf7.png

Les failles logicielles sont des vulnérabilités pouvant entraîner une défaillance accidentelle du système ou fournir aux pirates informatiques un moyen 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 à la recherche de failles dans 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 fois que vous transférez une image vers Container Registry ou Artifact Registry. L'activation de cette API permet également d'analyser les packages de langage pour détecter les failles Go et Java.

L'API On-Demand Scanning vous permet d'analyser des images stockées localement 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 à la recherche de failles. Vous pouvez utiliser l'analyse à la demande pour analyser les images de votre pipeline CI/CD avant de décider de les stocker 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 l'analyse à la demande
  • Ajouter l'analyse des images dans le 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 mettre à jour à 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 de votre 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 gratuit 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 créera votre image de conteneur, l'analysera, puis évaluera les résultats. Si aucune faille critique n'est détectée, l'image est transmise au dépôt. Si des failles critiques sont détectées, la compilation échoue et se termine.

Accorder l'accès au compte de service Cloud Build

Cloud Build aura besoin de 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 et accéder à un répertoire de travail

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 devez toutefois inclure des instructions et des tests spécifiques à l'application en plus des étapes de 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 de CI

Envoyer la compilation pour traitement

gcloud builds submit

Vérifier les informations sur le build

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

  1. Ouvrez Cloud Build dans Cloud Console.
  2. Cliquez sur le build pour en afficher le 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

Modifier 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 de 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étectées. 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 l'image et les résultats de failles dans le tableau de bord Artifact Registry.

  1. Ouvrez Artifact Registry dans Cloud Console.
  2. Cliquez sur le dépôt artifact-scanning-repo pour en 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" de 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. Consultez les paramètres d'Artifact Registry pour savoir comment désactiver/activer la recherche automatique.

6. Analyse à la demande

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 conteneur peut analyser une image et corriger les problèmes avant de transférer le code vers le contrôle des sources. Dans l'exemple ci-dessous, vous allez compiler et analyser l'image localement avant d'agir sur les résultats.

Créer une image

À cette étape, vous allez utiliser Docker 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 .

Numériser l'image

Une fois l'image créée, demandez une analyse de l'image. Les résultats de l'analyse sont stockés dans un serveur de métadonnées. La tâche se termine avec l'emplacement des résultats sur 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 de consulter la sortie de l'étape précédente, qui a été stockée 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 sur 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 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 enregistrer les failles critiques 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. Analyser dans le CICD avec Cloud Build

Accorder l'accès au compte de service Cloud Build

Cloud Build aura besoin de 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"

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 devez toutefois inclure des instructions et des tests spécifiques à l'application en plus des étapes de 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 de CI

Envoyez le build pour traitement afin de vérifier qu'il é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 CRITIQUES.

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

Corriger la faille

Mettez à jour le fichier Dockerfile pour utiliser une image de base qui ne contient pas de failles critiques.

Remplacez le fichier Dockerfile pour utiliser l'image Debian 10 à l'aide de 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 de CI avec l'image correcte

Envoyez le build pour traitement afin de vérifier qu'il aboutit lorsqu'aucune faille de gravité CRITICAL n'est détectée.

gcloud builds submit

Vérifier la réussite de la compilation

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

Vérifier que la compilation a bien réussi 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 Cloud Console.
  2. Cliquez sur le dépôt artifact-scanning-repo pour en 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
  • Analyse à la demande
  • Analyser dans le CICD 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: 23/03/2023