يتطلب مستوى: محترف
لو بتـ deploy صور Docker من غير ما تفحصها، انت بتشحن ثغرات معروفة للإنتاج وانت مش حاسس. الجملة دي مش تهويل: أغلب الصور الجاهزة بتيجي ومعاها عشرات الثغرات المسجّلة. هنا هتبني بوابة Trivy في الـ CI توقف أي صورة مصابة قبل ما توصل السيرفر، في أقل من 15 ثانية لكل build، وبصفر تكلفة.
المشكلة باختصار
خلّينا نبدأ بمثال بسيط للمبتدئ. تخيّل بوابة تفتيش في المطار. كل شنطة بتعدّي على جهاز الأشعة، والجهاز بيقارن اللي جوّاها بقائمة أشياء ممنوعة معروفة. لو لقى حاجة خطيرة، الشنطة بتتوقف قبل ما تركب الطيارة. مش منطقي إن الجهاز يعرف إن فيه سكينة جوّه ويسيبها تعدّي.
دلوقتي بالتفاصيل العلمية. صورة Docker مش ملف واحد، هي طبقات فيها مئات الحزم: نظام تشغيل أساسي (Debian مثلًا)، مكتبات نظام (OpenSSL، zlib)، ثم اعتماديات مشروعك. أي حزمة فيها ثغرة متسجّلة برقم CVE (رقم موحّد عالميًا للثغرة) في قاعدة NVD التابعة للحكومة الأمريكية، بتبقى باب دخول محتمل للمهاجم. Trivy هو جهاز الأشعة: بيقرأ كل حزمة في الصورة، يقارنها بقواعد الثغرات، ويقولك بالظبط أنهي حزمة فيها إيه وأنهي إصدار بيصلّحها.
الافتراض هنا: عندك pipeline CI (GitHub Actions أو GitLab CI) بيبني صورة ويدفعها لـ registry قبل الإنتاج. لو لسه بتبني وبتـ push بإيدك من اللاب توب، البوابة دي مش هتشتغل لوحدها.
ليه صورتك مليانة ثغرات وانت مكتبتش الكود ده
ركز في النقطة دي لأنها بتوفر 90% من المجهود. أغلب الثغرات مش في كودك، هي في الصورة الأساسية اللي بنيت عليها. لو بدأت بـ node:20 الكاملة، انت ورّثت توزيعة لينكس كاملة بكل أدواتها.
node:20الكاملة: وقت كتابة المقال بترجّع أكتر من 100 ثغرة بدرجة HIGH/CRITICAL (الرقم بيتغيّر يوميًا مع اكتشاف ثغرات جديدة).node:20-slim: بتنزّلهم لعشرات قليلة، لأنها بتشيل أدوات مش محتاجها في الإنتاج.gcr.io/distroless/nodejs20: قريبة من صفر، لأنها فيها الـ runtime بس من غير shell ولا package manager.
الـ trade-off هنا واضح: distroless بتوفّر لك أمان ومساحة، بتخسر إن الـ debugging أصعب (مفيش bash جوّه الحاوية). لو فريقك بيدخل على الحاويات في الإنتاج بـ exec كتير، ابدأ بـ slim مش distroless.
الحل: خلّي Trivy بوابة في الـ CI
الطريقة الشائعة الغلط: تشغّل Trivy وتطبع النتيجة في اللوج بس. ده بيفشل لأن محدّش بيقرأ اللوج. البديل الصح: خلّي الأمر يرجّع exit-code 1 لما يلاقي ثغرة، فالـ build نفسه يفشل ويمنع الدمج.
أبسط أمر تشغّله محليًا الأول:
trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 myapp:latest--ignore-unfixed نقطة مهمة: هي بتتجاهل الثغرات اللي لسه مفيش لها إصلاح. الافتراض إنك مش عايز توقف الـ deploy على حاجة معندكش حيلة تصلّحها دلوقتي. بتكسب هدوء في الـ CI، بتخسر إنك ممكن تنسى ثغرة لحد ما يطلع لها fix؛ فعوّضها بفحص دوري للصور الشغّالة فعلًا في الإنتاج.
دي البوابة في الـ CI. لاحظ إن الترتيب: ابنِ الصورة، افحصها، وبعدين بس ادفعها.
name: security-scan
on: [pull_request]
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:ci-image .
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: myapp:ci-image
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: '1'
format: tableلو محتاج تتجاهل ثغرة معيّنة بشكل واعٍ (مثلًا false positive أو مقبولة في سياقك)، حطها في ملف .trivyignore: تكتب رقم الـ CVE في سطر مع تعليق فوقه يوضّح ليه اتقبلت وامتى هتتراجع.
الأرقام: قبل وبعد
على جهاز عادي، Trivy بيفحص صورة حجمها ~400MB في حوالي 6 إلى 12 ثانية بعد أول تحميل لقاعدة الثغرات. ده معناه إن البوابة بتضيف أقل من ربع دقيقة على كل build. المقابل: بدل ما تكتشف إن صورتك فيها 37 ثغرة معروفة من تقرير أمني بعد الإنتاج، بترفضها في الـ PR قبل ما تتدمج أصلًا. فرق التكلفة بين إصلاح ثغرة قبل الدمج وإصلاحها بعد وصولها للإنتاج معروف إنه أضعاف مضاعفة، حسب تقارير هندسة البرمجيات المعتمدة.
الـ trade-offs اللي لازم تعرفها
- ضجيج الإنذارات: من غير
--ignore-unfixedهتغرق في ثغرات معندكش لها حل. الفلتر بيقلّل الضجيج بس بيأجّل مشاكل حقيقية. - تحديث قاعدة البيانات: Trivy بيحمّل قاعدة الثغرات من الإنترنت. في بيئة CI مغلقة، لازم تعمل mirror للقاعدة، وإلا الفحص هيفشل لأسباب مالهاش علاقة بالأمان.
- الحسم الصارم بيوقف الفريق: لو خليت أي MEDIUM يفشل الـ build، هتلاقي الناس بتعطّل البوابة. ابدأ بـ HIGH وCRITICAL بس، وشدّ تدريجيًا.
متى لا تستخدم بوابة Trivy
مش كل حالة محتاجة البوابة الصارمة. لو المشروع بروتوتايب داخلي عمره أسبوع ومش هيوصل مستخدم حقيقي، الحسم الصارم هيعطّلك من غير عائد. وكمان لو الفريق لسه مبيصلّحش الثغرات الموجودة أصلًا، تفعيل exit-code 1 فجأة هيكسر كل الـ builds؛ في الحالة دي شغّل Trivy في وضع تقرير بدون فشل لأسبوعين، نظّف المتراكم، وبعدين فعّل البوابة.
الخطوة التالية
افتح أي مشروع عندك فيه Dockerfile، وشغّل الأمر: trivy image --severity HIGH,CRITICAL --ignore-unfixed اسم-صورتك. لو رجّعلك أكتر من 10 ثغرات، أول تحرّك مش تصلّح كل واحدة — بدّل الصورة الأساسية لـ slim أو distroless وأعد الفحص. غالبًا الرقم هيقع للربع من سطر واحد.
المصادر
- توثيق Trivy الرسمي — Aqua Security: trivy.dev/latest/docs
- إجراء GitHub Actions الرسمي لـ Trivy: github.com/aquasecurity/trivy-action
- قاعدة الثغرات الوطنية NVD ونظام ترقيم CVE: nvd.nist.gov
- صور Distroless من Google: github.com/GoogleContainerTools/distroless
- أفضل ممارسات بناء صور Docker: docs.docker.com/build/building/best-practices