EduFlow

الوحدة 2 · Images جاهزة للإنتاج

فحص الـ Images وإبعاد الأسرار عنها

20 دقراءة 4 داكتشف الخطأ

الدرس في لمحة

افحص images ‏notes-api بحثًا عن الثغرات المعروفة باستخدام Docker Scout، وصنّف ما تجده حسب الأولوية، واستخدم secret mounts في BuildKit حتى لا تصل الـ tokens المطلوبة وقت البناء إلى أي layer في الـ image.

  1. 1افحص قبل أن تشحن
  2. 2صنّف بترتيب معقول
  3. 3كيف تتسرّب الأسرار إلى الـ images
  4. 4الـ secret mounts في BuildKit
  5. 5أسرار وقت التشغيل ليست من شأن الـ image

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

أسوأ حادث في سجلّي لم يكن انهيارًا. خطوة بناء احتاجت إلى token لـ registry خاص بـ npm، فمرّره أحدهم بوصفه وسيط بناء (build argument). ذهبت الـ image إلى registry متاح للقراءة على نطاق واسع، وبعد أشهر وجد تدقيق روتيني الـ token قابعًا في البيانات الوصفية للـ image، صالحًا طوال تلك المدة. هذا الدرس عمّا بداخل الـ images: الأجزاء التي لم تكتبها، والأجزاء التي لم تقصد تضمينها.

افحص قبل أن تشحن

كل حزمة في الـ image، من libssl في Debian إلى express في npm، لها تاريخ علني من الثغرات (CVEs). الماسح يقرأ جرد الحزم في الـ image ويطابقه مع قواعد البيانات تلك. ‏Docker Scout مدمج في واجهة Docker CLI:

Terminal
docker scout quickview notes-api:1.4.0
docker scout cves --only-severity critical,high notes-api:1.4.0
docker scout recommendations notes-api:1.4.0

الأمر quickview يعطي ملخّصًا في شاشة واحدة: عدد الثغرات في الـ image لديك، وفي الـ base image التي بُنيت عليها، وفي إصدار أحدث من تلك الـ base إن وُجد. والأمر cves يسرد كل نتيجة مع الحزمة، والإصدار المثبّت، والإصدار الذي يصلحها. والأمر recommendations يقترح tags لـ base images فيها ثغرات معروفة أقل. ‏Trivy وGrype ماسحان مفتوحا المصدر شائعان يؤدّيان العمل نفسه؛ اختر واحدًا واستخدمه في كل مكان.

صنّف بترتيب معقول

القائمة الطويلة من النتائج ليست قائمة مهام. اعمل عليها هكذا:

  1. نتائج الـ base image أولًا. إذا جاءت معظم النتائج من حزم نظام التشغيل، فحدّث إلى base مرقّعة حديثًا أو انتقل إلى base أصغر. الانتقال إلى distroless في الدرس 2.2 يزيل معظمها بخطوة واحدة.
  2. اعتماديات التطبيق القابلة للإصلاح بعد ذلك. إذا وُجد إصدار مُصلَح، فارفع رقم إصداره في package.json، وشغّل npm ci، وأعِد البناء.
  3. النتائج غير القابلة للإصلاح تحتاج إلى قرار، لا إلى صمت. اكتب لماذا لا يمكن الوصول إلى مسار الكود ذاك، أو ما الذي يخفّف الخطر، وحدّد موعدًا للنظر فيها مجددًا.

ثم اجعل الفحص بوّابة حتى لا تتسلّل الانتكاسات (regressions):

Terminal
docker scout cves --only-severity critical,high --exit-code notes-api:1.4.0

مع --exit-code يفشل الأمر حين يجد ثغرات بتلك الدرجات من الخطورة، فتفشل مهمّة الـ CI قبل الدفع. وما دمت في الـ CI، فاطلب من أداة البناء بيانات سلسلة التوريد أيضًا: ‏docker buildx build --sbom=true --provenance=mode=max … يرفق بالـ image قائمة مكوّنات البرمجيات (SBOM) وبيانات مصدر البناء (provenance)، فيصبح التدقيق التالي بحثًا سريعًا بدلًا من تحقيق.

كيف تتسرّب الأسرار إلى الـ images

الـ images تتذكّر أكثر مما تظن. ثلاثة تسريبات شائعة:

  • ENV API_KEY=… تُخزَّن في إعدادات الـ image وتُضبط في كل container. والأمر docker image inspect يُظهرها.
  • ARG NPM_TOKEN المستخدمة في خطوة RUN تُسجَّل في سجلّ بناء الـ image. والأمر docker history --no-trunc قد يكشفها.
  • ملف كُتب ثم حُذف لاحقًا (COPY .npmrc ثم RUN rm .npmrc) ما زال موجودًا في الـ layer السابقة. أي شخص لديه الـ image يستطيع استخراج تلك الـ layer.

تحذّر build checks في Docker من الأوّلين (SecretsUsedInArgOrEnv) حين يوحي اسم المتغيّر بأنه سرّ. خذ التحذير بجدية.

الـ secret mounts في BuildKit

الأداة الصحيحة هي الـ secret mount: يُتاح السرّ لخطوة RUN واحدة على هيئة ملف (أو متغيّر بيئة) ولا يُكتب أبدًا في layer. بناء notes-api يحتاج إلى token لحزمة خاصة، لذا:

Dockerfile
FROM node:24-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    --mount=type=cache,target=/root/.npm \
    npm ci
Terminal
docker build --secret id=npmrc,src=$HOME/.npmrc -t notes-api:1.4.0 .

خلال خطوة RUN تلك، يجد npm إعداداته في /root/.npmrc. وقبلها وبعدها لا يوجد الملف، ولا يُخزَّن عنه شيء في الـ image أو في الـ build cache. ودون target، تُركَّب الأسرار في /run/secrets/<id>. وإذا كانت أداة ما تقرأ الـ token من متغيّر بيئة، فركّبه متغيّرَ بيئة بـ RUN --mount=type=secret,id=npm_token,env=NPM_TOKEN npm ci ومرّر --secret id=npm_token,env=NPM_TOKEN في سطر الأوامر، فتُقرأ القيمة من المتغيّر NPM_TOKEN في الـ shell لديك.

أسرار وقت التشغيل ليست من شأن الـ image

كلمة مرور قاعدة البيانات التي يستخدمها notes-api في الإنتاج لا مكان لها في الـ image إطلاقًا. إنها تصل حين يبدأ الـ container: من متغيّر بيئة في Compose، أو من Kubernetes Secret، أو من مدير الأسرار في منصّتك السحابية. ستربط الأوّلين في الأقسام القادمة. تبقى الـ image متطابقة عبر البيئات؛ ولا يتغيّر إلا الإعداد المحقون.

انتهى القسم الثاني: الـ image لديك نحيفة، وبلا صلاحيات، وقابلة للتتبّع، ونظيفة. في القسم التالي ستعطيها قاعدة بيانات تتحدّث معها، باستخدام Docker Compose.

أهم النقاط

  • الأمر docker scout cves يسرد ثغرات CVE المعروفة لكل حزمة؛ ومع --only-severity critical,high --exit-code يتحوّل إلى بوّابة في الـ CI.
  • أصلح نتائج الـ base image أولًا بتحديثها أو تغييرها؛ والأمر docker scout recommendations يقترح بدائل.
  • كل ما يُمرَّر بـ ARG أو ENV، أو يُكتب في ملف داخل خطوة RUN، يمكن استرجاعه من الـ image، حتى لو حذفته خطوة لاحقة.
  • التعليمة RUN --mount=type=secret تتيح السرّ لخطوة بناء واحدة دون كتابته في أي layer.
  • أسرار وقت التشغيل مثل كلمات مرور قاعدة البيانات تُحقن عند بدء الـ container، ولا تُبنى داخل الـ image أبدًا.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

فحص notes-api:1.4.0 يُبلغ عن 30 ثغرة CVE، منها 27 في حزم Debian القادمة من الـ base image. ما الخطوة الأولى الأكثر فاعلية؟

السؤال 2 من 4

خطوة البناء هذه تحتاج إلى token لـ npm. ما الخطأ فيها؟

Dockerfile
ARG NPM_TOKEN
RUN echo "//npm.pkg.github.com/:_authToken=${NPM_TOKEN}" > .npmrc \
 && npm ci && rm .npmrc
السؤال 3 من 4

ماذا يعطي هذا السطر لخطوة npm ci؟ RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

السؤال 4 من 4

من أين يجب أن تأتي كلمة مرور قاعدة بيانات الإنتاج لـ notes-api؟

تدريب

اكتشف الخطأ

أين يتسرّب الـ token

هذه الـ image لأداة داخلية تثبّت حزمًا من registry خاص. يعتقد كاتبها أن حذف .npmrc في السطر 8 يُبقي الـ token آمنًا. اختر السطرين اللذين يضعان الـ token داخل الـ image حيث يستطيع أي شخص يسحبها استرجاعه.

حدّد السطر أو الأسطر التي تظنها خاطئة، ثم تحقّق.