في 20 أكتوبر 2026 أصدرت Docker النسخة Docker Engine 29.9.2، وهي تحديث يبدو عاديًا: مجرد ترقية لبيئة تشغيل Go. لكنه ليس عاديًا. فبيئة التشغيل الجديدة تجعل الـ daemon يرفض أي سلسلة شهادات TLS تمر عبر إحدى ثماني جهات إصدار وسيطة (CA) تابعة لـ eMudhra تسرّبت مفاتيحها الخاصة. إذا كان الـ registry الخاص بك أو الـ mirror أو خادم الملفات قد حصل على شهادته من إحدى هذه الجهات الوسيطة، فسيتوقف docker pull وdocker push عنه لحظة الترقية.
ورسالة الخطأ تزيد الأمر سوءًا لأنها تشير إلى سبب خاطئ. في ما يلي ما الذي تغيّر، وكيف تفحص خوادمك والـ registries خلال دقائق، وما الذي يجب تغييره في إعدادات الـ daemon وملفات Dockerfile وخطوط CI.
ما الذي حدث، بالترتيب
بحسب الإفصاح العلني (وهي الصفحة التي تربط إليها ملاحظات الإصدار الخاصة بـ Docker نفسها)، أثبت باحث أنه يملك المفاتيح الخاصة لثماني شهادات وسيطة من eMudhra بتوقيع رسالة بكل واحد منها، وأبلغ برامج الجذر لدى المتصفحات وأنظمة التشغيل في 17 أكتوبر. وتقول الصفحة نفسها إن المفاتيح "وُلّدت بشكل خاطئ من قِبل جهة الإصدار"، أي أن أي شخص كان يستطيع استخراجها من الشهادات نفسها. حظرت Chrome وFirefox هذه الشهادات الوسيطة منذ 17 أكتوبر، وMicrosoft منذ 20 أكتوبر، وApple منذ 22 أكتوبر.
عولجت المتصفحات عبر قنوات بيانات الإبطال (revocation). لكن كثيرًا من العملاء الآخرين لا يقرؤون بيانات الإبطال إطلاقًا، والإفصاح صريح في ذلك: هؤلاء العملاء "يحتاجون إلى تحديثات برمجية حتى يتوقفوا عن الوثوق بالشهادات الوسيطة المخترقة". وبما أن CLI وdaemon وBuildx وCompose في Docker كلها برامج مكتوبة بـ Go، فقد جاء إصلاحها من Go.
- صدر Go 1.26.10 في 19 أكتوبر مع إصلاح أمني في crypto/x509. وتقول تذكرة Go الخاصة بـ CVE-2026-111943: "ستقوم Verify الآن بتصفية السلاسل التي تحتوي على هذه الشهادات الوسيطة". ونُقل الإصلاح نفسه إلى فرع Go 1.27 في Go 1.27.3.
- نُشر Docker Engine 29.9.2 وBuildx 0.38.1 في 20 أكتوبر، وكلاهما مبني بـ Go 1.26.10.
- تبعهما Docker Compose 5.6.1 في 21 أكتوبر بتحديث Go نفسه.
- وصل الإصلاح إلى Docker Desktop على دفعات: 4.96.1 في 21 أكتوبر (Engine 29.9.2 وBuildx 0.38.1)، ثم 4.96.2 و4.96.3 في 22 أكتوبر لبقية المكوّنات المرفقة.
الشهادات الوسيطة الثماني المحظورة، كما وردت في ملاحظات إصدار Engine، هي: EM DV TLS CA - G3B وEM DV TLS CA - G3C وEM OV TLS CA - G3B وEM OV TLS CA - G3C وEM EV TLS CA - G3B وEM EV TLS CA - G3C، إضافة إلى جهتين لتوقيع الشيفرة هما EM OV CS CA - G3B وEM OV CS CA - G3C.
ما الذي يتعطل، وكيف تبدو رسالة الخطأ
تلخّص ملاحظات إصدار 29.9.2 الأمر في سطر واحد: "تفشل عمليات سحب الصور ودفعها في التحقق من شهادة TLS إذا كانت سلسلة شهادات الـ registry تعتمد على إحدى شهادات TLS الوسيطة المتأثرة". ولا يوجد خيار لتعطيل هذه التصفية.
يشغّل موقع الإفصاح خوادم اختبار تقدّم شهادات من كل جهة وسيطة محظورة، فوجّهنا خادم Docker 29.9.2 لدينا إلى أحدها في 2 نوفمبر:
$ docker pull dv-g3c-emudhra.we-have-a-problem.com/test:latest
Error response from daemon: Get "https://dv-g3c-emudhra.we-have-a-problem.com/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority
اقرأ هذا الخطأ بعناية. عبارة "unknown authority" هي الرسالة نفسها التي تظهر عندما يستخدم الـ registry شهادة موقّعة ذاتيًا، لذلك فإن أول ما يفعله معظم المناوبين هو وسم الـ registry بأنه insecure أو إضافة ملف CA. لا هذا ولا ذاك هو الحل الصحيح هنا، والأول يجعل الوضع أسوأ (نشرح ذلك أدناه).
الـ registries العامة الكبرى غير متأثرة. في 2 نوفمبر فحصنا السلاسل التي يقدّمها Docker Hub (registry-1.docker.io) وquay.io وpublic.ecr.aws (جميعها تحت جذور Amazon)، وghcr.io وregistry.gitlab.com (Sectigo)، وgcr.io (Google Trust Services). لا يمر أي منها عبر eMudhra. الخطر يقع على Harbor أو Nexus أو Artifactory أو registry:2 المستضافة ذاتيًا، وعلى الـ mirrors من نوع pull-through، والخوادم الداخلية التي تنزّل منها خطوط CI.
الخطوة 1: اعرف الإصدارات التي تشغّلها
هذه الأوامر الثلاثة للقراءة فقط وآمنة على خادم إنتاج:
docker version --format '{{.Client.Version}} / {{.Server.Version}}'
docker buildx version
docker compose version --short
على خادمنا تطبع 29.9.2 / 29.9.2 وv0.38.1 و5.6.1. أي إصدار أقدم من Engine 29.9.2 أو Buildx 0.38.1 أو Compose 5.6.1 من حزم Docker الرسمية مبني ببيئة Go أقدم وما زال يقبل السلاسل المخترقة. وهذه هي المشكلة الأمنية التي جاء التحديث لإصلاحها، لذا فالحل هو الترقية ثم معالجة ما يتعطل، لا البقاء على الإصدار القديم.
الخطوة 2: افحص كل registry تسحب منه
اعرض أسماء خوادم الـ registry التي تشير إليها حزمك. يلتقط هذا الأمر ملفات compose على القرص، بما فيها الحزم المتوقفة حاليًا:
grep -rhoE 'image: *"?[a-z0-9.-]+\.[a-z]{2,}(:[0-9]+)?/' --include='*compose*.y*ml' /srv /opt 2>/dev/null \
| sed -E 's/image: *"?//; s#/$##' | sort -u
ثم اسأل كل واحد منها عن السلسلة التي يقدّمها وابحث عن أسماء شهادات eMudhra الوسيطة:
for reg in registry.example.com mirror.example.com:5000; do
host=${reg%%:*}; port=${reg##*:}; [ "$port" = "$reg" ] && port=443
if echo | openssl s_client -connect "$host:$port" -servername "$host" -showcerts 2>/dev/null \
| grep -qE 'CN ?= ?EM (DV|OV|EV) TLS CA - G3[BC]'; then
echo "AFFECTED: $reg"
else
echo "ok: $reg"
fi
done
شغّلنا هذه الحلقة على ghcr.io وعلى خادم اختبار G3C: أعاد الأول ok والثاني AFFECTED. أضف أيضًا الخوادم التي تنزّل منها خطوط CI، فعملية build تنزّل ملفًا تنفيذيًا من خادم داخلي هي مجرد عميل TLS آخر.
الخطوة 3: أصلح الـ registry لا العميل
ملاحظات Docker واضحة: "تحتاج الـ registries المتأثرة إلى سلسلة شهادات بديلة لا تستخدم هذه الشهادات الوسيطة". عمليًا يعني ذلك شهادة جديدة من جهة إصدار أخرى (أو سلسلة إصدار مختلفة من المورّد نفسه)، تُثبَّت على ما يُنهي اتصال TLS أمام الـ registry، سواء كان الـ registry نفسه أو nginx أو Traefik أو موازن أحمال.
الاختصار المغري هو هذا، في /etc/docker/daemon.json:
{
"insecure-registries": ["registry.example.com"]
}
لا تعتمده في الإنتاج. فهو يطلب من الـ daemon تخطي فحص الشهادة لذلك الخادم، أي إلغاء الحماية نفسها التي أضافها تحديث Go. وأي شخص يستطيع الوقوف في مسار الشبكة ومعه شهادة من أحد هذه المفاتيح المسرّبة يمكنه حينها أن يقدّم لك أي صورة يريدها باسم الـ registry الخاص بك.
إذا كان الـ registry داخليًا ولا تريد شراء شهادة عامة أخرى، فأصدر شهادة من جهة الإصدار الخاصة بك وسلّم ملف CA إلى Docker بالطريقة التي يشرحها توثيق Docker لشهادات الـ registry:
/etc/docker/certs.d/
registry.example.com/
ca.crt
السلسلة التي تنتهي عند جهة الإصدار الخاصة بك لا تمر أبدًا عبر eMudhra، فلا تجد التصفية ما ترفضه. بعد تفعيل الشهادة الجديدة، أعد تشغيل حلقة الخطوة 2 واسحب صورة واحدة للتأكد.
الخطوة 4: صورك تحتاج الإصلاح نفسه
ترقية Docker تحمي Docker وحده، ولا تفعل شيئًا للبرمجيات داخل حاوياتك. يذكر الإفصاح العملاء الذين ما زالوا معرّضين للخطر: "التطبيقات والمكتبات التي تأتي مع حزمة CA خاصة بها"، إضافة إلى تطبيقات Electron، وينبّه إلى أن كثيرًا من العملاء غير المتصفحات لا يتحققون من الإبطال.
إعادة بناء الصورة مع حزمة ca-certificates حديثة لا تحل المشكلة وحدها. فحزمة CA تسرد الجذور الموثوقة فقط، ولا تملك طريقة لقول "ثق بهذا الجذر باستثناء هذه الشهادات الوسيطة تحته". الرفض يجب أن يأتي من مكتبة TLS، وهذا ما فعلته Go. لذلك فإن إصلاح خدمات Go هو إعادة البناء بأدوات مُرقّعة. في ملف Dockerfile متعدد المراحل:
# before
FROM golang:1.26.9 AS build
# after
FROM golang:1.26.10 AS build
الصورتان golang:1.26.10 وgolang:1.27.3 متاحتان على Docker Hub. وإذا كان ملف go.mod يثبّت إصدار toolchain فارفعه أيضًا، وإلا فسيُنزَّل القديم على أي حال:
# before
toolchain go1.26.9
# after
toolchain go1.26.10
إعادة البناء لا تفيد إلا إذا أُعيد إنشاء الحاويات من الصورة الجديدة. كتبنا عن هذا الفخ في دليل إعادة البناء الخاص بـ PHP2Shell: صورة أساس مُرقّعة لا تفعل شيئًا لحاوية بدأت من الصورة القديمة.
وتستحق خوادم CI نظرة أيضًا. إذا كان سير العمل يثبّت إصدار Buildx عبر docker/setup-buildx-action، فهذا التثبيت هو ما يحدد بيئة Go التي تنفّذ عمليات الدفع:
# before
- uses: docker/setup-buildx-action@v4
with:
version: v0.38.0
# after
- uses: docker/setup-buildx-action@v4
with:
version: v0.38.1
أجهزة المطورين التي تعمل بـ Docker Desktop
نبّهت Docker في ملاحظات 4.96.1 إلى أن "هذا الإصلاح يُطرح على دفعات مع تحديث كل مكوّن من مكوّنات Docker Desktop على حدة". الفريق الذي يعمل على 4.96.0 لا يملك أي جزء منه، والفريق على 4.96.1 يملك جزء Engine دون البقية. ارفع الجميع إلى 4.96.3 على الأقل.
ولمستخدمي Mac ثغرة إضافية. يقول تحديث 28 أكتوبر على صفحة الإفصاح إن أدوات المطورين التي تشحنها Apple مع macOS، ومنها git وcurl وpython3، ما زالت تثق بالشهادات المخترقة، وإن macOS 27.2 ألغى الإصلاحات التي طبّقها المستخدمون يدويًا. ونسخة Docker Desktop المُرقّعة على ذلك الجهاز لا تغيّر ما يثق به git clone.
ما الذي فعلناه على خوادمنا
نشغّل خدمات الإنتاج لدينا في Docker، لذا كان الأمر قائمة تحقق قصيرة لا مشروعًا: تأكدنا من إصدارات Engine وBuildx وCompose المذكورة أعلاه، وشغّلنا فحص السلسلة على كل registry تشير إليه ملفات compose لدينا، وتأكدنا من أن أيًا من شهاداتنا ليست من eMudhra. لم يتأثر شيء، وهذه هي النتيجة التي تريد معرفتها قبل الترقية، لا بعد فشل نشر في الثانية فجرًا. وإذا اعتدت فحص الإصدارات عبر الخوادم، فالطريقة نفسها في دليل ترقية PostgreSQL 14 تصلح هنا، ويوضح مقالنا عن كيف نشغّل نظام إدارة المحتوى الخاص بنا على Docker أين ينتهي اتصال TLS في حزمة مستضافة ذاتيًا نموذجية.
الأسئلة الشائعة
هل يمكنني الرجوع إلى إصدار أقدم من Docker Engine لتعود عمليات السحب إلى العمل؟
يمكنك ذلك، لكن المحرّك القديم سيقبل أيضًا شهادة مزوّرة لأي registry، بما فيه Docker Hub، موقّعة بأحد المفاتيح المسرّبة. استبدل شهادة الـ registry بدلًا من ذلك.
هل Docker Hub متأثر؟
لا. في 2 نوفمبر 2026 كانت سلسلة شهادات registry-1.docker.io تمر عبر Amazon RSA 2048 M01 وAmazon Root CA 1، لا عبر eMudhra. وكذلك الحال مع ghcr.io وquay.io وpublic.ecr.aws وregistry.gitlab.com وgcr.io.
الـ registry لدينا يستخدم شهادة موقّعة ذاتيًا أو من جهة إصدار داخلية. هل علينا فعل شيء؟
ليس بسبب هذا التغيير. التصفية تزيل فقط السلاسل التي تحتوي على شهادات eMudhra الوسيطة الثماني. السلسلة التي تنتهي عند جهة الإصدار الخاصة بك، والمضبوطة في /etc/docker/certs.d، تبقى عاملة.
هل يؤدي تحديث ca-certificates في صورنا إلى حظر الشهادات الوسيطة المخترقة؟
لا. حزمة CA تسرد الجذور فقط، فلا تستطيع استبعاد شهادات وسيطة بعينها تحت جذر يبقى موثوقًا. الحظر يجب أن يأتي من مكتبة TLS أو من فحص الإبطال. لبرامج Go، أعد البناء باستخدام Go 1.26.10 أو 1.27.3.
ما إصدارات Docker التي تتضمن الإصلاح؟
Docker Engine 29.9.2 وBuildx 0.38.1 وCompose 5.6.1، الصادرة في 20 و21 أكتوبر 2026. وفي Docker Desktop وصل الإصلاح عبر 4.96.1 و4.96.2 و4.96.3.
المصادر: ملاحظات إصدار Docker Engine 29، إصدار docker-v29.9.2 في moby/moby، Buildx v0.38.1، Compose v5.6.1، ملاحظات إصدار Docker Desktop، تذكرة Go رقم 82345، سجل إصدارات Go، وإفصاح اختراق جهات الإصدار الوسيطة لدى eMudhra. اختُبرت الأوامر على Docker Engine 29.9.2 وBuildx 0.38.1 وCompose 5.6.1 في 2 نوفمبر 2026.



