
Hello, Habr!
V današnji realnosti, zaradi vse bolj razširjene vloge konteinirizacije v razvojnem procesu, je vprašanje zagotavljanja varnosti različnih stopenj in entitet, povezanih s kontejnerji, izjemno pomembno. Ročno izvajanje preverjanj je zamudno opravilo, zato bi bilo dobro narediti vsaj prve korake k avtomatizaciji tega procesa.
V tem članku bom delil pripravljene skripte za namestitev več varnostnih orodij za Docker in navodila, kako postaviti majhno demo testno okolje za preizkušanje tega procesa. Gradiva lahko uporabite, da preizkusite, kako organizirati postopek testiranja varnosti slik in navodil Dockerfile. Jasno je, da je infrastruktura razvoja in uvajanja pri vsakem drugačna, zato bom spodaj navedel nekaj možnih možnosti.
Varnostna orodja za preverjanje
Obstaja veliko različnih pomožnih aplikacij in skript, ki izvajajo preverjanja različnih vidikov Docker infrastrukture. Nekatere od njih so bile že opisane v prejšnjem članku (), v tem gradivu pa se želim osredotočiti na tri, ki pokrivajo večino zahtev glede varnosti Docker slik, ki jih ustvarimo v postopku razvoja. Poleg tega bom pokazal primer, kako lahko te tri naprave povežemo v en pipeline za izvajanje preverjanja varnosti.
Hadolint
Relativno preprosto konzolno orodje, ki pomaga pri začetni oceni pravilnosti in varnosti navodil v Dockerfile-ih (na primer uporaba samo dovoljenih registrov slik ali uporaba sudo).

Dockle
Konzolno orodje, ki deluje s sliko (ali s shranjenim tar-arhivom slike), ki preverja pravilnost in varnost konkretne slike kot take, analizira njene plasti in konfiguracijo – kakšni uporabniki so ustvarjeni, katere ukaze se uporabljajo, kateri volumni so povezani, prisotnost praznega gesla itd. Število preverjanj ni še zelo veliko in temelji na nekaj lastnih preverjanjih in priporočilih. za Docker.

Trivy
Ta orodje je namenjeno odkrivanju ranljivosti dveh vrst - težave z zbirkami operacijskih sistemov (podprti so Alpine, RedHat (EL), CentOS, Debian GNU, Ubuntu) in težave odvisnosti (Gemfile.lock, Pipfile.lock, composer.lock, package-lock.json, yarn.lock, Cargo.lock). Trivy zna skenirati tako podobe v repozitoriju kot lokalne podobe ter izvajati skeniranje na podlagi posredovanega .tar datoteke z Docker-podobo.

Možnosti integracije orodij
Da bi preizkusili opisana orodja v izoliranih pogojih, bom predstavil navodila za namestitev vseh orodij v okviru poenostavljenega postopka.
Osnovna ideja je prikazati, kako lahko uvedemo avtomatsko preverjanje vsebine Dockerfile in Docker-podob, ki se ustvarjajo v procesu razvoja.
Samo preverjanje je sestavljeno iz naslednjih korakov:
- Preverjanje pravilnosti in varnosti navodil Dockerfile - s pomočjo orodja linter Hadolint
- Preverjanje pravilnosti in varnosti končnih ter začasnih podob - s pomočjo orodja Dockle
- Preverjanje prisotnosti javno znanih ranljivosti (CVE) v osnovni podobi in nekaterih odvisnostih - s pomočjo orodja Trivy
Kasneje v članku bom predstavil tri možnosti za uvedbo teh korakov:
Prva - z konfiguracijo CI/CD pipeline na primeru GitLab (z opisom procesa vzpostavitve testne instance).
Druga - z uporabo shell-skripte.
Tretja - z gradnjo Docker-podobe za skeniranje Docker-podob.
Lahko izberete možnost, ki vam najbolj ustreza, jo prenesete na svojo infrastrukturo in prilagodite svojim potrebam.
Vsi potrebni dokumenti in dodatna navodila so prav tako na voljo v repozitoriju:
Integracija v GitLab CI/CD
V prvi možnost bomo raziskali, kako lahko uvedemo varnostne preglede na primeru sistema repozitorijev GitLab. Tu bomo prešli skozi korake in razložili, kako nastaviti testno okolje z GitLab, sestaviti postopek skeniranja in izvesti zagon orodij za preverjanje testnega Dockerfile in naključne slike - aplikacije JuiceShop.
Namestitev GitLab
1. Namestite Docker:
sudo apt-get update && sudo apt-get install docker.io2. Dodajte trenutnega uporabnika v skupino docker, da lahko delate z dockerjem brez sudo:
sudo addgroup docker3. Ugotovite svoj IP:
ip addr4. Namestite in zaženite GitLab v kontejnerju, pri čemer zamenjajte IP naslov v hostname z vašim:
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Čakamo, da GitLab izvede vse potrebne postopke za namestitev (postopek lahko spremljate preko izhoda log datoteke: docker logs -f gitlab).
5. V brskalniku odprite svoj lokalni IP in vidite stran, ki ponuja, da spremenite geslo za uporabnika root:

Določite novo geslo in se prijavite v GitLab.
6. Ustvarite nov projekt, na primer cicd-test, in ga inicializirajte s začetno datoteko README.md:

7. Zdaj moramo namestiti GitLab Runner: agenta, ki bo po potrebi izvajal vse potrebne operacije.
Prenesite zadnjo različico (v tem primeru - za Linux 64-bit):
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd648. Naredite ga izvršljivega:
sudo chmod +x /usr/local/bin/gitlab-runner9. Dodajte uporabnika OS za Runner in zaženite storitev:
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 startRezultat bi moral izgledati približno takole:
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. Zdaj registrirajte Runner, da lahko komunicira z našim instancom GitLab.
Za to odprite stran Nastavitve-CI/CD (http://OUR_ IP_ADDRESS/root/cicd-test/-/settings/ci_cd) in na zavihku Runners najdite URL in registracijski token:

11. Registrirajte Runner, tako da namestite URL in registracijski token:
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"Na koncu dobimo delujoč GitLab, v katerega je treba dodati navodila za zagon naših orodij. V tem prikaznem primeru nimamo korakov za gradnjo aplikacije in njeno kontejnerizacijo, vendar bodo v resničnem okolju ti koraki predhodili korakom skeniranja in oblikovali slike in Dockerfile za analizo.
Konfiguracija pipeline
1. Dodajte v repozitorij datoteke mydockerfile.df (to je nekakšen testni Dockerfile, ki ga bomo preverjali) in konfiguracijsko datoteko GitLab CI/CD procesa .gitlab-cicd.yml, ki navaja navodila za skenerje (pozornost na piko v imenu datoteke).
Datoteka YAML konfiguracije vsebuje navodila za zagon treh orodij (Hadolint, Dockle in Trivy), ki bodo analizirala izbrani Dockerfile in sliko, določeno v spremenljivki DOCKERFILE. Vse potrebne datoteke lahko dobite iz repozitorija:
Izvleček iz mydockerfile.df (to je abstraktna datoteka z nizom poljubnih navodil, namenjena le za prikaz delovanja orodja). Neposredna povezava do datoteke:
Vsebina 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 "
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 rootKonfiguracijski YAML izgleda takole (datoteko lahko dobite po neposredni povezavi tukaj: ):
Vsebina .gitlab-ci.yml
variables:
DOCKER_HOST: "tcp://docker:2375/"
DOCKERFILE: "mydockerfile.df" # название Dockerfile для анализа
DOCKERIMAGE: "bkimminich/juice-shop" # название Docker-образа для анализа
# DOCKERIMAGE: "knqyf263/cve-2018-11235" # тестовый Docker-образ с несколькими КРИТИЧЕСКИМИ CVE
SHOWSTOPPER_PRIORITY: "CRITICAL" # уровень критичности, при котором завершится работа Trivy
TRIVYCACHE: "$CI_PROJECT_DIR/.cache" # место для кеширования базы данных Trivy уязвимостей для более быстрого повторного использования
ARTIFACT_FOLDER: "$CI_PROJECT_DIR"
services:
- docker:dind # чтобы иметь возможность создавать Docker-образы внутри Runner
stages:
- scan
- report
- publish
HadoLint:
# Базовый анализ Dockerfile
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 всегда завершится с кодом 0
- ./hadolint-Linux-x86_64 -f json $DOCKERFILE > $ARTIFACT_FOLDER/hadolint_results.json || exit 0
artifacts:
when: always # возвращать артефакты даже после сбоя задания
paths:
- $ARTIFACT_FOLDER/hadolint_results.json
Dockle:
# Анализ лучших практик в отношении Docker-образов (права пользователей, соблюдение инструкций при создании образа и т. д.)
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 # возвращать артефакты даже после сбоя задания
paths:
- $ARTIFACT_FOLDER/dockle_results.json
Trivy:
# Анализ Docker-образа и зависимостей пакетов по нескольким базам CVE
stage: scan
image: docker:git
script:
# получение последней версии 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
# отображение всех уязвимостей без завершения сборки
- ./trivy -d --cache-dir $TRIVYCACHE -f json -o $ARTIFACT_FOLDER/trivy_results.json --exit-code 0 $DOCKERIMAGE
# вывод информации об уязвимостях в человекопонятном формате (чтение чистого json не увлекательно, не так ли?). Это можно удалить, если не нужно.
- ./trivy -d --cache-dir $TRIVYCACHE --exit-code 0 $DOCKERIMAGE
# завершение сборки, если найдены уязвимости с приоритетом SHOWSTOPPER
- ./trivy -d --cache-dir $TRIVYCACHE --exit-code 1 --severity $SHOWSTOPPER_PRIORITY --quiet $DOCKERIMAGE
artifacts:
when: always # возвращать артефакты даже после сбоя задания
paths:
- $ARTIFACT_FOLDER/trivy_results.json
cache:
paths:
- .cache
Report:
# комбинирование выводов инструментов в один 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При необходимости также можно сканировать и сохраненные образы в виде .tar-архива (однако потребуется в YAML файле изменить входные параметры для утилит)
NB: Trivy требует для своего запуска установленные rpm in git. В противном случае он будет выдавать ошибки при сканировании образов на базе RedHat и получении обновлений базы уязвимостей.
2. После добавления файлов в репозиторий, согласно инструкциям в нашем конфигурационном файле, GitLab автоматически начнёт процесс сборки и сканирования. На вкладке CI/CD → Pipelines можно будет увидеть ход выполнения инструкций.
В результате у нас есть четыре задачи. Три из них занимаются непосредственно сканированием, а последняя (Report) собирает простой отчёт из разрозненных файлов с результатами сканирования.

По умолчанию Trivy останавливает своё выполнение, если были обнаружены неуязвимости с критической серьезностью в образе или зависимостях. В то же время Hadolint всегда возвращает код выполнения Success, так как результаты его работы всегда включают замечания, что приводит к остановке сборки.
В зависимости от конкретных требований можно сконфигурировать код выхода так, чтобы эти утилиты останавливались также и процесс сборки при обнаружении проблем определенной критичности. В нашем случае сборка остановится, только если Trivy обнаружит уязвимость с критичностью, которую мы указали в переменной SHOWSTOPPER в .gitlab-ci.yml.

Результат работы каждой утилиты можно увидеть в логе каждой сканирующей задачи, непосредственно в json-файлах в разделе artifacts или в простом HTML-отчёте (о нём немного ниже):

3. Для представления отчётов утилит в более удобочитаемом формате используется небольшой скрипт на Python для конвертации трёх json-файлов в один HTML-файл с таблицей дефектов.
Этот скрипт запускается отдельной задачей Report, а его итоговым артефактом является HTML-файл с отчётом. Исходный код скрипта также хранится в репозитории, и его можно адаптировать под свои нужды, цвета и т. п.

Shell-скрипт
Druga varianta je pogodna za slučajeve kada je potrebno proveravati Docker slike van CI/CD sistema ili kada je potrebno imati sve upute u formatu koji se može izvršiti direktno na hostu. Ova varijanta pokriva gotovi shell skript koji se može pokrenuti na čistoj virtuelnoj (ili čak stvarnoj) mašini. Skript izvršava iste upute kao i prethodno opisani gitlab-runner.
Za uspešno izvođenje skripta, Docker treba biti instaliran u sistemu, a trenutni korisnik treba biti u docker grupi.
Sam skript možete preuzeti ovde:
Na vrhu fajla varijablama se definiše koju sliku treba skenirati i koje ozbiljnosti greške će izazvati izlazak iz Trivy alata sa datim kodom greške.
Tokom izvršavanja skripta, svi alati će biti preuzeti u direktorijum docker_tools, a rezultati njihove upotrebe će biti u direktorijumu docker_tools/json, a HTML izveštaj će se nalaziti u fajlu results.html.
Primer izlaza skripta
~\/docker_cicd$ \.\/docker_sec_check.sh\n\n[+] Postavljanje promenljivih okruženja\n[+] Instalacija potrebnih paketa\n[+] Priprema potrebnih direktorijuma\n[+] Preuzimanje uzorka Dockerfile\n2020-10-20 10:40:00 (45.3 MB\/s) - ‘Dockerfile’ sačuvano [8071\/8071]\n[+] Pulling image to scan\nlatest: Pulling from bkimminich\/juice-shop\n[+] Pokretanje Hadolinta\n...\nDockerfile:205 DL3015 Izbegavajte dodatne pakete tako što ćete odrediti `--no-install-recommends`\nDockerfile:248 DL3002 Poslednji KORISNIK ne bi trebao biti root\n...\n[+] Pokretanje Dockle\n...\nWARN - DKL-DI-0006: Izbegavajte latest oznaku\n * Izbegavajte 'latest' oznaku\nINFO - CIS-DI-0005: Omogućite poverenje sadržaja za Docker\n * export DOCKER_CONTENT_TRUST=1 pre docker pull\/build\n...\n[+] Pokretanje Trivy\njuice-shop\/frontend\/package-lock.json\n=====================================\nUkupno: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 2, CRITICAL: 0)\n\n+---------------------+------------------+----------+---------+-------------------------+\n| BIBLIOTEKA | ID RIZIKA | OZBURNOST | VERZIJA | NASLOV |\n+---------------------+------------------+----------+---------+-------------------------+\n| object-path | CVE-2020-15256 | HIGH | 0.11.4 | Prototipska zagađenja u |\n| | | | | object-path |\n+---------------------+------------------+ +---------+-------------------------+\n| tree-kill | CVE-2019-15599 | | 1.2.2 | Injekcija Koda |\n+---------------------+------------------+----------+---------+-------------------------+\n| webpack-subresource | CVE-2020-15262 | LOW | 1.4.1 | Nezaštićeni dinamički |\n| | | | | učitani delovi |\n+---------------------+------------------+----------+---------+-------------------------+\n\njuice-shop\/package-lock.json\n============================\nUkupno: 20 (UNKNOWN: 0, LOW: 1, MEDIUM: 6, HIGH: 8, CRITICAL: 5)\n\n...\n\njuice-shop\/package-lock.json\n============================\nUkupno: 5 (CRITICAL: 5)\n\n...\n[+] Uklanjanje preostalih\n[+] Uređivanje izlaza da izgledaju lepo\n[+] Konvertovanje JSON rezultata\n[+] Pisanje rezultata HTML\n[+] Čist izlaz ============================================================\n[+] Sve je završeno. Pronađite rezultirajući HTML izveštaj u results.htmlDocker slika sa svim alatima
Kao treća alternativa sastavio sam dva jednostavna Dockerfile-a za pravljenje slike sa sigurnosnim alatima. Jedan Dockerfile će pomoći u prikupljanju seta za skeniranje slike iz repozitorijuma, dok će drugi (Dockerfile_tar) pomoći da se zusammeli set za skeniranje tar datoteke sa slikom.
1. Preuzmite odgovarajući Docker file i skripte iz repozitorija .
2. Pokrenite ga za izgradnju:
docker build -t dscan:image -f docker_security.df .
3. Nakon završetka izgradnje napravite kontejner iz slike. Pritom prosledite varijablu okruženja DOCKERIMAGE sa nazivom željene slike i montirajte Dockerfile koji želite analizirati sa vaše mašine na datoteku /Dockerfile (obratite pažnju da je potreban apsolutni put do ove datoteke):
docker run --rm -v $(pwd)\/results:\/results -v $(pwd)\/docker_security.df:\/Dockerfile -e DOCKERIMAGE="bkimminich\/juice-shop" dscan:image
[+] Postavljanje varijabli okruženja
[+] Pokretanje Hadolint
\/Dockerfile:3 DL3006 Uvek označavajte verziju slike eksplicitno
[+] Pokretanje Dockle
WARN - DKL-DI-0006: Izbegavajte najnoviju oznaku
* Izbegavajte 'najnoviju' oznaku
INFO - CIS-DI-0005: Omogućite poverenje sadržaja za Docker
* izvezite DOCKER_CONTENT_TRUST=1 pre docker pull\/build
INFO - CIS-DI-0006: Dodajte HEALTHCHECK instrukciju u sliku kontejnera
* nije pronađena HEALTHCHECK izjava
INFO - DKL-LI-0003: Postavite samo neophodne datoteke
* nepotrebna datoteka: juice-shop\/node_modules\/sqlite3\/Dockerfile
* nepotrebna datoteka: juice-shop\/node_modules\/sqlite3\/tools\/docker\/architecture\/linux-arm64\/Dockerfile
* nepotrebna datoteka: juice-shop\/node_modules\/sqlite3\/tools\/docker\/architecture\/linux-arm\/Dockerfile
[+] Pokretanje Trivy
...
juice-shop\/package-lock.json
============================
Ukupno: 20 (NEPOZNATO: 0, NISKO: 1, SREDNJE: 6, VISOKO: 8, KRITIČNO: 5)
...
[+] Uređivanje izlaza
[+] Pokretanje glavnog modula ============================================================
[+] Konvertovanje JSON rezultata
[+] Pisanje rezultata u HTML
[+] Čist izlaz ============================================================
[+] Sve je gotovo. Pronađite rezultirajući HTML izveštaj u results.htmlRezultati
Razmatrali smo samo jedan osnovni skup alata za skeniranje Docker artefakata, koji, po mom mišljenju, prilično efikasno pokriva značajan deo sigurnosnih zahteva za slike. Postoji još mnogo plaćenih i besplatnih alata koji mogu vršiti iste provere, praviti lepe izveštaje ili raditi isključivo u konzolnom režimu, pokrivajući sisteme za upravljanje kontejnerima itd. Pregled ovih alata i načina njihove integracije možda će se pojaviti nešto kasnije.
Pozitivna platnost skupa alatov, ki je opisan v članku, je, da so vsi zgrajeni na odprtokodnih rešitvah in lahko jih preizkušate skupaj z drugimi podobnimi orodji, da najdete tisto, kar ustreza vašim zahtevam in značilnostim infrastrukture. Vse ranljivosti, ki jih boste odkrili, je seveda treba preučiti glede uporabnosti v določenih pogojih, vendar je to tema za prihodnji obsežen članek.
Upam, da vam ta navodila, skripti in orodja pomagajo in postanejo izhodiščna točka za ustvarjanje bolj varne infrastrukture na področju, ki se nanaša na kontejnerizacijo.
Izvor: habr.com
