Hey Habr!
In der heutigen Realität steht die Frage der Gewährleistung der Sicherheit verschiedener Phasen und Einheiten im Zusammenhang mit Containern aufgrund der zunehmenden Rolle der Containerisierung in Entwicklungsprozessen nicht an letzter Stelle. Die manuelle Durchführung von Prüfungen ist eine mühsame Aufgabe, daher wäre es schön, zumindest die ersten Schritte zur Automatisierung dieses Prozesses zu unternehmen.
In diesem Artikel werde ich vorgefertigte Skripte zur Implementierung mehrerer Docker-Sicherheitsdienstprogramme und Anweisungen zum Einrichten eines kleinen Demostands zum Testen dieses Prozesses vorstellen. Mithilfe der Materialien können Sie experimentieren, wie Sie den Prozess zum Testen der Sicherheit von Dockerfile-Bildern und -Anweisungen organisieren. Es ist klar, dass die Entwicklungs- und Implementierungsinfrastruktur für jeden unterschiedlich ist, daher werde ich im Folgenden mehrere mögliche Optionen nennen.
Dienstprogramme zur Sicherheitsüberprüfung
Es gibt eine große Anzahl verschiedener Hilfsanwendungen und Skripte, die Prüfungen verschiedener Aspekte der Docker-Infrastruktur durchführen. Einige davon wurden bereits in einem früheren Artikel beschrieben (
Hadolint
Ein recht einfaches Konsolendienstprogramm, das dabei hilft, zunächst die Richtigkeit und Sicherheit von Dockerfile-Anweisungen zu beurteilen (z. B. nur zulässige Image-Register verwenden oder sudo verwenden).
Dockle
Ein Konsolendienstprogramm, das auf einem Image (oder einem gespeicherten Image-Tarball) arbeitet und die Richtigkeit und Sicherheit eines bestimmten Images als solches überprüft, indem es seine Ebenen und Konfiguration analysiert – welche Benutzer erstellt werden, welche Anweisungen verwendet werden, welche Volumes gemountet werden , das Vorhandensein eines leeren Passworts usw. e. Die Anzahl der Überprüfungen ist zwar nicht sehr groß und basiert auf mehreren eigenen Überprüfungen und Empfehlungen
Wissenswertes
Dieses Dienstprogramm zielt darauf ab, zwei Arten von Schwachstellen zu finden: Betriebssystem-Build-Probleme (Alpine, RedHat (EL), CentOS, Debian GNU, Ubuntu werden unterstützt) und Abhängigkeitsprobleme (Gemfile.lock, Pipfile.lock, Composer.lock, Package-Lock). .json, Yarn.lock, Cargo.lock). Trivy kann sowohl das Image im Repository als auch das lokale Image scannen und außerdem basierend auf der übertragenen .tar-Datei mit dem Docker-Image scannen.
Implementierungsoptionen für Dienstprogramme
Um die beschriebenen Anwendungen unter isolierten Bedingungen auszuprobieren, stelle ich Anweisungen zur Installation aller Dienstprogramme im Rahmen eines vereinfachten Prozesses zur Verfügung.
Die Hauptidee besteht darin, zu demonstrieren, wie Sie eine automatische Inhaltsprüfung für Dockerfiles und Docker-Images implementieren können, die während der Entwicklung erstellt werden.
Die Verifizierung selbst besteht aus folgenden Schritten:
- Überprüfen der Richtigkeit und Sicherheit von Dockerfile-Anweisungen mit einem Linter-Dienstprogramm Hadolint
- Überprüfung der Richtigkeit und Sicherheit der endgültigen und Zwischenbilder – ein Dienstprogramm Dockle
- Überprüfung auf allgemein bekannte Schwachstellen (CVE) im Basis-Image und einer Reihe von Abhängigkeiten – durch das Dienstprogramm Wissenswertes
Später in diesem Artikel werde ich drei Optionen für die Umsetzung dieser Schritte nennen:
Die erste besteht darin, die CI/CD-Pipeline am Beispiel von GitLab zu konfigurieren (mit einer Beschreibung des Prozesses zum Erstellen einer Testinstanz).
Die zweite Möglichkeit besteht darin, ein Shell-Skript zu verwenden.
Der dritte Schritt besteht darin, ein Docker-Image zu erstellen, um Docker-Images zu scannen.
Sie können die Option auswählen, die am besten zu Ihnen passt, diese auf Ihre Infrastruktur übertragen und an Ihre Bedürfnisse anpassen.
Alle notwendigen Dateien und zusätzliche Anweisungen befinden sich ebenfalls im Repository:
GitLab CI/CD-Integration
In der ersten Option schauen wir uns am Beispiel des GitLab-Repository-Systems an, wie Sicherheitsprüfungen umgesetzt werden können. Hier gehen wir die Schritte durch und sehen, wie man eine Testumgebung mit GitLab von Grund auf einrichtet, einen Scan-Prozess erstellt und Dienstprogramme ausführt, um eine Test-Docker-Datei und ein zufälliges Image – die JuiceShop-Anwendung – zu testen.
GitLab installieren
1. Docker installieren:
sudo apt-get update && sudo apt-get install docker.io
2. Fügen Sie den aktuellen Benutzer zur Docker-Gruppe hinzu, damit Sie mit Docker arbeiten können, ohne sudo zu verwenden:
sudo addgroup <username> docker
3. Finden Sie Ihre IP:
ip addr
4. Installieren Sie GitLab und führen Sie es im Container aus. Ersetzen Sie dabei die IP-Adresse im Hostnamen durch Ihre eigene:
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
Wir warten darauf, dass GitLab alle erforderlichen Installationsvorgänge abschließt (Sie können den Vorgang anhand der Ausgabe der Protokolldatei verfolgen: docker logs -f gitlab).
5. Öffnen Sie Ihre lokale IP im Browser und sehen Sie eine Seite, auf der Sie das Passwort für den Root-Benutzer ändern können:
Legen Sie ein neues Passwort fest und gehen Sie zu GitLab.
6. Erstellen Sie ein neues Projekt, zum Beispiel cicd-test, und initialisieren Sie es mit einer Startdatei README.md:
7. Jetzt müssen wir den GitLab Runner installieren: einen Agenten, der auf Anfrage alle notwendigen Vorgänge ausführt.
Laden Sie die neueste Version herunter (in diesem Fall unter Linux 64-Bit):
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
8. Machen Sie es ausführbar:
sudo chmod +x /usr/local/bin/gitlab-runner
9. Fügen Sie einen Betriebssystembenutzer für den Runner hinzu und starten Sie den Dienst:
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
Es sollte ungefähr so aussehen:
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. Jetzt registrieren wir den Runner, damit er mit unserer GitLab-Instanz interagieren kann.
Öffnen Sie dazu die Seite „Einstellungen-CI/CD“ (http://OUR_ IP_ADDRESS/root/cicd-test/-/settings/ci_cd) und suchen Sie auf der Registerkarte „Läufer“ die URL und das Registrierungstoken:
11. Registrieren Sie den Runner, indem Sie die URL und das Registrierungstoken ersetzen:
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"
Als Ergebnis erhalten wir ein fertiges, funktionierendes GitLab, in dem wir Anweisungen zum Starten unserer Dienstprogramme hinzufügen müssen. In dieser Demo haben wir keine Anwendungserstellungs- und Containerisierungsschritte, aber in einer realen Umgebung gehen sie den Scanschritten voraus und generieren Bilder und eine Docker-Datei zur Analyse.
Pipeline-Konfiguration
1. Dateien zum Repository hinzufügen mydockerfile.df (Dies ist eine Test-Docker-Datei, die wir testen werden) und die GitLab CI/CD-Prozesskonfigurationsdatei .gitlab-cicd.yml, die Anweisungen für Scanner auflistet (beachten Sie den Punkt im Dateinamen).
Die .yaml-Konfigurationsdatei enthält Anweisungen zum Ausführen von drei Dienstprogrammen (Hadolint, Dockle und Trivy), die die ausgewählte Docker-Datei und das in der DOCKERFILE-Variablen angegebene Bild analysieren. Alle notwendigen Dateien können dem Repository entnommen werden:
Auszug aus mydockerfile.df (Dies ist eine abstrakte Datei mit einer Reihe willkürlicher Anweisungen, um zu veranschaulichen, wie das Dienstprogramm funktioniert.) Direktlink zur Datei:
Inhalt von mydockerfile.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
Das Konfigurations-YAML sieht so aus (die Datei selbst kann dem Direktlink hier entnommen werden:
Inhalt von .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
Bei Bedarf können Sie gespeicherte Bilder auch als .tar-Archiv scannen (allerdings müssen Sie die Eingabeparameter für die Dienstprogramme in der YAML-Datei ändern)
Hinweis: Trivy muss installiert sein rpm и git. Andernfalls werden beim Scannen von RedHat-basierten Bildern und beim Abrufen von Aktualisierungen der Schwachstellendatenbank Fehler generiert.
2. Nachdem Sie die Dateien gemäß den Anweisungen in unserer Konfigurationsdatei zum Repository hinzugefügt haben, startet GitLab automatisch den Erstellungs- und Scanvorgang. Auf der Registerkarte CI/CD → Pipelines können Sie den Fortschritt der Anweisungen sehen.
Daraus ergeben sich vier Aufgaben. Drei davon sind direkt am Scannen beteiligt, und der letzte (Bericht) sammelt einen einfachen Bericht aus verstreuten Dateien mit Scanergebnissen.
Standardmäßig stoppt Trivy seine Ausführung, wenn im Image oder in den Abhängigkeiten KRITISCHE Schwachstellen gefunden werden. Gleichzeitig gibt Hadolint immer einen Erfolgscode zurück, da seine Ausführung immer Bemerkungen enthält, was zu einem Build-Stopp führt.
Abhängig von Ihren spezifischen Anforderungen können Sie einen Exit-Code konfigurieren, damit diese Dienstprogramme den Build-Prozess auch stoppen, wenn Probleme einer bestimmten Kritikalität erkannt werden. In unserem Fall stoppt der Build nur, wenn Trivy eine Schwachstelle mit einem Schweregrad erkennt, den wir in der Variablen SHOWSTOPPER in angegeben haben .gitlab-ci.yml.
Das Ergebnis des Betriebs jedes Dienstprogramms kann im Protokoll jeder Scanaufgabe, direkt in JSON-Dateien im Abschnitt „Artefakte“ oder in einem einfachen HTML-Bericht angezeigt werden (mehr dazu weiter unten):
3. Um Versorgungsberichte in einer etwas besser lesbaren Form darzustellen, wird ein kleines Python-Skript verwendet, um drei JSON-Dateien in eine HTML-Datei mit einer Fehlertabelle zu konvertieren.
Dieses Skript wird von einer separaten Berichtsaufgabe gestartet und sein endgültiges Artefakt ist eine HTML-Datei mit einem Bericht. Die Skriptquelle befindet sich ebenfalls im Repository und kann an Ihre Bedürfnisse, Farben usw. angepasst werden.
Shell-Skript
Die zweite Option eignet sich für Fälle, in denen Sie Docker-Images überprüfen müssen, die sich nicht im CI/CD-System befinden, oder Sie alle Anweisungen in einer Form benötigen, die direkt auf dem Host ausgeführt werden kann. Diese Option wird durch ein vorgefertigtes Shell-Skript abgedeckt, das auf einer sauberen virtuellen (oder sogar realen) Maschine ausgeführt werden kann. Das Skript folgt den gleichen Anweisungen wie der obige Gitlab-Runner.
Damit das Skript erfolgreich funktioniert, muss Docker auf dem System installiert sein und der aktuelle Benutzer muss in der Docker-Gruppe sein.
Das Skript selbst finden Sie hier:
Am Anfang der Datei geben Variablen an, welches Bild gescannt werden soll und welcher Schweregrad der Fehler dazu führt, dass das Trivy-Dienstprogramm mit dem angegebenen Fehlercode beendet wird.
Während der Skriptausführung werden alle Dienstprogramme in das Verzeichnis heruntergeladen docker_tools, die Ergebnisse ihrer Arbeit - im Verzeichnis docker_tools/json, und der HTML-Code mit dem Bericht befindet sich in der Datei Ergebnisse.html.
Beispiel-Skriptausgabe
~/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
Docker-Image mit allen Dienstprogrammen
Als dritte Alternative habe ich zwei einfache Docker-Dateien kompiliert, um ein Image mit Sicherheitsdienstprogrammen zu erstellen. Eine Docker-Datei hilft beim Erstellen eines Satzes zum Scannen des Bildes aus dem Repository, die zweite (Dockerfile_tar) erstellt einen Satz zum Scannen der TAR-Datei mit dem Bild.
1. Wir nehmen die entsprechende Docker-Datei und die entsprechenden Skripte aus dem Repository
2. Führen Sie es zur Montage aus:
docker build -t dscan:image -f docker_security.df .
3. Nachdem der Build abgeschlossen ist, erstellen Sie einen Container aus dem Image. Gleichzeitig übergeben wir die Umgebungsvariable DOCKERIMAGE mit dem Namen des Bildes, an dem wir interessiert sind, und mounten die Docker-Datei, die wir von unserem Computer aus analysieren möchten, in die Datei /dockerfile (Beachten Sie, dass ein absoluter Pfad zu dieser Datei erforderlich ist):
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
Ergebnisse
Wir haben nur einen grundlegenden Satz von Docker-Tools zum Scannen von Artefakten behandelt, der meiner Meinung nach eine ganze Reihe von Bildsicherheitsanforderungen recht effektiv abdeckt. Es gibt viele andere kostenpflichtige und kostenlose Tools, die die gleichen Prüfungen durchführen, schöne Berichte erstellen oder rein im Konsolenmodus arbeiten, Container-Management-Systeme abdecken usw. Eine Übersicht über diese Tools und deren Integration folgt möglicherweise etwas später.
Der positive Aspekt der im Artikel beschriebenen Tools ist, dass sie alle auf Open Source basieren und Sie mit ihnen und anderen ähnlichen Tools experimentieren können, um herauszufinden, was genau Ihren Anforderungen und Infrastrukturfunktionen entspricht. Natürlich sollten alle gefundenen Schwachstellen auf ihre Anwendbarkeit unter bestimmten Bedingungen untersucht werden, aber dies ist ein Thema für einen zukünftigen großen Artikel.
Ich hoffe, dass diese Anweisungen, Skripte und Dienstprogramme Ihnen helfen und ein Ausgangspunkt für die Schaffung einer sichereren Infrastruktur im Bereich der Containerisierung werden.
Source: habr.com