Docker المتقدم وأمن الحاويات

توقيع الصور وإثبات المصدر

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

توقيع الصور وإثبات المصدر

فحص الصورة بحثاً عن الثغرات يُخبرك بالمشكلات الموجودة الآن، لكنه لا يستطيع إخبارك بمن بنى الصورة، أو أي كود مصدري أنتجها، أو إن كانت قد تم التلاعب بها أثناء النقل. يسد توقيع الصور وإثبات المصدر هذه الثغرة. يجيبان على السؤال الذي يجب أن يطرحه كل نظام إنتاجي قبل سحب صورة: "هل يمكنني إثبات أن هذه القطعة الأثرية هي بالضبط ما أنتجه نظام CI الخاص بي، ولا شيء غير ذلك؟"

لماذا سطح هجوم سلسلة التوريد حقيقي

أثبت اختراق SolarWinds عام 2020 وحادثة codecov أن المهاجمين لا يخترقون دائماً التطبيق المُشغَّل — بل يخترقون خط البناء ويسمّمون القطعة الأثرية قبل أن تصل إلى الإنتاج. صور الحاويات هدف ذو قيمة عالية: صورة أساسية مسمومة واحدة يمكنها الانتشار إلى آلاف الصور المشتقة عبر مئات الفرق.

يُنشئ سير عمل توقيع الصور القوي سلسلة حراسة مشفرة من التزام كود المصدر إلى الـ pod المُشغَّل. كل حلقة في تلك السلسلة — مستودع المصدر، ونظام البناء، والسجل، وبيئة التشغيل — يجب أن تكون قابلة للتحقق.

مصطلحات أساسية: التوقيع يُرفق توقيعاً مشفراً بـ digest الصورة بحيث يمكن لأي شخص يملك المفتاح العام التحقق من أنها صدرت من الجهة المتوقعة. إثبات المصدر (Provenance) هو تصديق موقَّع يُسجِّل كيفية بناء الصورة — أي مستودع، أي SHA للتزام، أي سير عمل، أي بناء — لتتمكن من مراجعة سلسلة الحراسة الكاملة.

Sigstore و Cosign

Sigstore مشروع من Linux Foundation (يدعمه Google وRed Hat وChainguard) يجعل التوقيع بلا مفاتيح طويلة الأمد وقابلاً للمراجعة. أداته الأساسية هي cosign. تتكون البنية من ثلاثة مكونات:

  • Fulcio — جهة إصدار شهادات تُصدر شهادات توقيع قصيرة الأمد مرتبطة بهويات OIDC (سير عمل GitHub Actions، أو حساب خدمة GCP، إلخ).
  • Rekor — سجل شفافية يُضاف إليه فقط ولا يُحذف منه، يسجل كل حدث توقيع، بحيث يمكنك التحقق من أن التوقيع صدر في وقت محدد واكتشاف ما إذا تم تأريخه بأثر رجعي.
  • cosign — واجهة سطر الأوامر التي تنسق التوقيع والتحقق والتصديق بالتنسيق مع تلك الخدمات.
Image signing and provenance supply chain Source Repo commit SHA CI Build GitHub Actions cosign sign OIDC identity Fulcio CA short-lived cert Rekor Log transparency Registry image + signature Runtime / K8s cosign verify SLSA Attestation provenance JSON push trigger cert log push sig verify attest attach
سلسلة التوريد الكاملة: التزام المصدر يُطلق بناء CI الذي يوقع الصورة عبر Fulcio وRekor، ويُرفق تصديق مصدر SLSA، ويدفع إلى السجل — حيث تتحقق بيئة التشغيل من كل شيء قبل القبول.

التوقيع بلا مفاتيح في GitHub Actions

مع التوقيع بلا مفاتيح، لا يوجد مفتاح خاص طويل الأمد يحتاج إلى دوران أو عرضة للتسريب. يتبادل سير عمل CI رمز OIDC الخاص به للحصول على شهادة قصيرة الأمد من Fulcio، يوقع بها digest الصورة، وتُسجَّل الشهادة (وحدث التوقيع) بشكل دائم في Rekor. يمكن لأي مراقب لاحقاً الاستعلام من Rekor للتأكد من أن التوقيع صدر من سير عمل GitHub Actions الشرعي الخاص بمستودعك.

# .github/workflows/build-sign.yml name: Build, Push & Sign on: push: branches: [main] permissions: contents: read packages: write id-token: write # مطلوب للتوقيع بلا مفاتيح (OIDC) jobs: build-sign: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push (capture digest) id: build uses: docker/build-push-action@v5 with: context: . push: true tags: ghcr.io/${{ github.repository }}:${{ github.sha }} - name: Install cosign uses: sigstore/cosign-installer@v3 # الوقيع بالـ digest وليس بالوسم — الوسوم قابلة للتغيير؛ الـ digests ليست كذلك - name: Sign image (keyless) run: | cosign sign --yes \ ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }} # توليد وإرفاق تصديق مصدر SLSA - name: Attest provenance run: | cosign attest --yes \ --predicate <(echo \'{"buildType":"https://github.com/actions","builder":{"id":"${{ github.workflow_ref }}"},"invocation":{"configSource":{"uri":"${{ github.repositoryUrl }}","digest":{"sha1":"${{ github.sha }}"}}}}') \ --type slsaprovenance \ ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
وقّع دائماً بالـ digest (@sha256:...) وليس بالوسم. يمكن إعادة توجيه وسوم مثل :latest إلى صورة مختلفة في أي وقت. التوقيع على digest يُنشئ ربطاً غير قابل للتزوير بين بصمة المحتوى المشفرة والتوقيع — وهو ثابت بطبيعته.

التحقق من التوقيعات

يؤكد التحقق ثلاثة أشياء في آنٍ واحد: التوقيع صالح تشفيرياً، والشهادة صدرت من Fulcio من هوية OIDC المتوقعة، والحدث مُسجَّل في Rekor. إذا فشل أي من هذه الفحوصات، خرجت cosign بكود غير صفري.

# التحقق من التوقيع بلا مفاتيح — فحص هوية الشهادة والجهة المُصدِرة cosign verify \ --certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build-sign.yml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ghcr.io/myorg/myrepo@sha256:abc123... # التحقق وطباعة إدخال سجل Rekor الكامل cosign verify \ --certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build-sign.yml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ghcr.io/myorg/myrepo@sha256:abc123... \ | jq . # التحقق من تصديق مصدر SLSA cosign verify-attestation \ --type slsaprovenance \ --certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build-sign.yml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ghcr.io/myorg/myrepo@sha256:abc123... \ | jq .payload | base64 -d | jq .

فرض التحقق من التوقيع عند القبول — وحدات تحكم السياسة

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

  • Sigstore Policy Controller (من مشروع Sigstore) — خطاف قبول Kubernetes يتحقق من توقيعات cosign والتصديقات بالتنسيق مع CRD يُسمى ClusterImagePolicy.
  • Kyverno — محرك سياسات Kubernetes للأغراض العامة مع دعم أول لـ cosign. مُعتمَد على نطاق واسع في الشركات الكبرى التي تحتاج أيضاً إلى سياسات غير متعلقة بالصور (مثل: اشتراط الوسوم، حظر رفع الامتيازات).
# Kyverno ClusterPolicy: السماح فقط بالصور الموقَّعة من سير عمل CI الخاص بك apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: require-signed-images spec: validationFailureAction: Enforce background: false rules: - name: check-image-signature match: any: - resources: kinds: [Pod] namespaces: [production, staging] verifyImages: - imageReferences: - "ghcr.io/myorg/myrepo*" attestors: - count: 1 entries: - keyless: subject: "https://github.com/myorg/myrepo/.github/workflows/build-sign.yml@refs/heads/main" issuer: "https://token.actions.githubusercontent.com" rekor: url: https://rekor.sigstore.dev
وضع Enforce سيحجب كل الصور غير الموقَّعة، بما فيها صورك إذا كان سير العمل مُهيَّأ بشكل خاطئ. ابدأ في وضع Audit أولاً: تُسجَّل الانتهاكات لكنها لا تُحجب. انتقل إلى Enforce فقط بعد التحقق من أن كل عبء عمل في الفضاء المستهدف يجتاز الفحص. تسببت فرق كثيرة في انقطاعات بتفعيل وضع الفرض قبل ترحيل كل الصور.

Docker Content Trust مع Notary (سياق القديم)

قبل Sigstore، أطلق Docker نظام Docker Content Trust (DCT) المدعوم بـ Notary v1. قد تصادفه في أنظمة قديمة وسجلات المؤسسات. يُفعَّل DCT بـ DOCKER_CONTENT_TRUST=1 ويستخدم بنية تحتية مستقلة للمفاتيح. يعمل لكنه يُشكّل عبئاً تشغيلياً كبيراً (حفل مفتاح الجذر، مفاتيح التفويض، دوران المفاتيح) مقارنة بـ Sigstore بلا مفاتيح. المشاريع الجديدة يجب أن تستخدم cosign. إذا كنت تُشغِّل نظاماً مبنياً على Notary، فكّر في الترحيل — Notary v2 (أُعيدت تسميته الآن إلى ORAS Notation) يتوافق أكثر مع معايير OCI، وcosign تبقى الخيار السائد في المنظومة.

مستويات SLSA — إطار النضج

SLSA (Supply-chain Levels for Software Artifacts، تُنطق "salsa") إطار عمل من Google يُحدد أربعة مستويات لسلامة سلسلة التوريد. معظم الفرق تستهدف مستوى SLSA 2 أو 3 للصور الإنتاجية:

  • المستوى 1 — سكريبتات البناء موجودة ويُولَّد إثبات المصدر (لا يتطلب تحققاً).
  • المستوى 2 — بناء CI مستضاف (مثل GitHub Actions)، إثبات المصدر موقَّع من خدمة البناء. يغطي الغالبية العظمى من عمليات النشر الواقعية.
  • المستوى 3 — منصة بناء مُحكمة: بيئات بناء مؤقتة، بدون بيانات اعتماد دائمة في البناء، مراجعة من طرفين مطلوبة. Google وGitHub يعملان داخلياً على هذا المستوى.
  • المستوى 4 — بنيات محكمة وقابلة للتكرار مع موافقة شخصين. نادر جداً خارج مشاريع أنظمة التشغيل وبرامج التحكم في الأجهزة.
أمر cosign attest --type slsaprovenance في سير العمل أعلاه يُنتج سجل مصدر بمستوى SLSA 2. للوصول إلى المستوى 3، تحتاج إضافةً إلى تشغيل البناء في بيئة معزولة مؤقتة بدون وصول شبكي أثناء التجميع، وأن يكون البناء نفسه مُصدَّقاً. GitHub Actions مع actions/attest-build-provenance يمكنه توليد إثبات مصدر بمستوى 3 تلقائياً.

أنماط الفشل في الإنتاج

الفرق التي تعتمد توقيع الصور على نطاق واسع تصطدم بانتظام بهذه الأنماط من الفشل:

  • التوقيع بعد وسم digest مختلف — خطوة البناء تُخرج digest، لكن إذا أُعيد وسم الصورة أو دفعها قبل التوقيع، يتغير الـ digest ويغطي التوقيع قطعة أثرية خاطئة. احتفظ دائماً بالـ digest من خرج خطوة البناء ووقّع تلك المرجعية الصريحة.
  • حدود معدل Rekor — مثيل Rekor العام (rekor.sigstore.dev) له حدود معدل. المؤسسات الكبيرة التي تُشغِّل مئات عمليات CI في الساعة يجب أن تنشر مثيل Rekor خاصاً أو تستخدم خدمة SaaS مدفوعة (Chainguard Enforce، GitHub Artifact Attestations).
  • انحراف الساعة يُبطل الشهادات — Fulcio تُصدر شهادات صالحة لمدة 10 دقائق. إذا كانت ساعة المشغّل منحرفة بأكثر من دقائق قليلة، ستُعتبر الشهادة غير صالحة عند التحقق. تأكد من مزامنة NTP على جميع المشغّلات.
  • الصور متعددة المعماريات وقوائم manifest — وقّع على digest قائمة manifest (الفهرس متعدد المعماريات) وليس على الـ digests الخاصة بالمنصة. cosign sign --yes IMAGE:TAG يحل قائمة manifest تلقائياً؛ تحقق بالـ digest ذاته.

ملخص

يُوفر توقيع الصور بـ cosign وSigstore طريقة بلا مفاتيح طويلة الأمد وقابلة للمراجعة وخفيفة تشغيلياً لإرساء سلسلة حراسة مشفرة من التزام المصدر إلى الحاوية المُشغَّلة. وقّع بالـ digest في CI بعد كل بناء، أرفق تصديق مصدر SLSA، وفرض التحقق على طبقة قبول Kubernetes بـ Kyverno أو Sigstore Policy Controller. هذه الممارسات، مقترنة بفحص الصور من الدرس السابق، تمنحك دفاعاً متعدد الطبقات ضد هجمات سلسلة التوريد — أخطر ناقل تهديد في عمليات الحاويات الحديثة.