Pozdravljeni, Habr!
V sodobni realnosti, zaradi vse večje vloge kontejnerizacije v razvojnih procesih, vprašanje zagotavljanja varnosti različnih faz in entitet, povezanih s kontejnerji, ni najmanj pomembno vprašanje. Izvajanje ročnih pregledov je zamudno, zato bi bilo dobro narediti vsaj prve korake k avtomatizaciji tega procesa.
V tem članku bom delil že pripravljene skripte za implementacijo več varnostnih pripomočkov Docker in navodila za namestitev majhnega predstavitvenega stojala za testiranje tega postopka. Materiale lahko uporabite za eksperimentiranje s tem, kako organizirati postopek testiranja varnosti slik in navodil Dockerfile. Jasno je, da je razvojna in implementacijska infrastruktura za vsakogar drugačna, zato bom spodaj navedel več možnih možnosti.
Pripomočki za varnostno preverjanje
Obstaja veliko število različnih pomožnih aplikacij in skriptov, ki izvajajo preverjanja različnih vidikov infrastrukture Docker. Nekatere izmed njih smo že opisali v prejšnjem članku (
Hadolint
Precej preprost konzolni pripomoček, ki pomaga, kot prvi približek, oceniti pravilnost in varnost navodil Dockerfile (na primer uporaba samo pooblaščenih registrov slik ali uporaba sudo).
Dockle
Konzolni pripomoček, ki deluje s sliko (ali s shranjenim tar arhivom slike), ki preverja pravilnost in varnost določene slike kot take, analizira njene plasti in konfiguracijo – kateri uporabniki so ustvarjeni, katera navodila so uporabljena, katera nosilci so nameščeni, prisotnost praznega gesla itd. d. Zaenkrat število preverjanj ni zelo veliko in temelji na več naših lastnih preverjanjih in priporočilih
Trivy
Ta pripomoček je namenjen iskanju dveh vrst ranljivosti - težave z različicami operacijskega sistema (podprto s strani Alpine, RedHat (EL), CentOS, Debian GNU, Ubuntu) in težave z odvisnostmi (Gemfile.lock, Pipfile.lock, composer.lock, package -lock.json, yarn.lock, cargo.lock). Trivy lahko skenira sliko v repozitoriju in lokalno sliko ter lahko skenira tudi na podlagi prenesene datoteke .tar s sliko Docker.
Možnosti za izvajanje pripomočkov
Da bi opisane aplikacije preizkusili v izoliranem okolju, bom podal navodila za namestitev vseh pripomočkov v nekoliko poenostavljenem postopku.
Glavna ideja je pokazati, kako lahko implementirate samodejno preverjanje vsebine datotek Docker in slik Docker, ki so ustvarjene med razvojem.
Samo preverjanje je sestavljeno iz naslednjih korakov:
- Preverjanje pravilnosti in varnosti navodil Dockerfile z uporabo pripomočka linter Hadolint
- Preverjanje pravilnosti in varnosti končne in vmesne slike s pomočjo pripomočka Dockle
- Preverjanje prisotnosti javno znanih ranljivosti (CVE) v osnovni sliki in številnih odvisnosti - z uporabo pripomočka Trivy
Kasneje v članku bom podal tri možnosti za izvedbo teh korakov:
Prvi je s konfiguriranjem cevovoda CI/CD z uporabo GitLaba kot primera (z opisom postopka dvigovanja testne instance).
Drugi je uporaba lupinskega skripta.
Tretja vključuje izdelavo slike Docker za skeniranje slik Docker.
Izberete lahko možnost, ki vam najbolj ustreza, jo prenesete na svojo infrastrukturo in prilagodite svojim potrebam.
Vse potrebne datoteke in dodatna navodila se nahajajo tudi v repozitoriju:
Integracija v GitLab CI/CD
V prvi možnosti si bomo ogledali, kako lahko implementirate varnostne preglede na primeru sistema repozitorija GitLab. Tukaj bomo šli skozi korake in ugotovili, kako namestiti testno okolje z GitLabom iz nič, ustvariti postopek skeniranja in zagnati pripomočke za preverjanje testne Dockerfile in naključne slike - aplikacije JuiceShop.
Namestitev GitLaba
1. Namestite Docker:
sudo apt-get update && sudo apt-get install docker.io
2. Dodajte trenutnega uporabnika v skupino dockerjev, da boste lahko delali z dockerjem brez uporabe sudo:
sudo addgroup <username> docker
3. Poiščite svoj IP:
ip addr
4. Namestite in zaženite GitLab v vsebniku, tako da naslov IP v imenu gostitelja zamenjate s svojim:
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
Počakamo, da GitLab dokonča vse potrebne namestitvene postopke (postopek lahko spremljate prek izhodne datoteke dnevnika: docker logs -f gitlab).
5. Odprite svoj lokalni IP v brskalniku in si oglejte stran, ki zahteva, da spremenite geslo za uporabnika root:
Nastavite novo geslo in pojdite na GitLab.
6. Ustvarite nov projekt, na primer cicd-test in ga inicializirajte z začetno datoteko PREBERITE.md:
7. Zdaj moramo namestiti GitLab Runner: agenta, ki bo izvajal vse potrebne operacije na zahtevo.
Prenesite najnovejšo različico (v tem primeru za 64-bitni Linux):
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
8. Naj bo izvršljivo:
sudo chmod +x /usr/local/bin/gitlab-runner
9. 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 start
Videti bi moralo nekako 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. Sedaj registriramo Runner, da lahko komunicira z našim primerkom GitLab.
Če želite to narediti, odprite stran Settings-CI/CD (http://OUR_IP_ADDRESS/root/cicd-test/-/settings/ci_cd) in na zavihku Runners poiščite URL in registracijski žeton:
11. Registrirajte Runner tako, da zamenjate URL in registracijski žeton:
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"
Kot rezultat dobimo že pripravljen delujoč GitLab, v katerega moramo dodati navodila za zagon naših pripomočkov. V tej predstavitvi nimamo korakov za gradnjo aplikacije in njeno shranjevanje v vsebnike, toda v resničnem okolju bi bili ti pred koraki skeniranja in bi ustvarili slike in Dockerfile za analizo.
konfiguracijo cevovoda
1. Dodajte datoteke v repozitorij mydockerfile.df (to je testna datoteka Docker, ki jo bomo preverili) in konfiguracijska datoteka procesa GitLab CI/CD .gitlab-cicd.yml, ki navaja navodila za optične bralnike (upoštevajte piko v imenu datoteke).
Konfiguracijska datoteka YAML vsebuje navodila za zagon treh pripomočkov (Hadolint, Dockle in Trivy), ki bodo analizirali izbrano datoteko Dockerfile in sliko, navedeno v spremenljivki DOCKERFILE. Vse potrebne datoteke lahko vzamete iz repozitorija:
Odlomek iz mydockerfile.df (to je abstraktna datoteka z nizom poljubnih navodil samo za prikaz delovanja pripomočka). 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 <[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
Konfiguracija YAML je videti tako (samo datoteko najdete preko neposredne povezave tukaj:
Vsebina .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
Po potrebi lahko skenirate tudi shranjene slike v obliki arhiva .tar (vendar boste morali spremeniti vhodne parametre za pripomočke v datoteki YAML)
Opomba: Trivy zahteva nameščen rpm и git. V nasprotnem primeru bo ustvaril napake pri skeniranju slik, ki temeljijo na RedHat, in prejemanju posodobitev baze podatkov ranljivosti.
2. Po dodajanju datotek v repozitorij bo GitLab v skladu z navodili v naši konfiguracijski datoteki samodejno začel postopek gradnje in skeniranja. Na zavihku CI/CD → Cevovodi si lahko ogledate potek navodil.
Kot rezultat imamo štiri naloge. Trije se ukvarjajo neposredno s skeniranjem, zadnji (Poročilo) pa zbira preprosto poročilo iz razpršenih datotek z rezultati skeniranja.
Privzeto se Trivy preneha izvajati, če so v sliki ali odvisnostih odkrite KRITIČNE ranljivosti. Hkrati Hadolint vedno vrne kodo uspeha, ker vedno povzroči komentarje, kar povzroči zaustavitev gradnje.
Glede na vaše specifične zahteve lahko konfigurirate izhodno kodo, tako da ko ti pripomočki zaznajo težave določene kritičnosti, prav tako ustavijo postopek gradnje. V našem primeru se bo gradnja ustavila samo, če Trivy zazna ranljivost s kritičnostjo, ki smo jo podali v spremenljivki SHOWSTOPPER v .gitlab-ci.yml.
Rezultat vsakega pripomočka si lahko ogledate v dnevniku vsakega opravila skeniranja, neposredno v datotekah json v razdelku artefaktov ali v preprostem poročilu HTML (več o tem spodaj):
3. Za predstavitev poročil o pripomočkih v nekoliko bolj berljivi obliki se uporabi majhen skript Python za pretvorbo treh datotek JSON v eno datoteko HTML s tabelo napak.
Ta skript se zažene z ločeno nalogo Report, njegov končni artefakt pa je datoteka HTML s poročilom. Izvor skripte je tudi v repozitoriju in ga je mogoče prilagoditi vašim potrebam, barvam itd.
Lupinski skript
Druga možnost je primerna za primere, ko morate preveriti slike Docker zunaj sistema CI/CD ali morate imeti vsa navodila v obliki, ki jo je mogoče izvesti neposredno na gostitelju. To možnost pokriva že pripravljen lupinski skript, ki ga je mogoče zagnati na čistem virtualnem (ali celo resničnem) računalniku. Skript izvaja ista navodila kot gitlab-runner, opisan zgoraj.
Za uspešno izvajanje skripta mora biti Docker nameščen v sistemu in trenutni uporabnik mora biti v skupini dockerjev.
Sam scenarij najdete tukaj:
Na začetku datoteke spremenljivke določajo, katero sliko je treba skenirati in katere kritične napake bodo povzročile izhod pripomočka Trivy z navedeno kodo napake.
Med izvajanjem skripta bodo vsi pripomočki preneseni v imenik docker_tools, rezultati njihovega dela so v imeniku docker_tools/json, in HTML s poročilom bo v datoteki rezultati.html.
Primer izpisa skripta
~/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 slika z vsemi pripomočki
Kot tretjo možnost sem sestavil dve preprosti datoteki Dockerfile za ustvarjanje slike z varnostnimi pripomočki. En Dockerfile bo pomagal zgraditi nabor za skeniranje slike iz repozitorija, drugi (Dockerfile_tar) pa bo pomagal zgraditi nabor za skeniranje datoteke tar s sliko.
1. Vzemite ustrezno datoteko Docker in skripte iz repozitorija
2. Zaženemo ga za montažo:
docker build -t dscan:image -f docker_security.df .
3. Po končani montaži iz slike izdelamo posodo. Hkrati posredujemo spremenljivko okolja DOCKERIMAGE z imenom slike, ki nas zanima, in priklopimo datoteko Dockerfile, ki jo želimo analizirati iz našega stroja v datoteko /Dockerfile (upoštevajte, da je potrebna absolutna pot do te datoteke):
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
Ugotovitve
Ogledali smo si le en osnovni nabor pripomočkov za skeniranje artefaktov Docker, ki po mojem mnenju zelo učinkovito pokriva spodoben del varnostnih zahtev slike. Obstaja tudi veliko število plačljivih in brezplačnih orodij, ki lahko izvajajo iste preglede, rišejo čudovita poročila ali delujejo izključno v konzolnem načinu, pokrivajo sisteme za upravljanje vsebnikov itd. Pregled teh orodij in kako jih integrirati se lahko pojavi malo kasneje .
Dobra stvar nabora orodij, opisanih v članku, je, da so vsa zgrajena na odprtokodni kodi in lahko eksperimentirate z njimi in drugimi podobnimi orodji, da najdete tisto, kar ustreza vašim zahtevam in infrastrukturnim funkcijam. Seveda je treba vse najdene ranljivosti preučiti glede uporabnosti v določenih pogojih, vendar je to tema za prihodnji večji članek.
Upam, da vam bodo ta vodnik, skripte in pripomočki pomagali in postali izhodišče za ustvarjanje varnejše infrastrukture na področju kontejnerizacije.
Vir: www.habr.com