یکی از گامهای ساده در عین حال بسیار اثرگذار در بحث امنسازی سیستمها، پویش (scan) آسیبپذیریهای شناخته شده (known vulnerabilities) میباشد. بسیاری از اپهای کانتینری شده ممکن است بر پایهی یک ایمیج قدیمی ایجاد شده باشند یا همچنین ممکن است در توسعهی آنها از کتابخانههایی استفاده شده باشد که دارای آسیبپذیری شناخته شده باشد.
در یک برنامهی کانتینری شده یک آسیبپذیری در سطح سیستمعامل میتواند امنیت کل برنامه را زیر سوال ببرد. همچنین یکی از مواردی که اغلب توسط برنامهنویسها نادیده گرفته میشود پویش آسیبپذیری کتابخانههای ثالث است که در توسعهی برنامه مورد استفاده قرار میگیرد. به همین دلیل لازم است قبل از برپاسازی یک برنامه در محیط عملیاتی از جنبهی امنیت و همچنین انطباق با سیاستهای خاص (مثلا برنامههای حوزهی پرداخت یا پزشکی) مورد پویش قرار بگیرد.
برای مثال شما به پکیج OpenSSL نیاز دارید و در ابتدای Dockerfile خودتون اون رو با استفاده از apt نصب می کنید. حالا اگر همین نسخه OpenSSL که نصب میشه دارای باگ امنیتی باشه چه اتفاقی میوفته ؟ شما اونو توی کانتینر خودتون قرار دادید و اونو یه جایی Deploy می کنید.
بریم جلوتر … شما یه پروژه Laravel توسعه دادید و نیازمندی های اون رو با استفاده از Composer نصب کردید. یکی از این نیازمندی ها مثلا guzzle مثل مورد قبلی دارای باگ خاصی باشه.
اگر یه تیم بخواد تک تک پکیج ها رو بررسی کنه و vulnerabilities ازشون در بیاره و نسخه تنظیم کنه خب باید برن یه تیم دیگه استخدام کنن … اصلا این کار رو چندبار باید انجام بدن ؟ ممکنه هفته ای یک نسخه منتشر بشه ٬ باید هر هفته چند ساعت وقت بذارن و دستی تمام موارد رو بررسی کنن ؟ مسلما نه
در این شرایط با پویش پیوسته برنامهها در زمان ساخت آنها، در چرخهی CI/CD، میتوان در فاز ساخت ایمیج کانتینر بخشی از آسیبپذیریها را شناسایی کرد و از انتشار نسخهی ناامن و برپاسازی آن در محیط عملیاتی جلوگیری کرد. در این نوشتار برای پویش امنیتی ایمیجها به معرفی ابزار Trivy و نحوهی استفادهی آن در چرخهی CI/CD در Gitlab میپردازیم.
Trivy چیست؟
Trivy یک ابزار متن باز است که در ابتدا به عنوان یک پروژهی شخصی برای رفع نیازمندی پویش امنیتی ایمیج کانتینرها در بستر کوبرنتیز توسعه داده شد. پس از انتشار نسخههای اولیه در کامیونتیهای متنباز به صورت گسترده مورد استقبال قرار گرفت. انتشار Trivy به صورت یک فایل باینری مستقل که وابستگی به هیچ پایگاه داده کتابخانه و یا سیستمعامل خاصی ندارد باعث شده که استقرار و بکارگیری آن بسیار ساده و مطلوب شود.
به طور دقیقتر با استفاده از Trivy میتوان آسیبپذیریهایی را که در سطح بستههای (Package) سیستمعامل یا کتابخانههای یک زبان برنامهنویسی وجود دارد را شناسایی کرد.
نصب Trivy
نصب Trivy خیلی ساده است و فقط با یه دستور انجام میشه :
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/master/contrib/install.sh | sh -s -- -b /usr/local/bin
برای اسکن کردن تنها کافیه یک Docker image که درواقع همون کانتینر شما به حساب میاد رو به Trivy بدید :
trivy image [YOUR_IMAGE_NAME]
# Example
trivy image python:3.4-alpine3.9
2019-05-16T01:20:43.180+0900 INFO Updating vulnerability database...
2019-05-16T01:20:53.029+0900 INFO Detecting Alpine vulnerabilities...
python:3.4-alpine3.9 (alpine 3.9.2)
===================================
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
+---------+------------------+----------+-------------------+---------------+--------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------------------------------+
| openssl | CVE-2019-1543 | MEDIUM | 1.1.1a-r1 | 1.1.1b-r1 | openssl: ChaCha20-Poly1305 |
| | | | | | with long nonces |
+---------+------------------+----------+-------------------+---------------+--------------------------------+
مشاهده می کنید که در ابتدا دیتابیس vulnerability به روزرسانی میشه و سپس پکیج های سیستم عامل که در اینجا ما Alpine داریم بررسی میشه و نتایج اعلام میشه.
trivy image hub.hamdocker.ir/library/httpd:2.4.43 | jq ".Results[].Vulnerabilities[].VulnerabilityID"
"CVE-2021-33574"
"CVE-2021-35942"
"CVE-2020-1751"
"CVE-2020-1752"
...
در مثال بالا فهرست آسیبپذیریهای شناخته شده ایمیج httpd نسخهی ۲.۴.۴۳ استخراج شده است. در زمان اجرا میتوان از سوییچهای زیر برای فیلتر کردن پویش استفاده کرد. استفاده از ignore-unfixed برای نادیده گرفتن آسیبپذیریهایی که فاقد وصله هستند و تاکنون رفع نشده اند مثلا:
trivy image hub.hamdocker.ir/library/httpd:2.4.43 --ignore-unfixed
فیلتر کردن آسیبپذیریها بر اساس شدت با استفاده از severity
trivy image hub.hamdocker.ir/library/httpd:2.4.43 --severity HIGH,CRITICAL --ignore-unfixed
فیلتر کردن نمایش و ذخیره کردن خروجی در یک فایل با استفاده از output و format
به صورت پیشفرض Trivy نتیجهی آزمایش را در حالت جدول مانندی در خروجی نمایش میدهد. با استفاده از format , output میتوانید فرمت خروجی را تغییر دهید. مثلا:
trivy image hub.hamdocker.ir/library/httpd:2.4.43 --format json --output result.json
همچنین با استفاده از فرمت template میتوانید فرمت خروجی اختصاصی خودتون را بسازید.
استفاده از Trivy در فرآیند CI/CD
در چرخهی توسعهی نرمافزار اگر عملیات تست امنیت در گامهای اولیهی و به صورت مدوام انجام شود اثرگذاری بسیار زیادی خواهد داشت. برای خودکار کردن فرآیند پویش ایمیج کانتینرها میتوانید در چرخهی CI/CD در زمان ساخت ایمیجها بررسیهای لازم را انجام دهید. در صورتی که پویش با کشف آسیبپذیری همراه بود از انتشار نسخهی ناامن جلوگیری کنید.
برای استفاده از Trivy در فرایند CI/CD در Gitlab یا گیتهاب باید در فایل gitlab-ci.yml پروژه یک فاز برای پویش آسیبپذیریها تعریف کنید. در مثال زیر این عملیات در فاز test بعد از build شدن ایمیج و push شدن آن در registry صورت میگیرد.
stages:
- build
- test
build:
stage: build
image: docker:stable
script:
- export IMAGE="registry.hamdocker.ir/GROUP_NAME/PROJECT_NAME"
- docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASSWORD" "$REGISTRY"
- docker build -t PROJECT_NAME:$CI_COMMIT_SHORT_SHA .
- docker image tag PROJECT_NAME:$CI_COMMIT_SHORT_SHA $IMAGE:$CI_COMMIT_SHORT_SHA
- docker image push $IMAGE:$CI_COMMIT_SHORT_SHA
container_scanning:
stage: test
image:
name: hub.hamdocker.ir/aquasec/trivy:latest
entrypoint: [""]
variables:
GIT_STRATEGY: none
TRIVY_USERNAME: "$REGISTRY_USER"
TRIVY_PASSWORD: "$REGISTRY_PASSWORD"
TRIVY_AUTH_URL: "$REGISTRY"
FULL_IMAGE_NAME: registry.denyip.ir/GROUP_NAME/PROJECT_NAME:$CI_COMMIT_SHORT_SHA
script:
- trivy --version
# cache cleanup is needed when scanning images with the same tags, it does not remove the database
- trivy image --clear-cache
# update vulnerabilities db
- trivy image --download-db-only --no-progress --cache-dir .trivycache/
# Builds report and puts it in the default workdir $CI_PROJECT_DIR, so `artifacts:` can take it from there
- trivy image --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/contrib/gitlab.tpl" --output "gl-container-scanning-report.json" "$FULL_IMAGE_NAME"
# Fails on high and critical vulnerabilities
- trivy image --exit-code 1 --cache-dir .trivycache/ --severity CRITICAL --no-progress "$FULL_IMAGE_NAME"
cache:
paths:
- .trivycache/
artifacts:
when: always
paths:
- gl-container-scanning-report.json
reports:
container_scanning: gl-container-scanning-report.json
only:
refs:
- master
برای استفاده از این مثال باید در قسمت variables در تعریف FULL_IMAGE_NAME نام گروه و نام پروژه را وارد کنید. مثلا به صورت زیر
FULL_IMAGE_NAME: registry.denyip.ir/mehdi/myapp:$CI_COMMIT_SHORT_SHA
پس از اضافه کردن این فاز پس از هر commit در مخزن فرآیند build اجرا میشود و بعد از آن در فرآیند test عملیات پویش امنیتی ایمیج کانتینر صورت میگیرد. در این فاز بعد از آپدیت کردن دیتابیس Trivy پویش کامل ایمیج صورت میگیرد که نتیجهی اجرای آن در فایل gl-container-scanning-report.json ذخیره میشود. در تصویر زیر مشاهده میکنیم که فرآیند پویش امنیتی به دلیل وجود آسیبپذیری با خطا خاتمه یافته است. در سمت راست در قسمت Job artifact میتوانید گزارش پویش را دانلود و مشاهده کنید. همانطور که مشاهده میکنید فرآیند test مربوط به پویش امنیتی با شکست خاتمه یافته است.
همچنین گزارش این پویش در بخش Security Dashboard > Vulnerability Report در دسترس میباشد.
در تصویر بالا مشاهده میکنیم که آسیبپذیریهای کشف شده در فرآیند CI/CD در قسمت Vulnerability Report آورده شده است که برای افراد دخیل در پروژه قابل پیگیری میباشد.
همانطور که اشاره شد با هربار commit کردن در مخزن این فرآیند اجرا خواهد شد و اگر پروژه دارای آسیبپذیری با درجهی CRITICAL باشد، job فعلی با خطا خاتمه مییابد و فرآیند deploy صورت نخواهد گرفت.
نتیجهگیری
در این نوشتار دیدیم که استفاده از ابزار Trivy ساده است و به راحتی میتوان از آن در توسعهی یک پروژه و فرآیند CI/CD استفاده کرد. بروز بودن این ابزارها و قدرتشان در تشخیص پکیجها و وابستگیها در سطح سیستمعامل و زبانهای برنامهنویسی بسیار قابل اتکاست، که با بهرهگیری از آنها بخش زیادی از آسیبپذیریها به سرعت در فاز توسعه کشف میشوند و از انتشار نسخهی ناامن جلوگیری خواهد شد. در نظر داشته باشید که ممکن است حتی پس از انتشار یک نسخه نیز آسیبپذیریهای در بستهها و کتابخانهها کشف شود. در این شرایط پیشنهاد میشود که با خودکار کردن فرآیند تست، به صورت پیوسته در زمانهای معینی در طول روز همهی کانتینرهای در حال اجرا در کلاستر را از جنبهی امنیت تست کنید.