# Stocker et vérifier ses images de container

Bonjour à toutes et tous

Aujourd'hui je vais ouvrir une boîte Pandore 🧐

Depuis quelques mois, je me suis plongé profondément dans l'écosystème du cloud native, Docker, images, containers et Kubernetes.

Jusqu'à maintenant je ne m'étais pas réellement préoccupé de la sécurité de ce que je faisais.

Mes images étaient basées sur les images officielles que l'on pouvait trouver sur docker hub et je les considérais comme sécurisées.

Ce que je n'avais pas compris ou fait semblant de ne pas comprendre, c'est que les images sont une représentation figée de l'état d'un système de fichier. Et donc par conséquent les failles de sécurité ne sont pas corrigées, il faut sans cesse vérifier si de nouvelles images sont crées et encore on est complètement dépendant du bon vouloir des éditeurs d'images pour les sécuriser.

Dans cet article, je vais vous proposer deux manières de vérifier le niveau de sécurité de vos images Docker. La première méthode se fera sur le site Gilab.com et la deuxième via un projet open-source qui peut s'auto-héberger poussé par VMware.

Pour chacune des deux manières je vais vous montrer comment les mettre en place, pour bénéficier d'une automatisation vérification des images qui seront poussés sur le registry Docker.

C'est parti ! 😄

# Méthode Gitlab.com

Gitlab propose un outil (opens new window) de vérification de containers et d'images directement intégré au sytème de CI.

Pour cela, il vous faut un compte sur gitlab.com (opens new window), celui ci est gratuit et vous permet de bénéficier de fonctionnalités avancées qui ne sont disponibles normalement que dans les paliers payants d'abonnements.

Dans ces fonctionnalités nous avons par exemple, la visualisation des failles de sécurité dans l'interface de contrôle des merge-requests.

Je considère que vous savez créer un projet sur Gitlab, dans le cas contraire la documentation (opens new window) est extrêmement fournie.

Dans ce projet à la racine vous aller créer 2 fichiers

Dockerfile

FROM ubuntu:20.04

Ce sera celui qui sera utilisé pour construire l'image que l'on désire tester.

.gitlab-ci.yml






















 

variables:
  DOCKER_DRIVER: overlay2

stages:
  - build
  - test

build:
  image: docker:stable
  stage: build
  services:
    - docker:19.03.12-dind
  variables:
    IMAGE: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
  script:
    - docker info
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
    - docker build -t $IMAGE .
    - docker push $IMAGE

include:
  - template: Container-Scanning.gitlab-ci.yml

Ce pipeline est composé de deux étapes:

  • une étape de build et de push de l'image dans le registry Docker de Gitlab
  • une étape de réalisation des opérations de vérifications

L'étape de vérification est entièrement contenue dans la ligne surlignée.

Elle inclue tout la logique du système de contrôle de sécurité.

Aujourd'hui celui-ci est basé sur un projet nommé Clair (opens new window). Mais dans l'avenir si le système change, votre pipeline n'aura pas besoin d'être modifié.

Pour tester le système, créez une branche peut importe le nom, je vais appeler la mienne dev.

Créez ensuite une merge-request de dev sur master.

Allez sur la branche dev

Modifiez le Dockerfile

FROM ubuntu

Puis committez les changements.

Après un petit moment vous devriez voir apparaître dans votre merge-request, une nouvelle ligne qui vous indique le nombre de failles.

Ici l'image busybox:latest ne contient ( et heureusement ) aucune failles de sécurités

Il est possible de visualiser le détail de ces vulnérabilités en cliquant sur le bouton "View full report".

On peut essayer avec plusieurs images:

# ubuntu:21.04

De même, occupe vulnérabilité à déplorer 😄

# ubuntu:20.04

On voit déjà quelques vulnérabilités mais elles sont considérées comme modérées.

On peut visualiser le détail d'une CVE en particulier en cliquant sur le bouton information (flèche rouge)

Une description de l'exploit vous indique l'impact de la faille sur le système.

A vous de juger si l'image doit être ou non corrigée.

# tomcat:10.0-jdk11-openjdk

Commençons par une image particulièrement vulnérable

On est sur un nombre très importants de vulnérabiltés (106)!

Il est vivement recommandé de patcher ces vulnérabilités.

Comme pour tout à l'heure on peut vérifier les détails.

# tomcat:10.0-jdk11-corretto

Modifier l'image de base permet de diminuer de manière drastique les failles de sécurité présentes dans l'image.

Il y en a toujours mais elles sont bien moins nombreuses et bien moins dangeureuses qu'avec la version openjdk basée sur debian 10.

Et c'est à peu près tout ce que vous devez faire sur Gitlab.com pour avoir l'assurance de la sécurité de vos images.

Après tout système est imparfait, Clair se base sur une base de données limité pour vérifier les différentes CVE et comme celles-ci apparaissent tous les jours c'est une course perdue d'avance. Tout ce qu'on peut faire c'est limité les risques.

Passons à la seconde méthode. 😃

# Méthode Harbor

Harbor (opens new window) est un service de registry d'images de container et de charts helm.

Le projet est opensource, gratuit, permet de pouvoir stocker, vérifier et distribuer ses images.

Il dispose aussi d'un système d'ACL assez poussé et de tout un tas de fonctionnalité sympas.

L'une d'elle va nous intéresser tout particulièrement: il s'agit de l'intégration Trivy (opens new window).

Comme son github l'indique, le rôle de Trivy est de scanner les vulnérabilités des containers et de réaliser un rapport détaillé et lisible afin qu'un humain puisse comprendre les problèmes et les fixer si besoin est.

Exactement comme pour la méthode gitlab.

Mais vous vous demandez surement pourquoi je vous parle de Harbor alors que le scan des images est très facile sur Gitlab.

Et bien, il y a une subtilité : en version auto-hébergée, Gitlab gratuit propose bien le scan des images mais pas la jolie interface de rapport.

Tout ce que vous aurez dans l'interface de votre merge request c'est un lien pour télécharger le rapport au format JSON.

Mais on doit se l'avouer c'est presque inutilisable, surtout lorsque le nombre de vulnérabilités est grand.

On sait d'expérience que si un outil n'est pas pratique on ne l'utilise pas. Et ce fichier JSON à télécharger est l'exemple type !

C'est pour cette raison je propose d'installer le service Harbor.

# Installer Harbor

Heureusement pour nous Harbor est très facile à installer. Il se présente sous une collection d'images docker rassemblé sous un docker-compose.

Les pré-requis sont donc d'avoir une machine possédant docker et docker-compose.

Il vous faut aussi un nom de domaine, soit vous avez un enregistrement DNS soit vous modifiez votre /etc/hosts.

Je ferai mon installation sur une ubuntu 20.04.

Vous pouvez récupérer la dernière release sur cette page (opens new window).

La version online est infiniment plus légère que la offline et donc je propose de l'utiliser, le résultat sera le même de toute façon. 😃

Avant tout on va récupérer un certificat.

Si vous n'avez pas de nom de domaine réel, vous pouvez utiliser un certificat auto-signé, mais attention, vous risquez d'avoir des alertes de sécurité pour plusieurs choses.

Nous allons préférer l'utilisation de Let's Encrypt qui permet d'obtenir des certificats signés par une autorité et donc parfaitement reconnu sans les problèmes de sécurités dont je vous ai mentionné l'existence plus haut.

Pour cela il existe un utilitaire extrêmement pratique nommé certbot.

On l'installe.

snap install certbot --classic

J'utilise snap car le package apt est à une version bien inférieure.

Vérifiez bien que votre nom de domaine résout bien l'IP de votre serveur harbor.

Vous pouvez vous aidez de:

nslookup harbor.example.com

Ensuite on génère le certificat

certbot certonly -m me@example.com --agree-tos --standalone -d harbor.example.com

Après quelques instants vous devriez

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/harbor.example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/harbor.example.com/privkey.pem
   Your certificate will expire on 2021-07-10. To obtain a new or
   tweaked version of this certificate in the future, simply run
   certbot again. To non-interactively renew *all* of your
   certificates, run "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Téléchargez la release:

wget https://github.com/goharbor/harbor/releases/download/v2.2.1/harbor-online-installer-v2.2.1.tgz

Modifier la version si besoin est.

Décompressez l'archive

tar xzvf harbor-online-installer-v2.2.1.tgz -C /opt

Copiez le fichier de configuration

cp /opt/harbor/harbor.yml.tmpl /opt/harbor/harbor.yml

Modifier le fichier /opt/harbor/harbor.yml.

Pour faire correspondre à votre nom de domaine et aux chemins des certificats.

hostname: harbor.example.com
https:
    port: 443
    certificate: /etc/letsencrypt/live/harbor.example.com/fullchain.pem
    private_key: /etc/letsencrypt/live/harbor.example.com/privkey.pem

Vous pouvez aussi modifier les mots de passes, je vais les laisser tel quel mais en production c'est évidemment une très mauvaise idée !!

Nous allons installer Harbor avec 3 composants supplémentaires:

cd /opt/harbor
./install.sh --with-notary --with-trivy --with-chartmuseum

Ce script à pour rôle de télécharger les images docker de Harbor, de configurer tout ce qui est à configurer et de lancer le tout.

Après quelques instants, vous pouvez vous rendre sur https://harbor.example.com.

Connectez vous avec les identifiants:

Par défaut:

user: admin
password: Harbor12345

Vous arrivez sur l'interfaces des projets, dans le jargon de Harbor, le projet est une isolation qui permet de limiter les accès aux images à un certain nombre d'utilisateurs.

Par défaut un projet library est déjà présent.

Cliquez sur le lien en bleu library.

Dans configuration coché la case Automatically scan images on push

Ceci va permettre de lancer automatiquement des scan de sécurité des images.

On va créer un utilisateur robot qui nous permettra de pousser nos images sur le repository.

  1. Cliquez dans la side-bar sur Robot accounts.
  2. Cliquez sur New Robot Account
  3. Donnez un nom à votre compte. Modifiez l'expiration du token si besoin
  4. Donnez les droits au robot sur le projet library

Le token ne sera plus remontré. Stockez-le quelque part. Il va être réutilisé tout de suite.

Et pour la forme je vous montre que trivy tourne bien ^^

# Configurer Gitlab

On va modifier notre pipeline Gitlab défini à la partie précédente.

Tout d'abord on se créé une nouvelle branche dérivée de master.

puis on modifie ainsi le docker-compose.yml

stages:
  - build

build:
  stage: build
  image: 
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  variables:
    IMAGE: ubuntu:21.04
    REGISTRY: harbor.laforge.works
  script:
  - cat $HARBOR_ROBOT_AUTH_FILE > /kaniko/.docker/config.json
  - | 
      /kaniko/executor \
      --context=$CI_PROJECT_DIR \
      --dockerfile=$CI_PROJECT_DIR/Dockerfile \
      --destination=$REGISTRY/library/$IMAGE \
      --build-arg=IMAGE=$IMAGE

On utilise un projet appelé kaniko (opens new window), celui ci remplace avantageusement le runtime docker par une solution plus rapide, plus légère moins contraignante et plus sécurisée.

En effet, kaniko, ne nécéssite pas de container privilégié pour construire les images puis les pousser dans un registry, ici pour nous notre Harbor.

On modifie aussi notre Dockerfile.

ARG $IMAGE=busybox:latest
FROM $IMAGE

Cette modification permet de ne changer l'image que dans la variable de lancement du pipeline.

Il nous manque juste l'authentification de notre robot sur harbor.

Rendez-vous dans l'interface de gestion des varaibles de CI/CD.

Et ajoutez une nouvelle variable.

  1. Dans le champ key indiqué la chaîne de caractères HARBOR_ROBOT_AUTH_FILE.

  2. Dans value

Remplissez la avec:

{
   "auths":{
      "harbor.example.com":{
         "username":"robot$$gitlab",
         "password":"***********************"
      }
   }
}

Modifiez harbor.example.com par le domaine de votre instance Harbor et le veillez bien à doubler le symbole dollar $$ sinon la deuxième partie sera considéré comme une variable d'environnement.

  1. Modifiez le type par File.

  2. Décochez le Protect variable

  3. Ajoutez la variable.

C'est bon on est prêt à tester notre pipeline 😄

# Tests des images

Pour lancer un pipeline de construction et d'upload de notre image, vous devez vous rendre dans la section CI/CD -> Pipelines de notre projet Gitlab.

Puis

  1. Modifier la branche utiliser comme source
  2. Ajouter une varible appelée IMAGE
  3. Ajouter un nom d'image à construire ici par exemple: busybox:latest
  4. Lancer votre pipeline

Après quelques instants vous pouvez retourner sur Harbor dans le projet library.

Vous y trouverez une image busybox, et comme vous le constatez 0 vulnérabilté à constater.

# ubuntu:21.04

Réitérez les steps du dessus avec comme l'image ubuntu:21.04 comme valeur de IMAGE

Comme pour les checks Gitlab, aucune vulnérabilité à signaler.

# ubuntu:20.04

On essaie avec une ubuntu:20.04.

Cette fois, pour l'image ubuntu:20.04 on voit un M jaune. Cela signifie que l'image possède des vulnérabilités de sévérité modérées.

En cliquant sur le sha1 de l'ubuntu:20.04 on peut visualiser les détails des vulnérabilités.

Comme on peut le voir, chaque vulnérabilité possède sa CVE et donc le détail du problème de sécurité.

Avec la flèche on peut même bénéficier d'un abstract du problème.

# tomcat:10.0-jdk11-openjdk

Allons y pour notre image trouée la tomcat:10.0-jdk11-openjdk

Elle nous donne le résultat suivant

Et les détails sont encore plus alarmants... 😨

Le résultat est clair, il ne faut pas utiliser sous aucun prétexte cette image en production! 😡

# tomcat:10.0-jdk11-corretto

Réparons tout ça avec la tomcat:10.0-jdk11-corretto

Ouf 😄

Sur Harbor il existe tout un système de Harbor de webhooks, je planche actuellement sur une intégration Mattermost pour m'avertir lorsqu'une vulnérabilité apparaît sur une image.

Je ferai un article sur le sujet quand j'aurai un peu plus avancé ^^

# Conclusion

Cet article devait être court et ce ne fut pas le cas 😁

Ce que je vous est montré aujourd'hui je l'utilise désormais tous les jours.

Dans un prochain article sur la sécurité, je vous expliquerai comment créer des images extrêmement légères et donc avec une surface d'attaque extrêmement réduite.

Cette méthode s'appelle le multi-stage building et l'utilisation de scratch comme base.

Je vous remercie de m'avoir lu et je vous dis à la prochaine 😃