Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker

Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker
Hé Habr !

Dans la réalité d'aujourd'hui, en raison du rôle croissant de la conteneurisation dans les processus de développement, la question d'assurer la sécurité des différentes étapes et entités associées aux conteneurs n'est pas la dernière. Effectuer des contrôles manuellement est une tâche laborieuse, il serait donc bien de prendre au moins les premières étapes vers l'automatisation de ce processus.

Dans cet article, je partagerai des scripts prêts à l'emploi pour implémenter plusieurs utilitaires de sécurité Docker et des instructions sur la façon de configurer un petit stand de démonstration pour tester ce processus. Vous pouvez utiliser le matériel pour expérimenter comment organiser le processus de test de la sécurité des images et des instructions Dockerfile. Il est clair que l'infrastructure de développement et de mise en œuvre est différente pour chacun, je vais donc donner ci-dessous plusieurs options possibles.

Utilitaires de contrôle de sécurité

Il existe un grand nombre d'applications d'assistance et de scripts différents qui effectuent des vérifications sur divers aspects de l'infrastructure Docker. Certains d'entre eux ont déjà été décrits dans un article précédent (https://habr.com/ru/company/swordfish_security/blog/518758/#docker-security), et dans cet article, je voudrais me concentrer sur trois d'entre eux, qui couvrent l'essentiel des exigences de sécurité pour les images Docker créées au cours du processus de développement. De plus, je montrerai également un exemple de la façon dont ces trois utilitaires peuvent être combinés en un seul pipeline pour effectuer des contrôles de sécurité.

Hadolint
https://github.com/hadolint/hadolint

Un utilitaire de console assez simple qui permet d'évaluer d'abord l'exactitude et la sécurité des instructions Dockerfile (par exemple, en utilisant uniquement les registres d'images autorisés ou en utilisant sudo).

Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker

Quille
https://github.com/goodwithtech/dockle

Un utilitaire de console qui fonctionne sur une image (ou sur une archive tar d'image enregistrée) qui vérifie l'exactitude et la sécurité d'une image particulière en tant que telle en analysant ses couches et sa configuration - quels utilisateurs sont créés, quelles instructions sont utilisées, quels volumes sont montés , la présence d'un mot de passe vide, etc. E. Alors que le nombre de contrôles n'est pas très important et est basé sur plusieurs propres contrôles et recommandations Benchmark CIS (Centre pour la sécurité Internet) pour docker.
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker

Anecdote
https://github.com/aquasecurity/trivy

Cet utilitaire vise à détecter deux types de vulnérabilités : les problèmes de construction du système d'exploitation (Alpine, RedHat (EL), CentOS, Debian GNU, Ubuntu sont pris en charge) et les problèmes de dépendance (Gemfile.lock, Pipfile.lock, composer.lock, package-lock). .json , fil.lock, Cargo.lock). Trivy peut analyser à la fois l'image dans le référentiel et l'image locale, et également analyser en fonction du fichier .tar transféré avec l'image Docker.

Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker

Options de mise en œuvre des services publics

Afin de tester les applications décrites dans des conditions isolées, je fournirai des instructions pour installer tous les utilitaires dans le cadre d'un processus simplifié.

L'idée principale est de démontrer comment vous pouvez implémenter la vérification automatique du contenu pour les fichiers Docker et les images Docker créées pendant le développement.

La vérification elle-même comprend les étapes suivantes :

  1. Vérifier l'exactitude et la sécurité des instructions Dockerfile avec un utilitaire linter Hadolint
  2. Vérification de l'exactitude et de la sécurité des images finales et intermédiaires - un utilitaire Quille
  3. Vérification des vulnérabilités communément connues (CVE) dans l'image de base et un certain nombre de dépendances - par l'utilitaire Anecdote

Plus loin dans l'article, je donnerai trois options pour mettre en œuvre ces étapes :
La première consiste à configurer le pipeline CI/CD en utilisant l'exemple de GitLab (avec une description du processus de création d'une instance de test).
La seconde utilise un script shell.
La troisième consiste à créer une image Docker pour analyser les images Docker.
Vous pouvez choisir l’option qui vous convient le mieux, la transférer sur votre infrastructure et l’adapter à vos besoins.

Tous les fichiers nécessaires et instructions supplémentaires se trouvent également dans le référentiel : https://github.com/Swordfish-Security/docker_cicd

Intégration GitLab CI/CD

Dans la première option, nous examinerons comment des contrôles de sécurité peuvent être mis en œuvre en utilisant le système de référentiel GitLab comme exemple. Ici, nous allons passer en revue les étapes et voir comment configurer un environnement de test avec GitLab à partir de zéro, créer un processus d'analyse et exécuter des utilitaires pour tester un Dockerfile de test et une image aléatoire - l'application JuiceShop.

Installer GitLab
1. Installez Docker :

sudo apt-get update && sudo apt-get install docker.io

2. Ajoutez l'utilisateur actuel au groupe docker afin de pouvoir travailler avec docker sans utiliser sudo :

sudo addgroup <username> docker

3. Trouvez votre IP :

ip addr

4. Installez et lancez GitLab dans le conteneur, en remplaçant l'adresse IP du nom d'hôte par la vôtre :

docker run --detach 
--hostname 192.168.1.112 
--publish 443:443 --publish 80:80 
--name gitlab 
--restart always 
--volume /srv/gitlab/config:/etc/gitlab 
--volume /srv/gitlab/logs:/var/log/gitlab 
--volume /srv/gitlab/data:/var/opt/gitlab 
gitlab/gitlab-ce:latest

Nous attendons que GitLab termine toutes les procédures d'installation nécessaires (vous pouvez suivre le processus jusqu'à la sortie du fichier journal : docker logs -f gitlab).

5. Ouvrez votre adresse IP locale dans le navigateur et voyez une page proposant de changer le mot de passe de l'utilisateur root :
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker
Définissez un nouveau mot de passe et accédez à GitLab.

6. Créez un nouveau projet, par exemple cicd-test et initialisez-le avec un fichier de démarrage README.md:
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker
7. Nous devons maintenant installer GitLab Runner : un agent qui exécutera toutes les opérations nécessaires sur demande.
Téléchargez la dernière version (dans ce cas, sous Linux 64 bits) :

sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

8. Rendez-le exécutable :

sudo chmod +x /usr/local/bin/gitlab-runner

9. Ajoutez un utilisateur du système d'exploitation pour Runner et démarrez le service :

sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start

Ça devrait ressembler a quelque chose comme ca:

local@osboxes:~$ sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
Runtime platform arch=amd64 os=linux pid=8438 revision=0e5417a3 version=12.0.1
local@osboxes:~$ sudo gitlab-runner start
Runtime platform arch=amd64 os=linux pid=8518 revision=0e5417a3 version=12.0.1

10. Maintenant, nous enregistrons le Runner afin qu'il puisse interagir avec notre instance GitLab.
Pour ce faire, ouvrez la page Paramètres-CI/CD (http://OUR_IP_ADDRESS/root/cicd-test/-/settings/ci_cd) et dans l'onglet Runners recherchez l'URL et le jeton d'enregistrement :
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker
11. Enregistrez le Runner en remplaçant l'URL et le jeton d'enregistrement :

sudo gitlab-runner register 
--non-interactive 
--url "http://<URL>/" 
--registration-token "<Registration Token>" 
--executor "docker" 
--docker-privileged 
--docker-image alpine:latest 
--description "docker-runner" 
--tag-list "docker,privileged" 
--run-untagged="true" 
--locked="false" 
--access-level="not_protected"

En conséquence, nous obtenons un GitLab fonctionnel prêt à l'emploi, dans lequel nous devons ajouter des instructions pour démarrer nos utilitaires. Dans cette démo, nous n'avons pas d'étapes de création d'application et de conteneurisation, mais dans un environnement réel, elles précéderont les étapes d'analyse et généreront des images et un Dockerfile pour l'analyse.

configuration des pipelines

1. Ajouter des fichiers au référentiel monfichierdocker.df (il s'agit d'un Dockerfile de test que nous allons tester) et le fichier de configuration du processus GitLab CI/CD .gitlab-cicd.yml, qui répertorie les instructions pour les scanners (notez le point dans le nom du fichier).

Le fichier de configuration YAML contient des instructions pour exécuter trois utilitaires (Hadolint, Dockle et Trivy) qui analyseront le Dockerfile sélectionné et l'image spécifiée dans la variable DOCKERFILE. Tous les fichiers nécessaires peuvent être extraits du référentiel : https://github.com/Swordfish-Security/docker_cicd/

Extrait de monfichierdocker.df (il s'agit d'un fichier abstrait avec un ensemble d'instructions arbitraires juste pour démontrer le fonctionnement de l'utilitaire). Lien direct vers le fichier : monfichierdocker.df

Contenu de mondockerfile.df

FROM amd64/node:10.16.0-alpine@sha256:f59303fb3248e5d992586c76cc83e1d3700f641cbcd7c0067bc7ad5bb2e5b489 AS tsbuild
COPY package.json .
COPY yarn.lock .
RUN yarn install
COPY lib lib
COPY tsconfig.json tsconfig.json
COPY tsconfig.app.json tsconfig.app.json
RUN yarn build
FROM amd64/ubuntu:18.04@sha256:eb70667a801686f914408558660da753cde27192cd036148e58258819b927395
LABEL maintainer="Rhys Arkins <[email protected]>"
LABEL name="renovate"
...
COPY php.ini /usr/local/etc/php/php.ini
RUN cp -a /tmp/piik/* /var/www/html/
RUN rm -rf /tmp/piwik
RUN chown -R www-data /var/www/html
ADD piwik-cli-setup /piwik-cli-setup
ADD reset.php /var/www/html/
## ENTRYPOINT ##
ADD entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
USER root

La configuration YAML ressemble à ceci (le fichier lui-même peut être extrait du lien direct ici : .gitlab-ci.yml):

Contenu de .gitlab-ci.yml

variables:
    DOCKER_HOST: "tcp://docker:2375/"
    DOCKERFILE: "mydockerfile.df" # name of the Dockerfile to analyse   
    DOCKERIMAGE: "bkimminich/juice-shop" # name of the Docker image to analyse
    # DOCKERIMAGE: "knqyf263/cve-2018-11235" # test Docker image with several CRITICAL CVE
    SHOWSTOPPER_PRIORITY: "CRITICAL" # what level of criticality will fail Trivy job
    TRIVYCACHE: "$CI_PROJECT_DIR/.cache" # where to cache Trivy database of vulnerabilities for faster reuse
    ARTIFACT_FOLDER: "$CI_PROJECT_DIR"
 
services:
    - docker:dind # to be able to build docker images inside the Runner
 
stages:
    - scan
    - report
    - publish
 
HadoLint:
    # Basic lint analysis of Dockerfile instructions
    stage: scan
    image: docker:git
 
    after_script:
    - cat $ARTIFACT_FOLDER/hadolint_results.json
 
    script:
    - export VERSION=$(wget -q -O - https://api.github.com/repos/hadolint/hadolint/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/hadolint/hadolint/releases/download/v${VERSION}/hadolint-Linux-x86_64 && chmod +x hadolint-Linux-x86_64
     
    # NB: hadolint will always exit with 0 exit code
    - ./hadolint-Linux-x86_64 -f json $DOCKERFILE > $ARTIFACT_FOLDER/hadolint_results.json || exit 0
 
    artifacts:
        when: always # return artifacts even after job failure       
        paths:
        - $ARTIFACT_FOLDER/hadolint_results.json
 
Dockle:
    # Analysing best practices about docker image (users permissions, instructions followed when image was built, etc.)
    stage: scan   
    image: docker:git
 
    after_script:
    - cat $ARTIFACT_FOLDER/dockle_results.json
 
    script:
    - export VERSION=$(wget -q -O - https://api.github.com/repos/goodwithtech/dockle/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.tar.gz && tar zxf dockle_${VERSION}_Linux-64bit.tar.gz
    - ./dockle --exit-code 1 -f json --output $ARTIFACT_FOLDER/dockle_results.json $DOCKERIMAGE   
     
    artifacts:
        when: always # return artifacts even after job failure       
        paths:
        - $ARTIFACT_FOLDER/dockle_results.json
 
Trivy:
    # Analysing docker image and package dependencies against several CVE bases
    stage: scan   
    image: docker:git
 
    script:
    # getting the latest Trivy
    - apk add rpm
    - export VERSION=$(wget -q -O - https://api.github.com/repos/knqyf263/trivy/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/knqyf263/trivy/releases/download/v${VERSION}/trivy_${VERSION}_Linux-64bit.tar.gz && tar zxf trivy_${VERSION}_Linux-64bit.tar.gz
     
    # displaying all vulnerabilities w/o failing the build
    - ./trivy -d --cache-dir $TRIVYCACHE -f json -o $ARTIFACT_FOLDER/trivy_results.json --exit-code 0 $DOCKERIMAGE    
    
    # write vulnerabilities info to stdout in human readable format (reading pure json is not fun, eh?). You can remove this if you don't need this.
    - ./trivy -d --cache-dir $TRIVYCACHE --exit-code 0 $DOCKERIMAGE    
 
    # failing the build if the SHOWSTOPPER priority is found
    - ./trivy -d --cache-dir $TRIVYCACHE --exit-code 1 --severity $SHOWSTOPPER_PRIORITY --quiet $DOCKERIMAGE
         
    artifacts:
        when: always # return artifacts even after job failure
        paths:
        - $ARTIFACT_FOLDER/trivy_results.json
 
    cache:
        paths:
        - .cache
 
Report:
    # combining tools outputs into one HTML
    stage: report
    when: always
    image: python:3.5
     
    script:
    - mkdir json
    - cp $ARTIFACT_FOLDER/*.json ./json/
    - pip install json2html
    - wget https://raw.githubusercontent.com/shad0wrunner/docker_cicd/master/convert_json_results.py
    - python ./convert_json_results.py
     
    artifacts:
        paths:
        - results.html

Si nécessaire, vous pouvez également numériser les images enregistrées sous forme d'archive .tar (cependant, vous devrez modifier les paramètres d'entrée des utilitaires dans le fichier YAML)

NB : Trivy nécessite d'être installé rpm и jet. Sinon, cela générera des erreurs lors de l'analyse des images basées sur RedHat et de l'obtention des mises à jour de la base de données de vulnérabilités.

2. Après avoir ajouté des fichiers au référentiel, conformément aux instructions de notre fichier de configuration, GitLab lancera automatiquement le processus de construction et d'analyse. Dans l'onglet CI/CD → Pipelines, vous pouvez voir la progression des instructions.

En conséquence, nous avons quatre tâches. Trois d'entre eux sont directement impliqués dans l'analyse, et le dernier (Rapport) collecte un simple rapport à partir de fichiers dispersés avec les résultats de l'analyse.
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker
Par défaut, Trivy arrête son exécution si des vulnérabilités CRITIQUES sont trouvées dans l'image ou les dépendances. Dans le même temps, Hadolint renvoie toujours un code Success, puisque son exécution comporte toujours des remarques, ce qui entraîne un arrêt de la build.

En fonction de vos besoins spécifiques, vous pouvez configurer un code de sortie afin que ces utilitaires arrêtent également le processus de construction lorsque des problèmes d'une certaine criticité sont détectés. Dans notre cas, la construction ne s'arrêtera que si Trivy détecte une vulnérabilité avec une gravité que nous avons spécifiée dans la variable SHOWSTOPPER dans .gitlab-ci.yml.
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker

Le résultat du fonctionnement de chaque utilitaire peut être visualisé dans le journal de chaque tâche d'analyse, directement dans les fichiers json dans la section artefacts, ou dans un simple rapport HTML (plus d'informations ci-dessous) :
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker

3. Pour présenter les rapports d'utilitaires sous une forme légèrement plus lisible par l'homme, un petit script Python est utilisé pour convertir trois fichiers json en un seul fichier HTML avec un tableau des défauts.
Ce script est lancé par une tâche de rapport distincte et son artefact final est un fichier HTML avec un rapport. Le source du script est également dans le référentiel et peut être adapté à vos besoins, couleurs, etc.
Méthodes et exemples de mise en œuvre d'utilitaires de vérification de la sécurité Docker

Script shell

La deuxième option convient aux cas où vous devez vérifier les images Docker ne faisant pas partie du système CI/CD, ou où vous devez disposer de toutes les instructions sous une forme pouvant être exécutée directement sur l'hôte. Cette option est couverte par un script shell prêt à l'emploi qui peut être exécuté sur une machine virtuelle (ou même réelle) propre. Le script suit les mêmes instructions que le gitlab-runner ci-dessus.

Pour que le script fonctionne correctement, Docker doit être installé sur le système et l'utilisateur actuel doit appartenir au groupe Docker.

Le script lui-même peut être trouvé ici : docker_sec_check.sh

Au début du fichier, les variables spécifient quelle image doit être numérisée et quelle gravité des défauts entraînera la fermeture de l'utilitaire Trivy avec le code d'erreur spécifié.

Lors de l'exécution du script, tous les utilitaires seront téléchargés dans le répertoire docker_tools, les résultats de leur travail - dans l'annuaire docker_tools/json, et le HTML avec le rapport sera dans le fichier résultats.html.

Exemple de sortie de script

~/docker_cicd$ ./docker_sec_check.sh

[+] Setting environment variables
[+] Installing required packages
[+] Preparing necessary directories
[+] Fetching sample Dockerfile
2020-10-20 10:40:00 (45.3 MB/s) - ‘Dockerfile’ saved [8071/8071]
[+] Pulling image to scan
latest: Pulling from bkimminich/juice-shop
[+] Running Hadolint
...
Dockerfile:205 DL3015 Avoid additional packages by specifying `--no-install-recommends`
Dockerfile:248 DL3002 Last USER should not be root
...
[+] Running Dockle
...
WARN    - DKL-DI-0006: Avoid latest tag
        * Avoid 'latest' tag
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
...
[+] Running Trivy
juice-shop/frontend/package-lock.json
=====================================
Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 2, CRITICAL: 0)

+---------------------+------------------+----------+---------+-------------------------+
|       LIBRARY       | VULNERABILITY ID | SEVERITY | VERSION |             TITLE       |
+---------------------+------------------+----------+---------+-------------------------+
| object-path         | CVE-2020-15256   | HIGH     | 0.11.4  | Prototype pollution in  |
|                     |                  |          |         | object-path             |
+---------------------+------------------+          +---------+-------------------------+
| tree-kill           | CVE-2019-15599   |          | 1.2.2   | Code Injection          |
+---------------------+------------------+----------+---------+-------------------------+
| webpack-subresource | CVE-2020-15262   | LOW      | 1.4.1   | Unprotected dynamically |
|                     |                  |          |         | loaded chunks           |
+---------------------+------------------+----------+---------+-------------------------+

juice-shop/package-lock.json
============================
Total: 20 (UNKNOWN: 0, LOW: 1, MEDIUM: 6, HIGH: 8, CRITICAL: 5)

...

juice-shop/package-lock.json
============================
Total: 5 (CRITICAL: 5)

...
[+] Removing left-overs
[+] Making the output look pretty
[+] Converting JSON results
[+] Writing results HTML
[+] Clean exit ============================================================
[+] Everything is done. Find the resulting HTML report in results.html

Image Docker avec tous les utilitaires

Comme troisième alternative, j'ai compilé deux fichiers Docker simples pour créer une image avec des utilitaires de sécurité. Un Dockerfile aidera à créer un ensemble pour analyser l'image à partir du référentiel, le second (Dockerfile_tar) créera un ensemble pour analyser le fichier tar avec l'image.

1. Nous prenons le fichier Docker et les scripts appropriés du référentiel https://github.com/Swordfish-Security/docker_cicd/tree/master/Dockerfile.
2. Exécutez-le pour l'assemblage :

docker build -t dscan:image -f docker_security.df .

3. Une fois l'assemblage terminé, nous créons un conteneur à partir de l'image. En même temps, nous passons la variable d'environnement DOCKERIMAGE avec le nom de l'image qui nous intéresse et montons le Dockerfile que nous voulons analyser depuis notre machine vers le fichier /Fichier Docker (notez qu'un chemin absolu vers ce fichier est requis) :

docker run --rm -v $(pwd)/results:/results -v $(pwd)/docker_security.df:/Dockerfile -e DOCKERIMAGE="bkimminich/juice-shop" dscan:image


[+] Setting environment variables
[+] Running Hadolint
/Dockerfile:3 DL3006 Always tag the version of an image explicitly
[+] Running Dockle
WARN    - DKL-DI-0006: Avoid latest tag
        * Avoid 'latest' tag
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : juice-shop/node_modules/sqlite3/Dockerfile
        * unnecessary file : juice-shop/node_modules/sqlite3/tools/docker/architecture/linux-arm64/Dockerfile
        * unnecessary file : juice-shop/node_modules/sqlite3/tools/docker/architecture/linux-arm/Dockerfile
[+] Running Trivy
...
juice-shop/package-lock.json
============================
Total: 20 (UNKNOWN: 0, LOW: 1, MEDIUM: 6, HIGH: 8, CRITICAL: 5)
...
[+] Making the output look pretty
[+] Starting the main module ============================================================
[+] Converting JSON results
[+] Writing results HTML
[+] Clean exit ============================================================
[+] Everything is done. Find the resulting HTML report in results.html

résultats

Nous n'avons couvert qu'un seul ensemble de base d'utilitaires d'analyse d'artefacts Docker, qui, je pense, couvre assez efficacement une bonne partie des exigences de sécurité des images. Il existe de nombreux autres outils payants et gratuits qui peuvent effectuer les mêmes vérifications, créer de beaux rapports ou fonctionner uniquement en mode console, couvrir les systèmes de gestion de conteneurs, etc. Un aperçu de ces outils et de la manière de les intégrer apparaîtra peut-être un peu plus tard.

Le côté positif de l'ensemble d'outils décrits dans l'article est qu'ils sont tous construits sur une source open source et que vous pouvez les expérimenter ainsi que d'autres outils similaires pour trouver ce qui correspond exactement à vos besoins et aux fonctionnalités de votre infrastructure. Bien entendu, toutes les vulnérabilités découvertes doivent être étudiées pour déterminer leur applicabilité dans des conditions spécifiques, mais il s’agit d’un sujet pour un prochain article volumineux.

J'espère que ces instructions, scripts et utilitaires vous aideront et deviendront un point de départ pour créer une infrastructure plus sécurisée dans le domaine de la conteneurisation.

Source: habr.com

Ajouter un commentaire