پویش امنیتی برنامه‌های کانتینری شده در چرخه‌ی CI/CD

مقدمه

در روزهای گذشته آسیب‌پذیری‌ Log4Shell (CVE-2021-44228, CVE-2021-45046) در صدر اخبار حوزه‌ی امنیت فناوری اطلاعات بوده است. بسیاری از شرکت‌ها برای شناسایی و رفع این آسیب‌پذیری دچار چالش شده بودند. برای شناسایی این آسیب‌پذیری ابزار‌ها و یک‌خطی‌های (one-liners) مختلفی در دسترس بوده است. ما در هم‌روش برای شناسایی و کمک به مشتریانی که آسیب‌پذیر بوده‌اند از ابزار Trivy استفاده کردیم. در کلاستر‌های هم‌روش به صورت مداوم در بازه‌های زمانی معین کانتینر‌های در حال اجرا توسط Trivy پویش می‌شوند. در این مورد خاص به علت اهمیت این موضوع نتایج آسیب‌پذیری در اختیار مشتریان قرار گرفت. مزایای استفاده از Trivy به جای راه‌کارهای سنتی در بستر کوبرنتیز ما را ترغیب کرد که در یک نوشتار به معرفی Trivy و نحوه‌ی استفاده از آن در چرخه‌ی CI/CD در گیت‌لب بپردازیم.

اهمیت اسکن کردن ایمیج‌ها در DevSecOps

یکی از گام‌های ساده در عین حال بسیار اثرگذار در بحث امن‌سازی سیستم‌ها، پویش (scan) آسیب‌پذیری‌های شناخته شده (known vulnerabilities) می‌باشد. بسیاری از اپ‌های کانتینری شده ممکن است بر پایه‌ی یک ایمیج قدیمی ایجاد شده باشند یا همچنین ممکن است در توسعه‌ی آنها از کتابخانه‌هایی استفاده شده باشد که دارای آسیب‌پذیری شناخته شده باشد. در یک برنامه‌ی کانتینری شده یک آسیب‌پذیری در سطح سیستم‌عامل می‌تواند امنیت کل برنامه را زیر سوال ببرد. همچنین یکی از مواردی که اغلب توسط برنامه‌نویس‌ها نادیده گرفته می‌شود پویش آسیب‌پذیری کتابخانه‌های ثالث است که در توسعه‌ی برنامه مورد استفاده قرار می‌گیرد. به همین دلیل لازم است قبل از برپاسازی یک برنامه در محیط عملیاتی از جنبه‌ی امنیت و همچنین انطباق با سیاست‌های خاص (مثلا برنامه‌های حوزه‌ی پرداخت‌ یا پزشکی) مورد پویش قرار بگیرد. در این شرایط با پویش پیوسته برنامه‌ها در زمان ساخت آنها، در چرخه‌ی CI/CD، می‌توان در فاز ساخت ایمیج کانتینر بخشی از آسیب‌پذیری‌ها را شناسایی کرد و از انتشار نسخه‌ی ناامن و برپاسازی آن در محیط عملیاتی جلوگیری کرد. در این نوشتار برای پویش امنیتی ایمیج‌ها به معرفی ابزار Trivy و نحوه‌ی استفاده‌ی آن در چرخه‌ی CI/CD در Gitlab می‌پردازیم.

معرفی Trivy

Trivy یک ابزار متن باز است که در ابتدا به عنوان یک پروژه‌ی شخصی برای رفع نیازمندی پویش امنیتی ایمیج کانتینرها در بستر کوبرنتیز توسعه داده شد. پس از انتشار نسخه‌های اولیه در کامیونتی‌های متن‌باز به صورت گسترده مورد استقبال قرار گرفت. انتشار Trivy به صورت یک فایل باینری مستقل که وابستگی به هیچ پایگاه داده کتابخانه و یا سیستم‌عامل خاصی ندارد باعث شده که استقرار و بکارگیری آن بسیار ساده و مطلوب شود.

Trivy توانایی شناسایی آسیب‌پذیری در سطح ایمیج برنامه‌‌های کانتینری شده، فایل سیستم، مخزن مبتنی بر Git و همچنین فایل‌های تنظیمات (Configuration) را داراست. به طور دقیق‌تر با استفاده از Trivy می‌توان آسیب‌پذیری‌هایی را که در سطح بسته‌های (Package) سیستم‌عامل یا کتابخانه‌‌های یک زبان برنامه‌نویسی وجود دارد را شناسایی کرد. در تصویر زیر نمای کلی Trivy نمایش داده شده است.

 منبع: https://aquasecurity.github.io/trivy/v0.17.0/imgs/overview.png
منبع: https://aquasecurity.github.io/trivy/v0.17.0/imgs/overview.png

پویشگر‌های امنیتی برای شناسایی آسیب‌پذیری‌ها نیاز دارند که نسخه‌ی بسته‌ها و کتابخانه‌های نصب شده را شناسایی کنند. سپس با استفاده از فهرستی از آسیب‌پذیری‌هایی که در اختیار دارند (مثلا دیتابیس NVD, Red Hat) بسته‌ها و کتابخانه‌های آسیب‌پذیر را شناسایی و گزارش می‌کنند. برای مثال برای استخراج فهرست بسته‌های موجود در سیستم‌عامل‌ها می‌توانند از مسیرهای /var/lib/dpkg/ یا /lib/apk/db/ اطلاعات لازم را بدست بیاورند. در تصویر زیر یک نمونه از محتویات فایل /var/lib/dpkgstatus نمایش داده شده است.

$ grep Package -A9 /var/lib/dpkg/status
استخراج فهرست و مشخصات بسته‌های نصب شده
استخراج فهرست و مشخصات بسته‌های نصب شده

اگر ایمیج یک کانتینر به جهت کاهش حجم ایمیج strip شده باشد و اطلاعاتی نظیر اطلاعات فوق از آن حذف شده باشد احتمالا Trivy و سایر پویشگر‌های مشابه در این فاز نمی‌توانند به درستی عمل کنند.

برای استخراج فهرست کتابخانه‌های استفاده شده نیز Trivy از فایل‌های معینی استفاده می‌کند. فهرست این فایل‌ها بر اساس زبان‌های برنامه‌نویسی در این لینک آورده شده است. برای مثال در پایتون فایل‌های Pipfile.lock, poetry.lock و requirements.txt در زمان پویش مخزن یا فایل‌سیستم مورد استفاده قرار می‌گیرد.

برای پویش امنیتی ایمیج کانتینر‌ها ابزارهای جایگزین دیگری نظیر Clair, Anchore وجود دارد. در اینجا، اینجا و اینجا مقایسه‌هایی بین این ابزارها صورت گرفته است. غیر از ویژگی‌هایی که در صفحه‌ی معرفی پروژه در گیت‌هاب درج شده است، بر اساس تجربه‌ی ما ویژگی‌های زیر در Trivy قابل توجه است.

- ساده بودن استقرار و بکارگیری

- سرعت بالا در اجرای پویش

- صحت عملکرد و میزان False/Positive کمتر نسبت به ابزارهای مشابه

- انطباق با فرآیند CI/CD

بررسی قابلیت‌های مهم Trivy

در این قسمت قابلیت‌های مهم Trivy نظیر پویش ایمیج کانتینر، پویش فایل سیستم و پویش تنظیمات ناامن در مانیفیست‌ها را با مثال‌ مرور می‌کنیم. شما هم می‌توانید با نصب کردن Trivy از طریق دستور زیر مثال‌های که در بخش بعدی آورده شده است را امتحان کنید. با اجرای دستور زیر نسخه‌ی ۰.۲۱.۲ بر روی سیستم‌عامل شما نصب خواهد شد.

curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.21.2

- پویش ایمیج کانتینر

همان‌طور که اشاره کردیم Trivy یک ابزار برای پویش آسیب‌پذیری‌های شناخته شده در ایمیج کانتینرها می‌باشد. برای پویش یک ایمیج می‌توانیم از دستور زیر استفاده کنیم.

$ trivy image hub.hamdocker.ir/library/httpd:2.4.43 | jq &quot.Results[].Vulnerabilities[].VulnerabilityID&quot
&quotCVE-2021-33574&quot
&quotCVE-2021-35942&quot
&quotCVE-2020-1751&quot
&quotCVE-2020-1752&quot
...

در مثال بالا فهرست آسیب‌پذیری‌های شناخته شده ایمیج 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

- مشخص کردن یک فهرست برای نادیده گرفتن آسیب‌پذیری‌های خاص در زمان پویش

برای اینکار باید در یک فایل به نام .trivyignore شناسه‌ی آسیب‌پذیری‌ها را وارد کنیم. مثلا:

$ cat .trivyignore
CVE-2021-33574
CVE-2021-35942
$ trivy  image hub.hamdocker.ir/library/httpd:2.4.43

در مثال بالا در زمان پویش، آسیب‌پذیری‌های CVE-2021-33574 و CVE-2021-35942 نادیده گرفته می‌شود. همچنین با استفاده از OPA می‌توانید فیلتر کردن آسیب‌پذیری‌ها را انجام دهد یا بر عکس مثال بالا، تعیین کنید که فقط یک آسیب‌پذیری خاص مثل log4shell در ایمیج‌ها بررسی شود.

- فیلتر کردن نمایش و ذخیره کردن خروجی در یک فایل با استفاده از output و format

به صورت پیش‌فرض Trivy نتیجه‌ی آزمایش را در حالت جدول مانندی در خروجی نمایش می‌دهد. با استفاده از format , output می‌توانید فرمت خروجی را تغییر دهید. مثلا:

$ trivy  image hub.hamdocker.ir/library/httpd:2.4.43 --format json --output result.json

همچنین با استفاده از فرمت template می‌توانید فرمت خروجی اختصاصی خودتان را بسازید.

- پویش فایل سیستم

یکی دیگر از قابلیت‌های مهم Trivy امکان پویش فایل سیستم می‌باشد. با استفاده از این قابلیت می‌توان پکیج‌های مورد استفاده در یک پروژه (به جای کانتینر) را مورد پویش قرار داد. برای مثال در پروژه‌ی زیر نسخه‌های آسیب‌پذیر کتابخانه‌های پرکاربرد requests و pillow مورد استفاده قرار گرفته است.

محتویات فایل requirements.txt پروژه
محتویات فایل requirements.txt پروژه

با استفاده از دستور زیر پروژه مورد آزمایش قرار گرفته است. Trivy به صورت خودکار فایل requirements.txt را شناسایی و تجزیه می‌کند و سپس اقدام به شناسایی آسیب‌پذیری در پکیج‌ها می‌نماید. مسیر قرار گرفتن فایل requirements.txt و بقیه‌ی فایل‌های مشابه برای Trivy اهمیتی ندارد.

$ trivy fs /tmp/vulnerable_app
شناسایی آسیب‌پذیری‌های شناخته‌شده در کتابخانه‌های استفاده شده در پروژه
شناسایی آسیب‌پذیری‌های شناخته‌شده در کتابخانه‌های استفاده شده در پروژه

از همین ویژگی پویش فایل سیستم می‌توان برای اجرای عملیات پویش در زمان ساخت ایمیج یا در داخل یک کانتینر نیز استفاده کرد. برای مثال یک فایل requirements.txt و یک Dockerfile با محتویات زیر ایجاد کنید:

#requirements.txt
requests==2.6.0


#Dockerfile
FROM hub.hamdocker.ir/library/python:3.7-alpine3.11
COPY requirements.txt .
RUN apk add curl \
 && curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/master/contrib/install.sh | sh -s -- -b /usr/local/bin  && trivy fs --exit-code 1 --no-progress /

سپس با دستور زیر فرایند ساختن (build) ایمیج را فراخوانی کنید:

docker build -t test-fs .

بعد از اجرای دستور فوق فرایند ساختن ایمیج به علت وجود آسیب‌پذیری در بسته‌های سیستم‌عامل (نسخه‌ی قدیمی Alpine) و همین‌طور آسیب‌پذیری CVE-2018-18074 در کتابخانه‌ی requests با خطا خاتمه می‌باید. در تصاویر زیر این مورد نمایش داده شده است.

پویش بسته‌های سیستم‌عامل توسط Trivy
پویش بسته‌های سیستم‌عامل توسط Trivy
پویش کتابخانه‌های معین شده در requirements.txt  توسط Trivy
پویش کتابخانه‌های معین شده در requirements.txt توسط Trivy

- پویش تنظیمات ناامن (Misconfiguration)

توانایی شناسایی misconfiguration در سطح فایل‌های تنظیمات (مانیفست‌های) مربوط به Docker, Kubernetes, Terrafrom و ... قابلیت بسیار مهمی دیگری است که در Trivy پیاده‌سازی شده است. با استفاده از conf می‌توان تنظیمات خطرناک در این فایل‌ها را شناسایی کرد. برای مثال در زیر مانیفست ساخت یک پاد در Kubernetes نشان داده شده است. در تعریف این پاد دو مورد ناامن استفاده شده است.

استفاده از capability خطرناک SYS_ADMIN و حالت privileged در تعریف pod
استفاده از capability خطرناک SYS_ADMIN و حالت privileged در تعریف pod

مورد اول استفاده از capability SYS_ADMIN و مورد دوم اجرای کانتینر در حالت privileged می‌باشد. با استفاده از conf در مسیر جاری که فایل تعریف پاد در آن قرار دارد را مورد آزمایش قرار می‌دهیم:

$ trivy conf --severity HIGH .
شناسایی موارد خطرناک توسط Trivy
شناسایی موارد خطرناک توسط Trivy

همان‌طور که در تصویر بالا مشاهده می‌کنید Trivy فایل‌های موجود در مسیر جاری را تجزیه کرده و نوع آن‌ها را نیز به درستی از نوع مانیفست‌های Kubernetes تشخیص داده و هر دو تنظیم ناامن را نیز شناسایی کرده است. در مسیری که برای پویش تعیین می‌کنیم اگر فایل‌های دیگری نیز وجود داشته باشد Trivy می‌تواند آنها را شناسایی و تحلیل کند. برای تسلط بیشتر بر این قابلیت پیشنهاد می‌کنم مثال‌هایی که در مخزن پروژه آورده شده است را مطالعه کنید. همچنین برای آشنایی با سایر ویژگی‌ها نظیر حالت client/server و همچنین مطالعه‌ی دقیق‌تر راجع به آنها می‌توانید به آخرین نسخه‌ی مستندات پروژه Trivy در اینجا مراجعه کنید.

استفاده از 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=&quotregistry.hamdocker.ir/GROUP_NAME/PROJECT_NAME&quot
  - docker login -u &quot$REGISTRY_USER&quot -p &quot$REGISTRY_PASSWORD&quot &quot$REGISTRY&quot 
  - 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: [&quot&quot]
  variables:
    GIT_STRATEGY: none
    TRIVY_USERNAME: &quot$REGISTRY_USER&quot
    TRIVY_PASSWORD: &quot$REGISTRY_PASSWORD&quot
    TRIVY_AUTH_URL: &quot$REGISTRY&quot
    FULL_IMAGE_NAME: registry.hamdocker.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 &quot@/contrib/gitlab.tpl&quot --output &quotgl-container-scanning-report.json&quot &quot$FULL_IMAGE_NAME&quot
    # Fails on high and critical vulnerabilities
    - trivy image --exit-code 1 --cache-dir .trivycache/ --severity CRITICAL --no-progress &quot$FULL_IMAGE_NAME&quot
  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.hamdocker.ir/milad/myapp:$CI_COMMIT_SHORT_SHA

پس از اضافه کردن این فاز پس از هر commit در مخزن فرآیند build اجرا می‌شود و بعد از آن در فرآیند test عملیات پویش امنیتی ایمیج کانتینر صورت می‌گیرد. در این فاز بعد از آپدیت کردن دیتابیس Trivy پویش کامل ایمیج صورت می‌گیرد که نتیجه‌ی اجرای آن در فایل gl-container-scanning-report.json ذخیره می‌شود. در تصویر زیر مشاهده می‌کنیم که فرآیند پویش امنیتی به دلیل وجود آسیب‌پذیری با خطا خاتمه یافته است. در سمت راست در قسمت Job artifact می‌توانید گزارش پویش را دانلود و مشاهده کنید.

فرآیند test مربوط به پویش امنیتی با شکست خاتمه یافته است
فرآیند test مربوط به پویش امنیتی با شکست خاتمه یافته است


همچنین گزارش این پویش در بخش Security Dashboard > Vulnerability Report در دسترس می‌باشد.

نمایش فهرست آسیب‌پذیری‌های کشف شده در این پروژه در قسمت Vulnerability Report
نمایش فهرست آسیب‌پذیری‌های کشف شده در این پروژه در قسمت Vulnerability Report

در تصویر بالا مشاهده می‌کنیم که آسیب‌پذیری‌های کشف شده در فرآیند CI/CD در قسمت Vulnerability Report آورده شده است که برای افراد دخیل در پروژه قابل پیگیری می‌باشد.

همان‌طور که اشاره شد با هربار commit کردن در مخزن این فرآیند اجرا خواهد شد و اگر پروژه دارای آسیب‌پذیری با درجه‌ی CRITICAL باشد، job فعلی با خطا خاتمه می‌یابد و فرآیند deploy صورت نخواهد گرفت.

فرآیند test با خطا مواجه شده است.
فرآیند test با خطا مواجه شده است.

نتیجه‌گیری

در این نوشتار دیدیم که استفاده از ابزار Trivy ساده است و به راحتی می‌توان از آن در توسعه‌ی یک پروژه‌ و فرآیند CI/CD استفاده کرد. بروز بودن این ابزارها و قدرت‌شان در تشخیص پکیج‌ها و وابستگی‌ها در سطح سیستم‌عامل و زبان‌های برنامه‌نویسی بسیار قابل اتکاست، که با بهره‌گیری از آنها بخش زیادی از آسیب‌پذیری‌ها به سرعت در فاز توسعه کشف می‌شوند و از انتشار نسخه‌ی ناامن جلوگیری خواهد شد. در نظر داشته باشید که ممکن است حتی پس از انتشار یک نسخه نیز آسیب‌پذیری‌های در بسته‌ها و کتابخانه‌ها کشف شود. در این شرایط پیشنهاد می‌شود که با خودکار کردن فرآیند تست، به صورت پیوسته در زمان‌های معینی در طول روز همه‌ی کانتینر‌های در حال اجرا در کلاستر را از جنبه‌ی امنیت تست کنید.