Halo, Habr!
Dalam realitas modern, karena meningkatnya peran containerization dalam proses pengembangan, masalah memastikan keamanan berbagai tahapan dan entitas yang terkait dengan container bukanlah hal terakhir. Melakukan pemeriksaan dalam mode manual memakan waktu, jadi sebaiknya lakukan setidaknya langkah awal untuk mengotomatiskan proses ini.
Pada artikel ini, saya akan membagikan skrip siap pakai untuk mengimplementasikan beberapa utilitas keamanan Docker dan instruksi tentang cara menyebarkan dudukan demo kecil untuk menguji proses ini. Anda dapat menggunakan sumber daya untuk bereksperimen dengan cara mengatur proses pengujian keamanan untuk gambar dan instruksi Dockerfile. Jelas bahwa infrastruktur pengembangan dan implementasi berbeda untuk setiap orang, jadi di bawah ini saya akan memberikan beberapa kemungkinan opsi.
Utilitas pemeriksaan keamanan
Ada banyak aplikasi dan skrip helper berbeda yang menguji berbagai aspek infrastruktur Docker. Beberapa di antaranya telah dijelaskan di artikel sebelumnya ( https://habr.com/ru/company/swordfish_security/blog/518758/#docker-security ), dan dalam materi ini saya ingin menyoroti tiga di antaranya yang mencakup bagian dari persyaratan keamanan untuk image Docker yang dibuat selama pengembangan. Selain itu, saya juga akan menunjukkan contoh bagaimana ketiga utilitas tersebut dapat dihubungkan menjadi satu pipeline untuk melakukan pemeriksaan keamanan.
Hadolint
https://github.com/hadolint/hadolint
Utilitas konsol yang cukup sederhana yang membantu, sebagai perkiraan pertama, untuk menilai kebenaran dan keamanan instruksi Dockerfile (misalnya, hanya menggunakan register gambar resmi atau menggunakan sudo).
Dockle
https://github.com/goodwithtech/dockle
Utilitas konsol yang bekerja dengan gambar (atau dengan arsip tar yang disimpan dari gambar) yang memeriksa kebenaran dan keamanan gambar tertentu, menganalisis lapisan dan konfigurasinya - pengguna mana yang dibuat, instruksi mana digunakan volume mana yang di-mount, adanya kata sandi kosong, dll. Sedangkan jumlah pemeriksaan tidak terlalu besar dan didasarkan pada beberapa pemeriksaan dan rekomendasi sendiri dari CIS (Center for Internet Security) Benchmark untuk Docker.
Trivy
https://github.com/aquasecurity/trivy
Utilitas ini bertujuan untuk menemukan dua jenis kerentanan - masalah pembuatan OS (didukung oleh Alpine, RedHat (EL), CentOS, Debian GNU, Ubuntu) dan masalah ketergantungan (Gemfile.lock, Pipfile. lock, composer.lock, package-lock.json, yarn.lock, Cargo.lock). Trivy dapat memindai image di repositori dan image lokal, serta memindai berdasarkan file .tar yang ditransfer dengan image Docker.
Opsi implementasi untuk utilitas
Untuk mencoba aplikasi yang dijelaskan dalam kondisi terisolasi, saya akan memberikan instruksi untuk menginstal semua utilitas dalam proses yang disederhanakan.
Ide utamanya adalah untuk mendemonstrasikan bagaimana Anda dapat mengimplementasikan validasi konten otomatis dari image Dockerfile dan Docker yang dibuat selama pengembangan.
Pemeriksaan itu sendiri terdiri dari langkah-langkah berikut:
- Memeriksa kebenaran dan keamanan instruksi Dockerfile - menggunakan linter Hadolint
- Memeriksa kebenaran dan keamanan gambar target dan menengah - menggunakan utilitas Dockle
- Memeriksa kerentanan terkenal (CVE) di gambar dasar dan sejumlah dependensi dengan utilitas Trivy
Lebih lanjut dalam artikel ini, saya akan memberikan tiga opsi untuk mengimplementasikan langkah-langkah ini:
Pertama, dengan mengkonfigurasi pipeline CI / CD menggunakan contoh GitLab (dengan penjelasan tentang proses peningkatan contoh uji).
Yang kedua adalah menggunakan skrip shell.
Yang ketiga adalah dengan membangun image Docker untuk memindai image Docker.
Anda dapat memilih opsi yang paling sesuai untuk Anda, mentransfernya ke infrastruktur Anda dan menyesuaikannya dengan kebutuhan Anda.
Semua file yang diperlukan dan instruksi tambahan juga ada di repositori: https://github.com/Swordfish-Security/docker_cicd
Integrasi ke dalam GitLab CI / CD
Pada opsi pertama, kita akan melihat bagaimana Anda dapat mengimplementasikan pemeriksaan keamanan menggunakan contoh sistem repositori GitLab. Di sini kita akan melalui langkah-langkah dan menganalisis cara mengatur lingkungan pengujian dengan GitLab dari awal, membuat proses pemindaian dan menjalankan utilitas untuk memeriksa uji Dockerfile dan gambar acak - aplikasi JuiceShop.
Menginstal GitLab
1. Instal Docker:
sudo apt-get update && sudo apt-get install docker.io
2. Tambahkan pengguna saat ini ke grup buruh pelabuhan sehingga Anda dapat bekerja dengan buruh pelabuhan tidak melalui sudo:
sudo addgroup <username> docker
3. Temukan IP Anda:
ip addr
4. Instal dan jalankan GitLab dalam wadah, ganti alamat IP di nama host dengan milik Anda:
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
Kami menunggu GitLab menyelesaikan semua prosedur instalasi yang diperlukan (Anda dapat mengikuti proses melalui keluaran dari file log: docker logs -f gitlab).
5. Buka IP lokal Anda di browser dan lihat halaman dengan proposal untuk mengubah kata sandi untuk pengguna root:
Setel kata sandi baru dan buka GitLab.
6. Buat proyek baru, misalnya cicd-test dan inisialisasi dengan file start README.md :
7. Sekarang kita perlu menginstal GitLab Runner: agen yang akan meluncurkan semua operasi yang diperlukan atas permintaan.
Unduh versi terbaru (dalam hal ini, untuk 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. Jadikan itu dapat dieksekusi:
sudo chmod +x /usr/local/bin/gitlab-runner
9. Tambahkan pengguna OS untuk Runner dan mulai layanan:
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
Seharusnya terlihat seperti ini:
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. Sekarang kita mendaftarkan Runner agar dapat berinteraksi dengan instance GitLab kita.
Untuk melakukan ini, buka halaman Settings-CI / CD (http: // OUR_ IP_ADDRESS / root / cicd-test / - / settings / ci_cd) dan pada tab Runners kami menemukan URL dan token Pendaftaran:
11. Daftarkan Runner dengan mengganti URL dan token Pendaftaran:
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"
Hasilnya, kami mendapatkan GitLab yang siap pakai, di mana kami perlu menambahkan instruksi untuk memulai utilitas kami. Dalam kasus demo ini, kami tidak memiliki langkah-langkah untuk membangun aplikasi dan penampungnya, tetapi dalam lingkungan nyata langkah-langkah tersebut akan mendahului langkah-langkah pemindaian dan menghasilkan gambar dan Dockerfile untuk dianalisis.
Konfigurasi pipeline
1. Tambahkan file mydockerfile.df ke repositori (ini adalah beberapa Dockerfile uji yang akan kami periksa) dan file konfigurasi proses GitLab CI / CD .gitlab-cicd.yml , yang mencantumkan instruksi untuk pemindai (catat titik di nama file ).
File konfigurasi YAML berisi instruksi untuk menjalankan tiga utilitas (Hadolint, Dockle, dan Trivy) yang akan mengurai Dockerfile yang dipilih dan image yang ditentukan dalam variabel DOCKERFILE. Semua file yang diperlukan dapat diambil dari repositori: https://github.com/Swordfish-Security/docker_cicd/
Kutipan dari mydockerfile.df (ini adalah file abstrak dengan serangkaian instruksi sewenang-wenang hanya untuk mendemonstrasikan cara kerja utilitas). Tautan langsung ke file: mydockerfile.df
Isi 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 <rhys@arkins.net>"
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 rootKonfigurasi YAML terlihat seperti ini (filenya sendiri dapat diambil dari tautan langsung di sini: .gitlab-ci.yml ):
Konten .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.htmlJika perlu, Anda juga dapat memindai gambar yang disimpan sebagai arsip .tar (namun, Anda perlu mengubah parameter input untuk utilitas dalam file YAML)
NB: Trivy rpm git. RedHat-based .
2. Setelah menambahkan file ke repositori, sesuai petunjuk di file konfigurasi kami, GitLab akan secara otomatis memulai proses build dan scan. Pada CI / CD โ tab Pipelines, Anda dapat melihat kemajuan instruksi.
Hasilnya, kami memiliki empat tugas. Tiga di antaranya menangani langsung pemindaian dan yang terakhir (Laporan) mengumpulkan laporan sederhana dari file yang tersebar dengan hasil pemindaian.
Secara default, Trivy berhenti mengeksekusi jika kerentanan KRITIS ditemukan di gambar atau dependensi. Pada saat yang sama, Hadolint selalu mengembalikan kode eksekusi ke Sukses, karena sebagai hasil dari eksekusinya, selalu ada komentar, yang mengarah ke build stop.
Bergantung pada kebutuhan spesifik Anda, Anda dapat mengonfigurasi kode keluar sehingga utilitas ini juga menghentikan proses build ketika mereka mendeteksi masalah dengan tingkat keparahan tertentu. Dalam kasus kami, build akan berhenti hanya jika Trivy mendeteksi kerentanan kritis yang kami tentukan dalam variabel SHOWSTOPPER di .gitlab-ci.yml .
Hasil operasi setiap utilitas dapat dilihat di log setiap tugas pemindaian, langsung di file json di bagian artefak, atau dalam laporan HTML sederhana (selengkapnya di bawah):
3. Untuk menyajikan laporan utilitas dalam bentuk yang sedikit lebih dapat dibaca manusia, skrip Python kecil digunakan untuk mengonversi tiga file json menjadi satu file HTML dengan tabel cacat.
Skrip ini diluncurkan oleh tugas Laporan terpisah, dan artefak akhirnya adalah file HTML dengan laporan. Sumber skrip juga ada di repositori dan Anda dapat menyesuaikannya dengan kebutuhan, warna, dll.
Skrip shell
Opsi kedua cocok untuk kasus-kasus ketika Anda perlu memeriksa image Docker di luar sistem CI / CD, atau Anda perlu memiliki semua instruksi dalam bentuk yang dapat dijalankan secara langsung di host. Opsi ini dilindungi oleh skrip shell siap pakai yang dapat dijalankan di mesin virtual (atau bahkan nyata) yang bersih. Skrip mengikuti instruksi yang sama seperti di atas gitlab-runner.
Agar skrip berhasil bekerja, Docker harus diinstal pada sistem dan pengguna saat ini harus berada dalam grup buruh pelabuhan.
Skrip itu sendiri dapat diambil di sini: docker_sec_check.sh
Pada awal file, variabel digunakan untuk mengatur gambar mana yang harus dipindai dan cacat kritis apa yang akan menyebabkan keluar dari utilitas Trivy dengan kode kesalahan yang ditentukan.
Selama eksekusi skrip, semua utilitas akan diunduh ke direktori docker_tools , hasil kerjanya - ke direktori docker_tools / json , dan HTML dengan laporannya akan ada di file results.html .
Contoh keluaran skrip
~/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.htmlGambar Docker dengan semua utilitas
Sebagai alternatif ketiga, saya telah menyusun dua Dockerfile sederhana untuk membuat image dengan utilitas keamanan. Satu Dockerfile akan membantu membangun satu set untuk memindai gambar dari repositori, yang kedua (Dockerfile_tar) - membangun satu set untuk memindai file tar dengan gambar.
1. Kami mengambil file dan skrip Docker yang sesuai dari repositori https://github.com/Swordfish-Security/docker_cicd/tree/master/Dockerfile .
2. Jalankan untuk perakitan:
docker build -t dscan:image -f docker_security.df .
3. Setelah perakitan selesai, buat container dari gambar. Pada saat yang sama, kami meneruskan variabel lingkungan DOCKERIMAGE dengan nama gambar yang kami minati dan memasang Dockerfile yang ingin kami analisis dari mesin kami ke file / Dockerfile (perhatikan bahwa jalur absolut ke file ini diperlukan):
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
hasil
Kami telah membahas hanya satu set alat dasar untuk memindai artefak Docker, yang, menurut saya, cukup efektif mencakup bagian yang layak dari persyaratan keamanan gambar. Ada banyak lagi alat gratis dan berbayar yang dapat melakukan pemeriksaan yang sama, membuat laporan yang indah atau bekerja murni dalam mode konsol, mencakup sistem manajemen kontainer, dll. Gambaran umum alat ini dan cara mengintegrasikannya mungkin akan muncul nanti.
Sisi positif dari seperangkat alat, yang dijelaskan dalam artikel, adalah bahwa semuanya dibangun di atas kode sumber terbuka dan Anda dapat bereksperimen dengannya dan alat serupa lainnya untuk menemukan apa yang benar-benar sesuai dengan persyaratan dan fitur infrastruktur Anda. Tentu saja, semua kerentanan yang akan ditemukan harus dipelajari untuk penerapan dalam kondisi tertentu, tetapi ini adalah topik untuk artikel besar mendatang.
Saya harap tutorial, skrip, dan utilitas ini akan membantu Anda dan menjadi titik awal untuk membuat infrastruktur yang lebih aman di bidang containerization.