SBOM وCosign بالعربي: اعرف إيه دخل الإنتاج قبل النشر
مستوى القارئ: متوسط
بعد المقال ده هتعرف تعمل gate بسيط في الـ CI يمنع صورة container مجهولة المصدر من الوصول للإنتاج. الفكرة مش إنك تزود خطوة شكلية، الفكرة إنك تعرف المكونات، التوقيع، ومين بنى الصورة قبل ما تعمل deploy.
المشكلة باختصار
الطريقة الشائعة إن الفريق يبني Docker image، يرفعها على registry، وبعدها Kubernetes يسحبها. الطريقة دي بتفشل لما يحصل incident بسبب package قديمة أو image اتبنت من branch غلط. وقتها السؤال بيبقى: الصورة دي فيها إيه بالظبط؟ ومين بناها؟ وهل حد غيّرها بعد الـ CI؟
افترض إن عندك API عليه 50K زائر يوميًا، وبتطلع 8 releases في الأسبوع. لو vulnerability ظهرت في مكتبة زي OpenSSL أو lodash، البحث اليدوي داخل كل صورة ممكن ياخد 2 إلى 4 ساعات. وجود SBOM يقلل السؤال من “ندور فين؟” إلى “افحص الملف ده وشوف التأثير”. الرقم هنا تقديري، لكنه قريب من فرق صغيرة عندها 10 إلى 20 service.
يعني إيه SBOM وSigning بمثال واضح
ركز في المثال ده. اعتبر الـ container زي شحنة داخلة مخزن. الـ SBOM هو كشف المحتويات: اسم كل package، نسختها، ونوعها. التوقيع بـ Cosign هو ختم الاستلام: مين بنى الشحنة، وإمتى، وعلى أي digest. من غير كشف المحتويات أنت شايف الصندوق فقط. ومن غير الختم أنت مش واثق إن الصندوق هو نفسه اللي خرج من المصنع.
علميًا، SBOM اختصار Software Bill of Materials. أدوات مثل Trivy تستطيع توليد SBOM بصيغتين شائعتين: SPDX وCycloneDX، وتقدر تفحص SBOM لاحقًا لاكتشاف ثغرات أو مشاكل license حسب توثيق Trivy الرسمي. Cosign من Sigstore يضيف توقيعًا أو attestation مرتبطًا بصورة الحاوية، ويمكن استخدام OIDC من GitHub بدل مفاتيح ثابتة مخزنة في secrets.
Workflow عملي على GitHub Actions
الهدف هنا بسيط: ابنِ الصورة، طلّع SBOM، ارفع الصورة، وقّعها، ثم احفظ attestation. المثال مبني على GitHub Actions وGHCR وTrivy وCosign. الافتراض إن عندك repository على GitHub وصورة Dockerfile في الجذر.
name: build-secure-image
on:
push:
branches: ["main"]
permissions:
contents: read
packages: write
id-token: write
attestations: write
artifact-metadata: write
env:
IMAGE: ghcr.io/${{ github.repository }}/api
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Build image
run: |
docker build -t "$IMAGE:${{ github.sha }}" .
docker push "$IMAGE:${{ github.sha }}"
- name: Generate SBOM with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.IMAGE }}:${{ github.sha }}
format: spdx-json
output: sbom.spdx.json
- name: Attest SBOM
uses: actions/attest@v4
with:
subject-path: sbom.spdx.json
sbom-path: sbom.spdx.json
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Sign image with keyless Cosign
run: cosign sign --yes "$IMAGE:${{ github.sha }}"