دليل عملي

أسرار Docker في الإنتاج: حماية بيانات الاعتماد على VPS

الأمان والمراقبة7 دقائق للقراءةعدد الخطوات: 5

ملف ‎docker-compose.yml‎ يحتوي على مفاتيح API بنص صريح هو تسريب ينتظر أول ماسح ضوئي. تشرح هذه المقالة كيفية القضاء على هذا الخطر على خادم VPS بصلاحيات root: Docker Secrets الأصلي (بدون Swarm منذ الإصدار Compose v2.24)، و‎.env.vault‎، وSOPS — كل طريقة مع أوامرها الدقيقة وقيودها الحقيقية.

المحتويات· الخطر الحقيقي: أسرارك تنتقل داخل صورك1/9
  1. 01الخطر الحقيقي: أسرارك تنتقل داخل صورك
  2. 025 أخطاء في الضبط تكشف أسرارك
  3. 03المتطلبات الأساسية
  4. 04الطريقة الأولى — Docker Secrets في Compose بدون Swarm (خطوة بخطوة)
  5. 05‏Docker Secrets مقابل ‎.env.vault مقابل SOPS — أي طريقة لأي سياق
  6. 06الضبط التكميلي: التدوير والتشفير أثناء الراحة
  7. 07ما لا يحميه Docker Secrets
  8. 08استكشاف الأخطاء وإصلاحها — أخطاء شائعة
  9. 09أسرارك تحت السيطرة — والخطوات التالية

الخطر الحقيقي: أسرارك تنتقل داخل صورك

في عام 2024، رصد GitGuardian أكثر من 12,8 مليون سر مكشوف في مستودعات GitHub العامة — بزيادة 28% مقارنة بالعام السابق وفق تقرير State of Secrets Sprawl 2025. وتُعدّ صور Docker Hub من أكثر المتجهات التي يُقلَّل من شأنها.

عندما تكتب ‎ARG API_KEY‎ في ‎Dockerfile‎ أو تمرر ‎-e DB_PASSWORD=hunter2‎ عند بدء الحاوية، لا يبقى هذا السر محصوراً في وقت التشغيل. بل قد ينتهي به المطاف:

- في طبقات الصورة (قابل للفحص بـ‎docker history --no-trunc‎)؛
- في بيانات وصف الصورة المُصدَّرة على Docker Hub (‎docker inspect‎)؛
- في ملف ‎‎.envالمُضاف عن طريق الخطأ خلال ‎git add .‎ المتسرّع.

تجوب الماسحات الآلية (‎Trivy‎، ‎Grype‎، ‎GitGuardian‎) Docker Hub باستمرار. مستودع عام يحوي سراً بنص صريح يُفهرَس في غضون دقائق. نافذة الكشف تكاد تكون معدومة.

5 أخطاء في الضبط تكشف أسرارك

  • متغيرات البيئة بنص صريح في ‎docker-compose.yml — ‎environment: DB_PASSWORD: hunter2‎ يقرأه كل من يصل إلى الملف أو الصورة.
  • ملف ‎‎.env‎ مُودَع في المستودع — خطأ واحد في ‎.gitignore‎ يُدخل السر في تاريخ git للأبد (حتى بعد ‎git rm‎، لا يزال متاحاً عبر ‎git log‎).
  • ARG‎ مُمرَّر عند البناء ثم مُدمَج في الصورة — تُخزَّن قيم ‎ARG‎ في بيانات طبقة الصورة ويمكن قراءتها بـ‎docker history‎.
  • الأسرار في السجلات — تطبيق يسجّل متغيرات بيئته عند الإقلاع (Spring Boot وRails في وضع debug وبعض خوادم Node) يطبع بيانات الاعتماد في ‎docker logs‎.
  • وحدات تخزين bind-mount على ‎/root‎ أو دليل المشروع — ملف ‎‎.env‎ مُثبَّت من المضيف يظل في متناول أي عملية داخل الحاوية بصلاحيات root.

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

لمتابعة هذه المقالة، تحتاج إلى:

- خادم VPS Linux (‎Debian 12‎ أو ‎Ubuntu 22.04+‎) بصلاحيات root.
- ‏Docker Engine ≥ 24 وDocker Compose ≥ 2.24 (تحقق بـ‎docker compose version‎).
- لـSOPS: تثبيت ‎age‎ (حزمة ‎age‎ على ‎Debian/Ubuntu‎، أو ثنائي من ‏github.com/FiloSottile/age).
- لـ‎.env.vault‎: ‏Node.js ≥ 18 وواجهة سطر الأوامر ‎dotenvx‎ (‎npm install -g @dotenvx/dotenvx‎).

لا يُشترط وجود مجموعة Swarm لأيٍّ من الطرق المقدَّمة هنا.

الطريقة الأولى — Docker Secrets في Compose بدون Swarm (خطوة بخطوة)

  1. التحقق من إصدار Compose

    يعمل Docker Secrets بدون Swarm منذ Docker Compose v2.24.0 (إصدار 11 يناير 2024). تحقق:

    docker compose version
    # Docker Compose version v2.27.1

    إن كنت أدنى من ‎v2.24‎، حدِّث Compose قبل المتابعة (‎apt-get install docker-compose-plugin‎ على ‎Debian/Ubuntu‎).

  2. إنشاء ملفات الأسرار

    أسرار Docker Compose في وضع غير Swarm هي ملفات على المضيف، مُثبَّتة كـ‎tmpfs‎ داخل الحاوية. أنشئها خارج دليل المشروع:

    mkdir -p /etc/myapp/secrets
    echo -n 'strong-db-password' > /etc/myapp/secrets/db_password
    echo -n 'stripe-api-key-xxxxx' > /etc/myapp/secrets/stripe_key
    chmod 600 /etc/myapp/secrets/*
    chown root:root /etc/myapp/secrets/*

    الخيار ‎-n‎ في ‎echo‎ يمنع إضافة سطر جديد في النهاية — بعض التطبيقات تقرأ الملف بأكمله بما في ذلك السطر الجديد، مما يُبطل المفتاح.

  3. الإعلان عن الأسرار في docker-compose.yml

    services:
      app:
        image: myapp:latest
        secrets:
          - db_password
          - stripe_key
        environment:
          # نُشير إلى المسار، لا إلى القيمة
          DB_PASSWORD_FILE: /run/secrets/db_password
          STRIPE_KEY_FILE: /run/secrets/stripe_key
    
    secrets:
      db_password:
        file: /etc/myapp/secrets/db_password
      stripe_key:
        file: /etc/myapp/secrets/stripe_key

    لاحظ استخدام اصطلاح ‎*_FILE‎: يجب أن يقرأ تطبيقك المتغير ‎DB_PASSWORD_FILE‎، ويفتح الملف المُشار إليه ويقرأ محتواه. الصور الرسمية لـ‎PostgreSQL‎ و‎MySQL‎ و‎Redis‎ ومعظم صور Bitnami تدعم هذا الاصطلاح بشكل أصلي — راجع توثيق صورتك.

  4. التحقق من التثبيت داخل الحاوية

    بعد ‎docker compose up -d‎، افحص التثبيت:

    docker compose exec app ls -la /run/secrets/
    # -r-------- 1 root root 20 Sep 20 08:12 db_password
    # -r-------- 1 root root 28 Sep 20 08:12 stripe_key
    
    docker inspect myapp_app_1 | grep -A5 Mounts
    # "Type": "tmpfs",
    # "Destination": "/run/secrets",

    nوع التثبيت هو ‎tmpfs‎: المحتوى يعيش في الذاكرة العشوائية، ولا يُكتب على قرص الحاوية. يختفي عند إيقاف الحاوية.

  5. ما لا يفعله Docker Secrets — افهم ذلك قبل المتابعة

    Docker Secrets ليس خزنة محكمة الإغلاق. ما لا يحمي منه:

    - الملف المصدر (‎/etc/myapp/secrets/db_password‎) يبقى على قرص المضيف بنص صريح — root على الخادم لديه دائماً وصول إليه.
    - أي عملية داخل الحاوية (‎PID 1‎ أو عملية فرعية أطلقها التطبيق) يمكنها قراءة ‎/run/secrets/*‎.
    - متغير بيئة مشتق من السر (‎DB_PASSWORD=$(cat /run/secrets/db_password)‎ في نقطة دخول) يُعيد السر إلى البيئة، مرئياً عبر ‎docker inspect‎.

    يحمي Docker Secrets من التسرب في طبقات الصورة وفي ‎docker-compose.yml‎. لا يحمي من عملية مخترقة داخل الحاوية.

‏Docker Secrets مقابل ‎.env.vault مقابل SOPS — أي طريقة لأي سياق

مرّر الجدول أفقيًا

المعيار‏Docker Secrets (Compose)‏‎.env.vault (dotenvx)‎‎SOPS + age‎
‏Swarm مطلوبلا (منذ Compose v2.24)لالا
السر مُخزَّن بنص صريح على المضيفنعم (الملف المصدر)لا (مشفَّر في المستودع)لا (مشفَّر في المستودع)
‏KMS عن بُعد مطلوبلالا (مفتاح محلي متماثل ممكن)لا (‎age‎ يعمل بلا اتصال)
التدوير بلا إعادة نشرلا (إعادة تشغيل ضرورية)لا (إعادة بناء ‎.env‎)لا (إعادة بناء ‎.env‎)
‏CI/CD: الحقن في خط الأنابيبمعقد (ملفات للتوفير)بسيط (متغير ‎`DOTENV_PRIVATE_KEY`‎)متوسط (مفتاح ‎age‎ كسر CI)
منحنى التعلممنخفض (أصلي في Compose)منخفض (واجهة ‎dotenvx‎)متوسط (صياغة ‎YAML‎ + مفاتيح ‎age/GPG‎)

الضبط التكميلي: التدوير والتشفير أثناء الراحة

تدوير أسرار Docker Compose. لا يدعم Docker Compose بدون Swarm التدوير الساخن (خلافاً لـSwarm الذي يمكنه تحديث السر دون إيقاف الخدمة). لتغيير سر:

# 1. كتابة القيمة الجديدة
echo -n 'new-password' > /etc/myapp/secrets/db_password
# 2. إعادة تشغيل الخدمة المعنية
docker compose restart app

تشفير الملفات المصدر بـ‎age‎. إن أردت تشفير الملفات على المضيف (لمواجهة سرقة لقطة أو نسخة احتياطية مخترقة)، يتيح لك ‎SOPS + age‎ تخزين ملفات مشفَّرة وفكّ تشفيرها عند الإقلاع:

# توليد مفتاح age
age-keygen -o /root/.config/sops/age/keys.txt
# تشفير ملف السر
sops --encrypt --age $(age-keygen -y /root/.config/sops/age/keys.txt) \
  /etc/myapp/secrets/db_password > /etc/myapp/secrets/db_password.enc
# في سكريبت الإقلاع، فكّ التشفير قبل docker compose up
sops --decrypt /etc/myapp/secrets/db_password.enc > /etc/myapp/secrets/db_password

مراجعة الوصول. فعِّل سجلات Docker مع ‎journald‎ (‎--log-driver=journald‎ في ‎/etc/docker/daemon.json‎) للاحتفاظ بسجل من أطلق أي حاويات ومتى.

ما لا يحميه Docker Secrets

يُثبِّت Docker Secrets السر كـ‎tmpfs‎ في ‎/run/secrets/: إنه شبكة أمان ضد التسريب في الصور وملفات Compose، لا ضد عملية مخترقة داخل الحاوية. أي عملية تعمل داخل الحاوية — بما فيها shell حُصل عليه عبر RCE — يمكنها قراءة ‎/run/secrets/*‎. وroot على المضيف يصل دائماً إلى الملف المصدر.

إن كان نموذج التهديد لديك يشمل حاوية مخترقة، فالجواب الصحيح هو مدير أسرار خارجي (‎HashiCorp Vault‎ أو ‎AWS Secrets Manager‎ أو ‎Infisical‎ المستضاف ذاتياً) يُسلِّم الأسرار عبر ‎API‎ مع مصادقة، دون كتابتها على قرص الحاوية أبداً.

استكشاف الأخطاء وإصلاحها — أخطاء شائعة

unknown shorthand flag: 's' in -s أثناء ‎docker compose up
أنت تستخدم الأمر القديم ‎docker-compose‎ (‎v1‎، ‎Python‎). انتقل إلى ‎docker compose‎ (‎v2‎، إضافة Go) بـ‎apt-get install docker-compose-plugin‎.

secrets are only supported when deploying to a swarm
إصدار Docker Compose لديك أقل من ‎v2.24‎. تحقق بـ‎docker compose version‎ وحدِّث.

الحاوية تبدأ لكن ‎/run/secrets/db_password‎ فارغ
تحقق من وجود الملف المصدر وعدم فراغه: ‎cat /etc/myapp/secrets/db_password | wc -c‎. ملف فارغ يُنشئ تثبيت ‎tmpfs‎ فارغاً دون رسالة خطأ.

permission denied‎ عند قراءة ‎/run/secrets/
تُثبَّت الملفات بأذونات الملف المصدر. إن كانت عمليتك تعمل بمستخدم غير root داخل الحاوية، اضبط أذونات الملف على المضيف: ‎chmod 640 /etc/myapp/secrets/db_password‎ وتحقق من GID العملية.

docker inspect‎ لا يزال يُظهر متغير البيئة بنص صريح
لقد أعلنت السر لكنك مررت القيمة أيضاً في ‎environment:‎. احذف الإدخال بالقيمة المباشرة واستخدم اصطلاح ‎*_FILE‎ فقط في ‎environment:‎.

أسرارك تحت السيطرة — والخطوات التالية

يُزيل Docker Secrets في وضع Compose غير Swarm السبب الرئيسي للتسريب: بيانات الاعتماد بنص صريح في ملفات الإعداد وطبقات الصورة. تنحصر الطريقة في خمس خطوات، وتعمل بلا بنية تحتية خارجية، وتندمج في أي سير عمل قائم.

للمزيد:

- قائمة التحقق للإنتاج: ‏10 نقاط لا غنى عنها لملف ‎docker-compose.yml‎ جاهز للإنتاج.
- البداية مع Docker على VPS: الشروع في استخدام Docker على VPS إن كنت تبني بيئتك الأولى.
- تصليب نظام التشغيل: قائمة تحقق تصليب Linux لتأمين طبقة المضيف التي يعمل عليها Docker.

تحكم في مكدسك البرمجي، تحكم في أسرارك

خادم VPS بصلاحيات root وصول كامل إلى Docker: أنت من يقرر إدارة الأسرار، وسطح الهجوم، وكل طبقة في بنيتك التحتية.

بحاجة إلى مساعدة؟

تصفّح مركز المساعدة والأسئلة الشائعة، أو تواصل مع فريقنا — معاودة اتصال أو WhatsApp أو بريد إلكتروني. الدعم بـالعربية والفرنسية والإنجليزية.

راسلنا على WhatsAppيُفتح في علامة تبويب جديدة