DevSecOps وأمن سلسلة التوريد

فحص الحاويات والبنية التحتية كرمز

18 دقيقة الدرس 6 من 28

فحص الحاويات والبنية التحتية كرمز

ملف Dockerfile ليس مجرد سكريبت بناء — بل هو مواصفة لحدود الأمان. كل سطر FROM يرث سطح هجوم. كل RUN apt-get install يثبّت إصدار حزمة ستنجرف نحو نطاق CVE في اليوم الذي تتوقف فيه عن إعادة البناء. ملفات البنية التحتية كرمز (Terraform، وبيانيات Kubernetes، وملفات Helm، وقوالب CloudFormation) تحمل مخاطر موازية: حقل مُهيأ خطأً مثل privileged: true، أو حاوية S3 مكشوفة للعموم مُعرَّفة في HCL، خطيرة بالقدر نفسه الذي تكون فيه المكتبة الثغرة. تعلّمك هذه الدرس كيفية اكتشاف كلتا الفئتين في CI — قبل أن تصل الصورة أو المورد إلى أي بيئة حقيقية.

فحص CVE للصور: كيف يعمل فعلاً

تعمل ماسحات صور الحاويات باستخراج مخزون البرامج من نظام ملفات طبقي — حزم نظام التشغيل من /var/lib/dpkg/status أو /var/lib/rpm، وحزم اللغة من package-lock.json وgo.sum وPipfile.lock وما إلى ذلك — ثم مطابقة هذا المخزون مع قواعد بيانات الثغرات: NVD، وقاعدة بيانات استشارات GitHub، وتوصيات RedHat، وتغذيات خاصة بالتوزيعات. الأداتان المفتوحتا المصدر السائدتان هما Trivy (من Aqua Security) وGrype (من Anchore). تدعم كلتاهما الفحص بالاسم أو بملف tar أو مباشرةً من نظام الملفات — وهذا مهم لأنك تريد الفحص في CI قبل دفع الصورة، لا بعده.

Trivy هو المعيار الفعلي في خطوط الأنابيب الحديثة. إنه ثنائي واحد بدون daemon، يتعامل مع صور الحاويات وأنظمة الملفات ومستودعات git وملفات IaC في أداة واحدة، ويُصدر مخرجات SARIF تتكامل مع GitHub Advanced Security ولوحات أمان GitLab.

# فحص صورة محلية (مبنية لكن لم تُدفع بعد) — فشل CI عند HIGH أو CRITICAL trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:${CI_COMMIT_SHA} # الفحص من ملف tar محفوظ (مفيد عندما Docker daemon غير متاح في CI) docker save myapp:${CI_COMMIT_SHA} | trivy image --input /dev/stdin --exit-code 1 --severity HIGH,CRITICAL # إصدار SARIF لـ GitHub Advanced Security trivy image --format sarif --output trivy-results.sarif myapp:${CI_COMMIT_SHA} # .trivyignore — إخماد المخاطر المقبولة بمعرّف CVE وتاريخ انتهاء CVE-2023-44487 exp:2024-12-31 # HTTP/2 Rapid Reset — مُخفَّف على مستوى LB CVE-2024-21626 # هروب حاوية runc — إعادة بناء الصورة الأساسية معلّقة
انضباط كود الخروج: --exit-code 1 هو ما يجعل الماسح بوابة لا مجرد مُقرِّر. بدونه، سيسرد Trivy كل CVE ويخرج بـ0 — يبدو خط الأنابيب أخضر بينما يُشحن RCE حرجي. اضبط دائماً كود خروج صريح؛ اضبط عتبة الخطورة لتتوافق مع اتفاقية مستوى الخدمة لمؤسستك (عادةً HIGH,CRITICAL للحجب، MEDIUM للتحذير فقط).

استراتيجية الصورة الأساسية: أكبر رافعة

أسرع طريقة للقضاء على CVEs هي تصغير الصورة الأساسية. صورة نموذجية مبنية على ubuntu:22.04 تحمل 200 إلى 400 حزمة نظام تشغيل. صورة gcr.io/distroless/base-debian12 تحمل نحو 20. ثنائي Go مبني على scratch لا يحمل أي حزمة. في Google، تُعدّ صور distroless الافتراض الافتراضي لجميع أحمال عمل الإنتاج — ليس لأنها رائجة بل لأن تقليص سطح الهجوم دراماتيكي وقابل للقياس.

يحقق نمط Dockerfile متعدد المراحل المعياري هذا دون التضحية بسهولة المطورين: البناء في صورة SDK كاملة، ونسخ الأداة المُجمَّعة فقط إلى صورة تشغيل مُصغَّرة.

# بناء متعدد المراحل: البناء في SDK الكامل، التشغيل في distroless FROM golang:1.22-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /app/server ./cmd/server # مرحلة التشغيل — distroless لا يحتوي shell ولا مدير حزم ولا أدوات FROM gcr.io/distroless/static-debian12:nonroot COPY --from=builder /app/server /server USER nonroot:nonroot EXPOSE 8080 ENTRYPOINT ["/server"]
ثبّت الـ digest لا الـ tag. FROM golang:1.22-alpine قابل للتغيير — يمكن إعادة كتابة التاغ بصمت إلى صورة مختلفة (ربما ثغرة). استخدم FROM golang:1.22-alpine@sha256:<digest> في Dockerfiles الإنتاج. يتعامل Dependabot وRenovate مع تحديثات الصور الأساسية المُثبَّتة بالـ digest تلقائياً.

فحص IaC: اكتشاف الأخطاء قبل التطبيق

تُرمَّز وضعيتك الأمنية في Terraform وبيانيات Kubernetes وملفات Helm وقوالب CloudFormation كرمز. privileged: true في DaemonSet، أو حاوية S3 مع block_public_acls = false، أو قاعدة مجموعة أمان مع cidr_blocks = ["0.0.0.0/0"] على المنفذ 22، كلها أخطاء إعداد ستصل إلى الإنتاج لحظة تشغيل terraform apply أو kubectl apply — ما لم تفحصها أولاً.

الماسحان الرائدان لـ IaC هما Checkov (من Bridgecrew/Prisma Cloud) وماسح الإعدادات المدمج في Trivy. يدعم كلاهما Terraform HCL وKubernetes YAML وHelm وDockerfile وCloudFormation وقوالب ARM. للتحكم الدقيق بسياسة كرمز، يتيح لك OPA Conftest كتابة سياسات Rego مخصصة ضد أي ملف إعداد منظّم.

# فحص جميع ملفات Terraform في الدليل الحالي trivy config --exit-code 1 --severity HIGH,CRITICAL ./terraform/ # Checkov مع مخرجات SARIF — يتكامل مع GitHub Advanced Security checkov -d ./terraform --output sarif --output-file-path checkov-results.sarif # Conftest: التحقق من بيانيات K8s ضد سياسات Rego مخصصة conftest test ./k8s/deployment.yaml --policy ./policies/ # نموذج سياسة Rego — حظر الحاويات المميزة # policies/no_privileged.rego package main deny[msg] { input.kind == "Deployment" container := input.spec.template.spec.containers[_] container.securityContext.privileged == true msg := sprintf("Container %s must not run as privileged", [container.name]) }

معمارية الفحص في CI

على النطاق الإنتاجي، تعمل مهام فحص الصور وIaC بالتوازي مع مراحل CI الأخرى لتجنب إضافة كمون على المسار الحرج. التخطيط المعياري لخط أنابيب GitHub Actions:

CI scanning pipeline: parallel security gates before image push git push Triggers CI Build Image docker build Trivy Image CVE scan Trivy Config IaC misconfig Checkov Terraform HCL Security Gate Push Registry Parallel scan jobs — all must pass before image is pushed to registry
ثلاث مهام فحص أمني تعمل بالتوازي بعد البناء؛ يجب أن تخرج جميعها بـ0 قبل ترقية الصورة إلى سجل الحاويات.
# .github/workflows/security-scan.yml — بوابات متوازية للصورة وIaC jobs: build: runs-on: ubuntu-latest outputs: image: ${{ steps.meta.outputs.tags }} steps: - uses: actions/checkout@v4 - name: بناء الصورة (محلي، لم تُدفع بعد) run: docker build -t myapp:${{ github.sha }} . trivy-image: needs: build runs-on: ubuntu-latest steps: - name: تشغيل فحص Trivy CVE uses: aquasecurity/trivy-action@0.20.0 with: image-ref: myapp:${{ github.sha }} exit-code: '1' severity: 'HIGH,CRITICAL' format: sarif output: trivy-image.sarif - uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: trivy-image.sarif trivy-config: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: تشغيل فحص Trivy IaC uses: aquasecurity/trivy-action@0.20.0 with: scan-type: config scan-ref: . exit-code: '1' severity: 'HIGH,CRITICAL' push: needs: [trivy-image, trivy-config] runs-on: ubuntu-latest steps: - name: دفع إلى السجل run: docker push myapp:${{ github.sha }}

أبرز النتائج الحرجة الشائعة وكيفية إصلاحها

فهم أكثر النتائج عالية الخطورة شيوعاً يساعدك في تحديد أولوية المعالجة. هذه الأنماط التي تظهر باستمرار عبر المؤسسات الهندسية الكبرى:

  • التشغيل كمستخدم root — الافتراض في تقريباً كل Dockerfile. الإصلاح: أضف USER 1001:1001 أو استخدم USER nonroot من distroless. في Kubernetes: فرّض ذلك بسياسة PodSecurity (runAsNonRoot: true).
  • نظام ملفات جذر قابل للكتابة — يتيح لمهاجم يحقق تنفيذ كود الإصرار على التغييرات. الإصلاح: readOnlyRootFilesystem: true في securityContext الحاوية، مع مُركَّبات emptyDir صريحة للمسارات القابلة للكتابة التي يحتاجها التطبيق.
  • الحاوية المميزة — مكافئة لامتلاك صلاحيات root على عقدة المضيف. لا يجب أن تظهر خارج إضافات CNI/التخزين المحددة جداً. تسامح صفري: privileged: false يجب أن يكون سياسة لا تذكيراً.
  • حدود الموارد المفقودة — ليست CVE لكنها HIGH في Checkov: حاوية غير مقيّدة يمكنها قتل جيرانها بنفاد الذاكرة. اضبط دائماً resources.limits.cpu وresources.limits.memory.
  • حزم نظام تشغيل قديمة في الصورة الأساسية — المصدر الأكثر شيوعاً لـ CVEs. الإصلاح: أعد البناء من أساس حالي أسبوعياً عبر طلبات سحب Dependabot/Renovate الآلية، أو استخدم صورة distroless للقضاء على طبقة نظام التشغيل كلياً.
مشكلة إنتاجية — تجاهل CVEs إلى أجل غير مسمى: كل إدخال في .trivyignore يجب أن يحمل تاريخ انتهاء (exp:YYYY-MM-DD) وتذكرة مرتبطة. بدون انتهاء صلاحية، تتراكم CVEs المُخمَّتة بصمت. نمط حادثة شائع: فريق يُخمَّت CVE بانتظار ترقية صورة أساسية، الترقية تُؤجَّل، تُستغَلّ الثغرة بعد ستة أشهر — ويكشف ما بعد الحادثة أن الماسح كان يُبلّغ عنها باللون الأخضر طوال الوقت.

الفحص في التحكم بالقبول: الجدار الأخير

بوابات فحص CI ضرورية لكنها غير كافية وحدها. يمكن للمهندسين دفع صور عبر CLI، ويمكن تجاوز خطوط أنابيب CI، وقد تدخل صور طرف ثالث إلى مجموعتك من ملفات Helm. يضيف نهج الدفاع المتعمق فحصاً عند وقت القبول عبر سياسات Kyverno أو OPA Gatekeeper التي تتحقق من مصدر الصورة وحالة الفحص قبل جدولة الـ pods.

سياسة Kyverno تحجب الصور غير المفحوصة بـ Trivy في آخر 24 ساعة (باستخدام تعليق التوثيق الذي يضعه CI) تبدو هكذا:

# kyverno-policy: اشتراط تعليق توثيق الفحص apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: require-image-scan spec: validationFailureAction: Enforce rules: - name: check-scan-annotation match: resources: kinds: [Pod] validate: message: "Image must have a valid Trivy scan annotation from the last 24h" pattern: spec: containers: - image: "*" # يضع CI هذا التعليق: kubectl annotate ... trivy-scanned=<timestamp> initContainers: "<*>"

على النطاق الكبير، تتكامل الفرق مع سجل حاويات يدعم الفحص المستمر في الخلفية — Docker Hub وECR وGCR وArtifact Registry جميعها تقدم هذا. يفحص السجل كل صورة عند الدفع ويعيد الفحص يومياً مقابل تغذيات CVE الجديدة، لذا حتى الصور التي اجتازت CI أمس تُشير إليها إذا ظهرت توصية حرجة جديدة بين عشية وضحاها. تبقى وضعية وقت التشغيل حديثة دون الحاجة إلى بناء جديد.

الوضعية الشاملة للفحص — بوابة CI تحجب عند البناء، وبوابة IaC تحجب عند التخطيط، وتحكم القبول يحجب عند الجدولة، وفحص السجل يُشير عند ظهور CVEs جديدة — تغلق النافذة من إنشاء الصورة إلى النشر إلى وقت التشغيل دون أي نقطة فشل منفردة. كل طبقة تصطاد ما فاتت الطبقة السابقة.