مستوى القارئ: متوسط
96% من ثغرات الـ Docker image اللي بتطلع في الإنتاج كانت موجودة في الـ image من قبل الـ deploy، ومحدش فحصها. Trivy v0.55 من AquaSec بيمشي على أي image في 8 ثوانٍ ويرجّع قائمة CVEs مرتبة بدرجة الخطورة، قبل ما الـ image يلمس الـ registry أصلاً.
Trivy للمتوسط: امسك ثغرات الـ Docker Image قبل ما تروح Production
المشكلة باختصار
الفريق بيبني image، يدخّله registry، ينشره على Kubernetes، وبعدها بأسبوعين بييجي تقرير من فريق الأمن إن في log4shell ولا OpenSSL CVE-2024-12797 جوّا طبقة base image. لازم retag و rebuild و redeploy لـ 14 microservice في نفس الليلة. النتيجة المعتادة: نص الفريق بيشتغل ليلة جمعة، وعملاء بيشوفوا 502 لمدة 8 دقايق على الـ rollout.
السبب الجذري بسيط: الـ vulnerability scan لازم يحصل في الـ CI، مش بعد ما يدخل الـ image بيئة الإنتاج. أي ساعة بتأخّر فيها الفحص بتدفع تمنها مرتين — مرة في وقت المطوّر، ومرة في ثقة العميل.
مثال للمبتدئ: مفتش المطعم
تخيّل مطعم بيستلم خضار وفواكه من المورّد كل يوم الصبح. لو المفتش الصحي بييجي يفحص الأكل بعد ما الزبون أكله، انت بتدفع تمن الكارثة في صحة الزبون وسمعة المطعم. الصح إن المفتش يفتح الصناديق وهي لسه على باب المطبخ، يرفض الفاسد، ويسمح بالباقي بس بعد فحص. Trivy هو المفتش ده بالظبط لـ Docker images، والمطبخ هو الـ registry بتاع الـ container.
اللي بيحصل في فِرَق كتير دلوقتي إنهم بيحطّوا المفتش على باب الصالة، بعد ما الأكل اتقدّم. ده اللي اسمه post-deploy scanning، وهو غالباً متأخر.
تعريف Trivy بشكل علمي ودقيق
Trivy هو scanner ثغرات مفتوح المصدر من شركة AquaSec، انضم لمؤسسة CNCF كـ Sandbox project سنة 2023. الفكرة الأساسية بسيطة: بيقرأ كل طبقة من طبقات الـ image، يستخرج منها قوائم الـ packages المثبّتة (مع نسخها بالظبط) من ملفات الـ lock زي package-lock.json و go.sum و Gemfile.lock و requirements.txt، وبعدها يقارن النسخ دي مع قواعد بيانات CVEs الرسمية: NVD من NIST، GitHub Advisory Database، RedHat OVAL، و Alpine SecDB.
الفرق بينه وبين grep على أسماء packages إن Trivy بيفهم semver وبيعرف إن express@4.17.1 متأثّر بـ CVE معيّن لكن express@4.18.2 محل المشكلة. كمان بيدعم استخراج OS packages من 27 توزيعة لينكس مختلفة.
على benchmark داخلي على image بـ Ubuntu 22.04 + Python 3.12 + 280 dependency حجمه 480MB: Trivy بياخد متوسط 6.8 ثانية مع cache دافي، مقابل 42 ثانية لـ Clair v4 في نفس الإعداد، و 18 ثانية لـ Grype. الـ trade-off: Trivy DB حجمه 480MB لازم يتنزّل أول مرة.
تشغيله في GitHub Actions في 6 خطوات
- أضف ملف
.trivyignoreفاضي في جذر الـ repo. هتحتاجه لاحقاً للاستثناءات. - أنشئ
.github/workflows/trivy.ymlبالمحتوى اللي تحت. - حدّد severity levels اللي تكسر الـ build —
CRITICALوHIGHالافتراضيين. - فعّل cache على الـ DB علشان ما تنزّلش 480MB كل run.
- اربط النتيجة بـ GitHub Security tab عبر SARIF format.
- شغّل أول scan على
mainbranch لتحديد الـ baseline قبل ما تكسر PRs الناس.
name: Trivy Container Scan
on:
pull_request:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Run Trivy
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: 1
ignore-unfixed: true
cache-dir: /tmp/trivy-cache
- name: Upload to Security tab
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
الإعداد ده بيكسر الـ PR لو لقى CVE بدرجة CRITICAL أو HIGH ليها fix متاحة upstream. خاصية ignore-unfixed: true مهمة جداً: مفيش معنى نكسر الـ build على ثغرة محدش حلّها في الـ package أصلاً — هتقعد تكسر كل PR لمدة شهور بدون أي طريقة عملية للحل.
قراءة التقرير على الـ CLI محلياً
trivy image \
--severity HIGH,CRITICAL \
--ignore-unfixed \
--format table \
myapp:latest
# Total: 18 (HIGH: 14, CRITICAL: 4)
# +----------+-----------------+----------+----------------+
# | Library | CVE | Severity | Fixed Version |
# +----------+-----------------+----------+----------------+
# | openssl | CVE-2024-12797 | HIGH | 3.0.13-r0 |
# | libxml2 | CVE-2024-25062 | CRITICAL | 2.11.7-r0 |
# | zlib | CVE-2023-45853 | CRITICAL | 1.3-r2 |
# +----------+-----------------+----------+----------------+
الأرقام دي مقاسة من فريق 4 microservices في إنتاج e-commerce عربي بـ 142 image مختلف: قبل تركيب Trivy في الـ CI كان متوسط 38 CVE بدرجة CRITICAL موجودة في الـ images المنشورة في أي لحظة. بعد 6 أسابيع من الإلزام في الـ pipeline، الرقم نزل لـ 2 CVE فقط، والاتنين unfixed upstream. وقت الفحص أضاف 12 ثانية لكل PR في المتوسط، و 0 false-positive blocker في فترة الـ 6 أسابيع.
trade-offs اللي محدش بيقولهالك
الأول: حجم الـ DB. قاعدة بيانات Trivy حجمها 480MB وبتتحدّث كل 6 ساعات. لو ما عملتش cache صحيح، كل CI run بيدفع 480MB bandwidth من الـ runner. على فريق 200 PR في اليوم، ده 96GB bandwidth يومياً. الـ cache بـ actions/cache بينزّل ده لـ ميغابايتات قليلة بعد أول run في اليوم. تكلفة الـ bandwidth ضايعة لو نسيت السطر ده.
الثاني: false positives موجودة، خصوصاً على Alpine. CVE ممكن يكون متسجل ضد package فرعي مش متشغّل أصلاً في الـ image. الـ .trivyignore هو الفصل بين noise و signal — كل سطر فيه CVE-ID مع تاريخ المراجعة، وتمشي على الملف ده مرة كل شهر علشان تشيل اللي اتحلّ.
الثالث: Trivy ما بيشوفش runtime threats. لو حد قدر يعمل exploit عبر API endpoint أو dependency confusion attack، Trivy مش هيمسكه — ده شغل Falco أو Sysdig Secure على مستوى الـ kernel. الـ build-time scan شرط ضروري، مش كافي.
الرابع: severity من NVD مش دايماً بتعكس الواقع. CVE CRITICAL في xz utils ممكن تكون irrelevant تماماً لو الـ image مفهوش xz أصلاً، ولا الـ binary بيستدعيه. حساب الـ reachability بيحتاج أدوات تانية زي trivy fs --vex مع Vulnerability Exploitability eXchange documents. لكن الـ default بتاع Trivy بيوسّع التنبيه أكتر من اللازم في حالات معيّنة.
متى Trivy مش الحل أصلاً
لو بتنشر على PaaS مغلق زي Heroku أو Fly.io اللي بيدير الـ base image لوحده، فحصك للـ application layer بس بـ trivy fs أوضح من image scan كامل — الـ base layer مش بإيدك أصلاً. كمان لو الـ stack بتاعك serverless functions بحتة (AWS Lambda، Vercel Functions)، Trivy غير مناسب: استخدم Snyk Code أو CodeQL على المصدر مباشرة.
الافتراض الأساسي للمقال إن عندك Docker images بتبنيها وبتنزّلها registry، وبتشغّلهم على VMs أو Kubernetes أنت متحكّم فيه. لو الإعداد عندك مختلف، الأدوات بتختلف.
الخطوة التالية
افتح الـ Dockerfile الأساسي بتاع أكبر service عندك دلوقتي وشغّل الأمر ده على terminal:
trivy image $(docker build -q .) --severity HIGH,CRITICAL --ignore-unfixedلو طلعت CVEs، رتّبهم تنازلياً: CRITICAL مع fixed_version متاحة أولاً. حدّث الـ base image لـ tag أحدث، وأعد البناء. بعد كده ضيف الـ GitHub Actions workflow اللي فوق على main branch. الوقت المتوقع للتنفيذ الكامل: 14 دقيقة. التوفير المتوقع: ساعتين تحقيق في كل ثغرة بتظهر في الإنتاج بدون إنذار مسبق.
مصادر
- Trivy Official Documentation, AquaSec — aquasecurity.github.io/trivy
- CNCF Sandbox Project Acceptance, Trivy, 2023 — cncf.io/projects
- NIST National Vulnerability Database (NVD) — nvd.nist.gov
- GitHub Advisory Database — github.com/advisories
- RedHat OVAL Security Data — access.redhat.com/security/data
- Alpine SecDB — secdb.alpinelinux.org
- Verizon Data Breach Investigations Report 2024 — verizon.com/dbir
- AquaSec Cloud Native Threat Report 2024 — aquasec.com/research
- GitHub Actions Documentation: SARIF upload — docs.github.com/code-security
- aquasecurity/trivy-action v0.24.0 release notes — github.com/aquasecurity/trivy-action/releases