الأدلة التقنية

Bastion Host أم Jump Server؟ نقطة الدخول الآمنة إلى سيرفراتك

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

Bastion Host أم Jump Server؟ نقطة الدخول الآمنة إلى سيرفراتك

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

والحل الذي تتفق عليه أغلب أطر الأمن هو أن تجعل للإدارة باباً واحداً فقط، تقويه وتراقبه وتسجل ما يمر منه، وتبقى بقية السيرفرات في شبكة خاصة لا يصلها أحد من الخارج، وهذا الباب له اسمان تسمعهما كثيراً: Bastion Host وJump Server، وأحياناً Jump Host أو Jump Box. وقد ذكرنا هذه الفكرة في عدة مقالات سابقة، مثل مناطق الشبكة في الفرق بين Tiers وLayers وخيارات الباب الإداري في خارطة CTO لشركة SaaS ناشئة، وفي هذا المقال سوف نشرحها كاملة، وسوف نناقش ما يلي:

  • لماذا لا يفتح كل سيرفر منفذ الإدارة للإنترنت، والحلول البسيطة التي يبدأ بها أغلبنا ولماذا لا تكفي.
  • ما هو Bastion Host وما هو Jump Server، وأين يستخدم المصطلحان للشيء نفسه وأين يختلفان.
  • كيف يعمل ProxyJump في OpenSSH، ولماذا هو أفضل من إعادة توجيه ال ssh-agent المعروفة باسم Agent Forwarding.
  • كيف تقوي ال Bastion: المفاتيح وMFA وتقييد الحساب وتقليل الحزم والسجلات والشهادات قصيرة العمر.
  • ما يقابله في Windows، والبدائل المدارة في السحابة، وبوابات الوصول المميز الحديثة.
  • ما الذي تطلبه الضوابط الأساسية للأمن السيبراني NCA ECC في هذا الجانب.
  • تجربة عملية كاملة بثلاثة Containers: Bastion واحد مكشوف وسيرفران داخليان دون أي منفذ منشور.

لماذا لا يفتح كل سيرفر منفذ الإدارة للإنترنت؟

لنفرض أن لديك سيرفر تطبيق app-01 وسيرفر قاعدة بيانات db-01 وسيرفر ويب وجهاز Windows، وأن كل واحد منها له عنوان عام وينشر منفذ الإدارة، فالمشكلة هنا ليست في سيرفر بعينه، وإنما في أن سطح الهجوم Attack Surface يكبر مع كل سيرفر تضيفه، والسبب أن كل سيرفر يحتاج أن تحدث خدمة SSH فيه، وأن تراجع حساباته ومفاتيحه، وأن تراقب سجلاته، وإذا فاتك واحد فقط فهو الذي سوف يستخدمه المخترق. والصورة التالية تبين الفرق بين الحالتين:

مخطط من لوحتين: على اليمين أربعة سيرفرات web-01 وapp-01 وdb-01 وwin-01 ينشر كل منها منفذ SSH أو RDP للإنترنت وأجهزة مسح ترسل محاولات إلى كل منفذ، وعلى اليسار السيرفرات نفسها في شبكة خاصة 10.70.0.0/24 دون منافذ عامة، وأمامها سيرفر Bastion واحد بدرع يقبل SSH بالمفاتيح فقط ويمرر الاتصال إلى السيرفرات
أربعة أبواب تحتاج إلى تحديث ومراقبة، أم باب واحد مقوى والسيرفرات خلفه

وقبل أن نصل إلى ال Bastion، لنمر على الحلول التي يبدأ بها أغلبنا، لأن فهم سبب قصورها هو الذي يوضح لماذا نحتاج إلى باب واحد:

  1. تغيير المنفذ من 22 إلى رقم آخر: يقلل الضجيج في السجلات، ولكن أدوات المسح تفحص كل المنافذ، فالخدمة ما زالت مكشوفة وما زالت تحتاج إلى التحديث والمراقبة في كل سيرفر.
  2. fail2ban على كل سيرفر: يحظر العناوين التي تكرر المحاولات الفاشلة كما شرحنا في تأمين سيرفر VPS من أول دخول، وهو مفيد، ولكنه لا يحمي من ثغرة في خدمة SSH نفسها، ولا من مفتاح مسروق.
  3. قائمة العناوين المسموحة Allowlist: تسمح للمنفذ من عنوان المكتب فقط، وهذا جيد حتى يعمل أحدكم من البيت أو من السفر، فتضيف عنوانه ثم تنسى أن تحذفه، وبعد سنة تجد القائمة طويلة لا يعرف أحد أصحابها، وعليك أن تحدثها في كل سيرفر.
  4. VPN أمام كل شيء: حل ممتاز للوصول إلى الشبكة كما شرحنا في ما هو VPN؟، ولكنه يعطي من دخل النفق طريقاً إلى الشبكة كلها إلا إذا قسمتها، ولا يسجل من دخل إلى أي سيرفر وبأي حساب. لذلك يستخدم VPN كثيراً مع ال Bastion لا بدلاً منه، فيكون ال Bastion نفسه غير مكشوف إلا داخل النفق.

والقاسم المشترك في هذه الحلول أنها تحمي كل باب على حدة، أما الفكرة التي نشرحها الآن فهي أن تغلق الأبواب كلها إلا واحداً.

ما هو Bastion Host، وما هو Jump Server؟

يعرف معجم NIST نقلاً عن CNSSI 4009 ال Bastion Host بأنه حاسوب لغرض خاص في الشبكة، صمم وضبط خصيصاً ليصمد أمام الهجمات (A special purpose computer on a network specifically designed and configured to withstand attacks). والاسم مأخوذ من الحصون القديمة، فالبرج البارز Bastion هو الجزء الذي يتلقى الهجوم أولاً ولذلك يبنى أقوى من بقية السور. وفي الواقع العملي هو سيرفر يقف على حافة الشبكة Perimeter، أي أنه مكشوف للإنترنت أو لشبكة أقل ثقة، وكل ما عليه موجه لهدف واحد هو أن يقبل اتصالاً موثقاً ثم يمرره إلى الداخل، فلا تطبيقات عليه ولا بيانات ولا أدوات.

أما ال Jump Server أو Jump Host فهو اسم وظيفي لا تعريف رسمي له، ويقصد به السيرفر الذي يدخل إليه المدير أولاً ثم «يقفز» منه إلى السيرفرات الأخرى، وغالباً يكون داخل منطقة إدارة Management Zone خاصة، ويصل إليه المدير من شبكة المكتب أو عبر VPN، ثم يعمل عليه فعلاً: يفتح جلسة RDP إلى سطح مكتب فيه أدوات الإدارة مثل kubectl وpsql وأدوات إدارة Active Directory، وقد يحفظ عليه جلسات وكلمات مرور. وتصف Microsoft في دليل حماية الوسطاء في الوصول المميز هذا النوع بأنه سطح مكتب بعيد Remote Desktop يستخدم في سيناريوهات الإدارة ويسمى غالباً Jump Server، وتنبه إلى أن الجلسات الحساسة كلها تمر عبره، فيستطيع المخترق الذي يسيطر عليه أن يسرق بيانات الدخول بأدوات سرقة بيانات الدخول المعروفة ويعيد استخدامها.

والصورة التالية تبين الفرق بين الشكلين:

مخطط من لوحتين: على اليمين Bastion Host يقف على خط الحافة بين الإنترنت والشبكة الخاصة، يصله حاسوب المدير الذي يبقى المفتاح عليه ويحاول المهاجمون الوصول إليه، ويمرر الاتصال إلى app-01 وdb-01 وweb-01. وعلى اليسار Jump Server داخل منطقة إدارة يصله المدير عبر VPN أو شبكة المكتب بجلسة RDP أو SSH، وعليه أدوات kubectl وpsql وRSAT وSSMS وجلسات محفوظة وبيانات دخول، ومنه يتصل بسيرفرات الإنتاج
ال Bastion يمرر الاتصال ولا يعمل عليه أحد، وال Jump Server يعمل عليه المدير وتعيش عليه الأدوات

أين يستخدم المصطلحان للشيء نفسه، وأين يختلفان؟

في أغلب الوثائق والمحادثات اليومية يستخدم الاسمان للشيء نفسه، فخدمة Azure تسمى Azure Bastion، ووثائق AWS تذكر «bastion hosts» و«jump boxes» معاً في الجملة نفسها، ومشروع JumpServer يقدم نفسه منصة وصول مميز. لذلك لا تتوقف عند الاسم، وإنما اسأل سؤالاً واحداً: هل يمر المستخدم عبر هذا السيرفر، أم يعمل عليه؟ والجدول التالي يلخص الفرق عندما يقصد كل اسم معناه الدقيق:

Bastion HostJump Server
مكانهعلى حافة الشبكة، مكشوف للإنترنت أو لشبكة أقل ثقةداخل منطقة إدارة، يصله المدير من الداخل أو عبر VPN
طريقة الاستخداميمرر الاتصال فقط Pass-through، ولا يفتح المستخدم عليه Shellيدخل إليه المدير ويعمل عليه، ثم يتصل منه بالسيرفرات
ما عليهأقل عدد ممكن من الحزم، دون أدوات ودون مفاتيحأدوات الإدارة، وأحياناً جلسات وبيانات دخول محفوظة
المفتاح الخاصيبقى على جهاز المستخدمقد يوجد على السيرفر نفسه، وهنا الخطر
الشكل الشائعLinux مع OpenSSH وProxyJumpWindows مع RDP، أو Linux بأدوات كثيرة
ما يراه من الجلسةبيانات مشفرة فقط، إلا إذا كان بوابة تنهي الجلسةكل شيء، لأن الجلسة تبدأ منه

والنصيحة العملية أن تبني ال Bastion بالمعنى الأول، أي سيرفراً يمرر ولا يعمل عليه أحد، أما إذا احتجت فعلاً إلى سطح مكتب إداري فيه أدوات Windows، فعامله كأصل حساس بحد ذاته، وضعه خلف بوابة تتحقق من الهوية، وسجل جلساته، ولا تحفظ عليه كلمات المرور.

كيف يعمل ProxyJump في OpenSSH؟

الطريقة القديمة للمرور عبر ال Bastion كانت أن تدخل إليه بجلسة SSH عادية، ثم تكتب منه أمر ssh مرة ثانية إلى السيرفر الداخلي، وهذه الطريقة تحتاج أن يكون مفتاحك على ال Bastion نفسه، أو أن تفعل Agent Forwarding حتى يستخدم ال Bastion مفتاحك من جهازك، وكلاهما سيئ كما سوف نرى. والطريقة الصحيحة هي الخيار -J، ويشرحه دليل ssh بأنه يتصل بالسيرفر الهدف عن طريق اتصال SSH أولاً بال Jump Host، ثم إنشاء توجيه TCP منه إلى الهدف النهائي، وهو اختصار للإعداد ProxyJump الموجود في دليل ssh_config. والصورة التالية تبين الخطوات:

مخطط من اليمين إلى اليسار: حاسوب يكتب ssh app-01 والمفتاح الخاص يبقى عليه، ثم أنبوب رمادي هو اتصال SSH الأول إلى ال bastion على المنفذ 22220 بالمستخدم jump دون Shell ومعه PermitOpen app-01:22 db-01:22، وداخله أنبوب أخضر هو اتصال SSH الثاني يمتد من الحاسوب حتى app-01 في الشبكة الخاصة 10.70.0.0/24. وأربع خطوات مرقمة: الدخول إلى ال bastion بالمفتاح، طلب قناة إلى app-01:22 بالخيار ssh -W، تحقق ال bastion من PermitOpen ثم فتح اتصال TCP، ثم اتصال SSH ثان داخل الأول لا يرى منه ال bastion إلا بيانات مشفرة
اتصال SSH ثان يمر داخل الأول، والمفتاح لا يغادر جهازك

في الصورة أعلاه لاحظ التالي:

  • الخطوة 1 دخول عادي إلى ال Bastion بمفتاحك وبحساب اسمه هنا jump، ولكن هذا الحساب لا يملك Shell ولا يحتاج إليه.
  • في الخطوة 2 يطلب ssh من ال Bastion قناة Channel من نوع direct-tcpip إلى app-01:22، وهذا ما يفعله الخيار -W الذي يضيفه ssh تلقائياً كما سوف ترى في مخرج التجربة.
  • في الخطوة 3 يتحقق ال Bastion من أن هذه الوجهة مسموحة، ثم يفتح اتصال TCP عادياً إلى السيرفر الداخلي، وهذا كل عمله.
  • في الخطوة 4 يبدأ ssh على جهازك اتصالاً ثانياً كاملاً مع app-01 داخل تلك القناة، بالتحقق من مفتاح السيرفر Host Key وبالمصادقة بمفتاحك، فالتشفير هنا من جهازك إلى app-01 مباشرة، وال Bastion لا يرى الأوامر ولا المخرج، ولا يحتاج إلى أي مفتاح يخصك.

ويمكنك أن تقفز عبر أكثر من سيرفر بفصلها بفاصلة، مثل ssh -J bastion,inner-jump app-01، ولاحظ ملاحظة مهمة في الدليل نفسه: الخيارات التي تكتبها في سطر الأوامر مثل -i و-o تنطبق غالباً على السيرفر الهدف لا على ال Jump Host، لذلك ضع إعدادات ال Bastion في ملف ~/.ssh/config كما سوف نفعل في التجربة.

لماذا تفضل ProxyJump على Agent Forwarding؟

ال ssh-agent برنامج يعمل على جهازك ويحفظ مفاتيحك مفتوحة في الذاكرة، والخيار -A أو الإعداد ForwardAgent yes يجعل السيرفر الذي دخلت إليه قادراً على أن يطلب من هذا البرنامج توقيع عمليات دخول جديدة، فتستطيع أن تكتب ssh db-01 من ال Bastion وكأن مفتاحك عليه. وقد يتساءل البعض: إذا كان المفتاح نفسه لا ينتقل، فأين المشكلة؟ والإجابة في دليل ssh_config نفسه، فهو ينبه إلى أن تفعيل Agent Forwarding يجب أن يكون بحذر، لأن من يستطيع تجاوز صلاحيات الملفات على السيرفر البعيد، أي root أو من سيطر عليه، يستطيع الوصول إلى ال ssh-agent المحلي عبر الاتصال المعاد توجيهه، وصحيح أنه لا يستطيع أن يأخذ المفتاح نفسه، ولكنه يستطيع أن يستخدمه للمصادقة بالهويات المحملة فيه. ويضيف دليل ssh في شرح الخيار -A أن البديل الأكثر أمناً قد يكون استخدام Jump Host عبر -J. والصورة التالية تبين الفرق:

مخطط من لوحتين: على اليمين ssh -A ops@bastion حيث يعاد توجيه ال ssh-agent من الحاسوب إلى ال bastion فيظهر عليه الملف /tmp/ssh-…/agent.136، ثم يستخدمه root على ال bastion ليدخل إلى db-01 بالأمر ssh admin@db-01 موقعاً بال agent الخاص بك. وعلى اليسار ssh -J حيث لا يوجد agent socket على ال bastion، ويرى root بيانات مشفرة فقط، ويصل الاتصال من الحاسوب إلى db-01 مباشرة
المفتاح لا يغادر جهازك في الحالتين، ولكن مع Agent Forwarding يوقع للمخترق ما دمت متصلاً

والقاعدة بسيطة: لا تفعل ForwardAgent إلى أي سيرفر لا تثق بكل من يملك root عليه، وال Bastion بالذات هو السيرفر المكشوف للإنترنت، أي أنه أول سيرفر نتوقع أن يهاجم. وسوف نرى هذا الخطر بمخرج حقيقي في التجربة العملية.

كيف تقوي ال Bastion؟

ال Bastion هو السيرفر الذي اخترت أن تكشفه، لذلك يستحق أكثر من إعدادات أي سيرفر آخر، والأساسيات المشتركة مثل المستخدم العادي وتعطيل كلمة المرور وجدار الحماية شرحناها في تأمين سيرفر VPS من أول دخول، وفيما يلي ما يضاف إليها:

  • المفاتيح فقط Key-only: PasswordAuthentication no وKbdInteractiveAuthentication no وPermitRootLogin no، والسبب أن ال Bastion يتلقى محاولات تخمين كلمات المرور طوال اليوم، والمفتاح لا يخمن.
  • المصادقة متعددة العوامل MFA: الشكل الأبسط في SSH مفتاح من نوع ed25519-sk محفوظ على مفتاح أمان FIDO2، فلا يعمل إلا بوجود المفتاح ولمسه، والإعداد PubkeyAuthOptions verify-required في دليل sshd_config يشترط التحقق من المستخدم على المفتاح نفسه بالرقم السري أو البصمة. وقد شرحنا هذه المفاتيح في Passkeys وFIDO2، وإضافة رمز TOTP عبر وحدات PAM في Linux في إدارة الهوية والوصول IAM والوصول المميز PAM.
  • حساب يمرر ولا يعمل: كتلة Match User تجعل حساب ال Bastion دون Shell عبر ForceCommand وPermitTTY no، وتسمح بالتوجيه المحلي فقط عبر AllowTcpForwarding local، وتحصر الوجهات المسموحة في PermitOpen، وتمنع Agent Forwarding وX11 والأنفاق. وينبه الدليل إلى أن منع التوجيه لا يحسن الأمن ما دام المستخدم يملك Shell، لأنه يستطيع تشغيل برنامج توجيه خاص به، ولهذا نمنع ال Shell أولاً.
  • أقل عدد ممكن من الحزم: لا تثبت على ال Bastion أي شيء لا يحتاجه SSH، فلا أدوات تطوير ولا curl ولا Python ولا عميل قاعدة بيانات، والسبب أن كل أداة عليه هي أداة جاهزة للمخترق الذي يصل إليه، وكل حزمة هي حزمة تحتاج إلى تحديث. وفي تجربتنا لا يوجد على ال Bastion المبني من debian:13 مع openssh-server أي من curl وwget وpython3 وnc وgcc.
  • السجلات خارج ال Bastion: فعل LogLevel VERBOSE حتى يسجل sshd بصمة المفتاح Fingerprint الذي دخل به كل شخص، وأرسل السجلات إلى سيرفر مركزي لا يملكه مدير ال Bastion، كما شرحنا في السجلات Logging للمطورين ومديري السيرفرات، والسبب أن من يصبح root على ال Bastion يستطيع أن يحذف سجلاته المحلية.
  • شهادات SSH قصيرة العمر: بدلاً من نسخ مفاتيح الفريق إلى كل سيرفر، يثق ال Bastion والسيرفرات بجهة توقيع واحدة عبر TrustedUserCAKeys، وتوقع هذه الجهة مفتاح كل مهندس لساعات بالأمر ssh-keygen -s كما في دليل ssh-keygen، فإذا غادر أحدهم لا تحتاج إلى حذف مفتاحه من أي مكان. وقد طبقنا ذلك خطوة بخطوة في تجربة شهادات SSH، فلا نعيده هنا.
  • السيرفرات الداخلية تقبل ال Bastion فقط: على كل سيرفر داخلي اسمح بالمنفذ 22 من عنوان ال Bastion وحده في جدار الحماية، وأضف في sshd نفسه AllowUsers [email protected] كما في التجربة، حتى لا يعتمد الحماية على طبقة واحدة.
  • ال Bastion لا يحمل شيئاً: لا مفاتيح خاصة، ولا ملفات إعداد فيها كلمات مرور، ولا بيانات، فإذا سقط فأنت تعيد بناءه من الصفر في دقائق، ويفضل أن يكون بناؤه آلياً من ملف إعداد حتى يكون ذلك سهلاً.
  • Bastion لكل بيئة: لا تجعل Bastion الإنتاج Production هو نفسه Bastion بيئة الاختبار، والسبب أن الوصول إلى بيئة الاختبار يعطى لعدد أكبر من الناس، ويجب ألا يكون طريقاً إلى الإنتاج.
  • حساب الطوارئ Break-Glass: ماذا لو توقف ال Bastion نفسه؟ جهز طريقاً بديلاً موثقاً، مثل لوحة المزود Console، بحساب طوارئ محمي ومراقب، كما شرحنا في ضوابط الوصول المميز.
⚠️
ال Bastion الذي يمرر الاتصال عبر ProxyJump لا يرى الأوامر التي تكتبها على السيرفرات الداخلية، لأن الجلسة مشفرة من جهازك إلى السيرفر مباشرة، فهو يعرف من دخل ومتى وإلى أي سيرفر ومنفذ فقط. لذلك إذا كنت تحتاج إلى تسجيل الأوامر نفسها، فسجلها على السيرفرات الداخلية بتسجيل جلسات sudo كما في تجربة sudo أو بأداة auditd، أو استخدم بوابة وصول تنهي الجلسة عندها كما في بوابات الوصول المميز.

وماذا عن خوادم Windows وRDP؟

المنطق نفسه ينطبق على Windows، ولكن الخطر أكبر، فدليل Microsoft في حماية الوسطاء لا ينصح باتصال RDP المباشر عبر الإنترنت، والسبب أن البروتوكول يملك حماية محدودة من الهجمات الحديثة مثل رش كلمات المرور Password Spray، وينصح بأن يمر عبر بوابة. والبوابة التي تأتي مع Windows Server هي دور Remote Desktop Gateway واختصاراً RD Gateway، وهي بحسب توثيقها تتيح اتصالات آمنة مشفرة بموارد Remote Desktop Services عبر الإنترنت دون الحاجة إلى VPN، وتعمل بشهادة TLS ينصح أن تكون صادرة من جهة عامة لأن البوابة مكشوفة للإنترنت. فتكون البوابة هي الباب الوحيد المكشوف، وأجهزة Windows خلفها لا تنشر المنفذ 3389 لأحد. والبديل مفتوح المصدر هو Apache Guacamole الذي يفتح RDP وSSH وVNC من المتصفح، ونذكره مع البوابات بعد قليل.

البدائل المدارة في السحابة

إذا كانت سيرفراتك في إحدى السحابات الكبيرة، فكل مزود يقدم خدمة تقوم بعمل ال Bastion دون أن تدير سيرفراً بنفسك، وهذه أشهرها كما يصفها توثيق كل مزود:

  • AWS Systems Manager Session Manager: بحسب توثيق Session Manager يتيح إدارة الأجهزة دون فتح منافذ واردة Inbound Ports ودون إدارة Bastion Hosts أو مفاتيح SSH، والصلاحيات فيه تأتي من سياسات IAM، وتعمل الجلسة عبر برنامج SSM Agent المثبت على الجهاز، ويمكن حفظ سجلات الجلسات كما في توثيق التسجيل.
  • Azure Bastion: بحسب توثيق Azure Bastion هو خدمة PaaS مدارة بالكامل توفر اتصال RDP وSSH بالأجهزة الافتراضية عبر TLS على المنفذ 443 من بوابة Azure أو من عميل SSH وRDP على جهازك، والأجهزة لا تحتاج إلى عنوان عام. ويأتي بأربع فئات SKU هي Developer وBasic وStandard وPremium، وتسجيل الجلسات Session Recording متاح في فئة Premium فقط.
  • Google Cloud IAP TCP Forwarding: بحسب توثيق IAP ينشئ نفقاً مشفراً تمرر عبره SSH وRDP وغيرهما إلى أجهزة ليس لها عنوان خارجي، فتسمح في الجدار الناري بالمنفذ 22 من النطاق 35.235.240.0/20 فقط، وتعطي المستخدم الدور IAP-Secured Tunnel User، ثم تتصل بالأمر gcloud compute ssh مع الخيار --tunnel-through-iap.

هل هذه الخدمات أفضل من Bastion تديره بنفسك؟ في أغلب الحالات نعم إذا كانت كل سيرفراتك عند المزود نفسه، والسبب أن المزود يحدثها ويراقبها، وأنها تربط الصلاحيات بحسابات IAM التي تديرها أصلاً، ودليل Microsoft السابق يصف هذه الخدمات السحابية بأنها تعطي المهاجم سطح هجوم محدوداً مقارنة بأجهزة الوصول التي يديرها فريق مشغول. ولكن لاحظ أنها تربطك بالمزود، ولا تفيدك في سيرفرات على VPS عند مزود صغير أو في مكتبك، وأن قوتها كلها مرهونة بحماية حسابات لوحة السحابة نفسها بMFA قوية.

بوابات الوصول المميز وZero Trust

ال Bastion التقليدي يعرف المفتاح الذي دخلت به، ولكنه لا يعرف أن هذا المفتاح لفلان، ولا يعرف لماذا دخل، ولا يرى ما فعل. والخطوة التالية هي بوابة الوصول المميز Privileged Access Gateway، وهي Bastion تنهي الجلسة عندها، فتتحقق من هويتك عبر مزود الهوية بالدخول الموحد SSO وMFA، ثم تصدر لك صلاحية مؤقتة أو تأخذ السر من خزنة، وقد تطلب موافقة شخص آخر، ثم تفتح الجلسة إلى السيرفر وتسجلها كاملة. وهذا هو التطبيق العملي لفكرة Zero Trust التي شرحناها في ما هو VPN؟، أي أن كل اتصال يتحقق منه بهوية المستخدم لا بمكانه في الشبكة. والصورة التالية تبين الفكرة، وتحتها المقابل المدار في السحابات:

مخطط في الأعلى: المدير يدخل من المتصفح أو ssh إلى مزود الهوية بالدخول الموحد وMFA، ثم إلى بوابة فيها شهادة لساعة واحدة وتسجيل للجلسة وموافقة وصلاحية مؤقتة وسجل تدقيق، ومن البوابة إلى Linux SSH وPostgreSQL وWindows RDP دون أي منفذ وارد من الإنترنت. وفي الأسفل ثلاث بطاقات: Google Cloud IAP مع TCP forwarding والسماح للنطاق 35.235.240.0/20 فقط مع دور IAM، وAzure Bastion مع RDP وSSH عبر TLS 443 دون عنوان عام للأجهزة، وAWS Systems Manager Session Manager مع وكيل على الجهاز دون منفذ وارد مفتوح
البوابة تعرف المستخدم وتصدر له صلاحية مؤقتة وتسجل الجلسة، والسحابات تقدم المقابل المدار

وأشهر الأدوات مفتوحة المصدر في هذه الفئة أربع، وقد راجعنا تراخيصها بالتفصيل في الأدوات مفتوحة المصدر للوصول المميز، وفيما يلي ما يخص موضوعنا منها:

  • Teleport: منصة وصول بشهادات قصيرة العمر وتسجيل للجلسات تغطي SSH وKubernetes وقواعد البيانات وWindows، والكود المصدري تحت AGPL-3.0، أما نسخة Community Edition الجاهزة فتحت ترخيص خاص يقصرها على المؤسسات الأصغر من حد معين، فاقرأه قبل أن تعتمد عليها.
  • Warpgate: بوابة Bastion في ملف تنفيذي واحد تحت Apache-2.0، تمرر SSH وHTTPS وقواعد البيانات وتسجل الجلسات وتدعم OIDC، وهي الأقرب إلى «Bastion يعرف المستخدم» بأقل تعقيد.
  • JumpServer: منصة PAM كاملة بواجهة ويب تحت GPL-3.0، مع مكونات تجارية في الإصدار Enterprise كما يوضح مستودعها، وتناسب من يريد إدارة أصول وأذونات كثيرة ولديه من يشغلها.
  • Apache Guacamole: بوابة من المتصفح إلى RDP وVNC وSSH تحت Apache-2.0 دون برنامج على جهاز المستخدم، مع تسجيل الجلسات وإعادة تشغيلها، وتناسب من يحتاج أساساً إلى سطح مكتب Windows عن بعد.

وسوف نقارن هذه البوابات الثلاث الأولى عملياً في مقال قادم بإذن الله بعنوان «بوابة الوصول المميز للسيرفرات: Teleport أم Warpgate أم JumpServer؟»، أما الصورة الكاملة للوصول المميز وضوابطه فهي في مقال IAM وPAM.

ما الذي تطلبه الضوابط الأساسية NCA ECC؟

إذا كانت جهتك ملزمة بالضوابط الأساسية للأمن السيبراني ECC-2:2024 الصادرة عن الهيئة الوطنية للأمن السيبراني، وقد شرحناها في الضوابط الأساسية NCA ECC بلغة بسيطة، فالضوابط لا تذكر Bastion أو Jump Server بالاسم، ولكن عدداً منها يقود إليه مباشرة، وهذه نصوصها كما في وثيقة الضوابط:

الضابطما يطلبهكيف يساعدك ال Bastion أو البوابة
2-2-3-2التحقق متعدد العناصر للدخول عن بعد وللحسابات ذات الصلاحيات الهامة، مع تحديد العناصر بناء على تقييم أثر فشل التحقق وتجاوزهنقطة واحدة تطبق فيها MFA على كل دخول إداري، بدلاً من ضبطها في كل سيرفر
2-2-3-4إدارة الصلاحيات الهامة والحساسة Privileged Access Managementحساب لكل شخص، وصلاحيات مؤقتة وشهادات قصيرة العمر، وبوابة تسجل الجلسات
2-5-3-1العزل والتقسيم المادي أو المنطقي لأجزاء الشبكة باستخدام جدار الحماية ومبدأ الدفاع الأمني متعدد المراحلالسيرفرات في شبكة خاصة، وال Bastion هو الطريق الوحيد إلى منطقة الإدارة
2-5-3-5تقييد خدمات الشبكة وبروتوكولاتها ومنافذها وإدارتهامنفذ إدارة واحد مكشوف بدلاً من منفذ في كل سيرفر، وPermitOpen يحصر الوجهات
2-12-3-2تفعيل سجلات الأحداث للحسابات ذات الصلاحيات الهامة ولأحداث الدخول عن بعدسجل واحد يجمع كل عمليات الدخول الإداري وبصمات المفاتيح والوجهات
2-12-3-5مدة الاحتفاظ بسجلات الأحداث، على ألا تقل عن 12 شهراًإرسال سجلات ال Bastion والسيرفرات إلى سيرفر مركزي يحفظها المدة المطلوبة

ولاحظ أن المدقق لا يكتفي بوجود ال Bastion، وإنما يطلب دليلاً على أن السيرفرات لا تقبل الإدارة إلا منه، مثل قواعد الجدار الناري ومخرج فحص من الخارج، وقد شرحنا شكل هذه الأدلة في كيف تثبت ذلك للمدقق.

تجربة عملية: Bastion وسيرفران داخليان في Docker

الآن سوف نبني كل ما سبق على جهازك بثلاثة Containers: ال Bastion باسم lab-bastion-gw وهو الوحيد الذي ينشر منفذاً، وعلى العنوان 127.0.0.1:22220 فقط، وسيرفران داخليان app-01 وdb-01 على شبكة Docker داخلية --internal لا تخرج إلى أي مكان ولا تنشر أي منفذ. وتحتاج إلى Docker مع Compose، وإذا لم يكن مثبتاً فانظر تثبيت Docker على Ubuntu، وإلى عميل OpenSSH على جهازك، وكل الأوامر التالية تعمل من مجلد واحد، ولا تلمس ملفات ~/.ssh الخاصة بك لأننا نمرر لكل أمر ملف إعداد خاصاً بالتجربة.

الملفات

نبدأ بملف Dockerfile واحد للسيرفرات الثلاثة، فيه Debian 13 مع openssh-server فقط:

FROM debian:13
RUN apt-get update \
 && apt-get install -y --no-install-recommends openssh-server \
 && rm -rf /var/lib/apt/lists/* \
 && mkdir -p /run/sshd
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["sh", "/entrypoint.sh"]

والملف entrypoint.sh ينشئ المستخدمين المذكورين في المتغير SSH_USERS ويضع لكل منهم مفتاحه العام بالصلاحيات الصحيحة، ثم يشغل sshd:

#!/bin/sh
# Create each user in $SSH_USERS and install its public key from /keys/<user>.pub
set -e
for u in $SSH_USERS; do
  id "$u" >/dev/null 2>&1 || useradd -m -s /bin/bash "$u"
  install -d -m 700 -o "$u" -g "$u" "/home/$u/.ssh"
  install -m 600 -o "$u" -g "$u" "/keys/$u.pub" "/home/$u/.ssh/authorized_keys"
done
exec /usr/sbin/sshd -D -e

وهذا إعداد ال Bastion، وهو قلب التجربة:

# /etc/ssh/sshd_config.d/bastion.conf on the bastion
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
LogLevel VERBOSE
LogVerbose *:server_request_direct_tcpip():*
# ops has a shell and exists only to show the agent-forwarding risk
AllowUsers jump ops

# jump: a pass-through account for ProxyJump, nothing else
Match User jump
    AllowTcpForwarding local
    PermitOpen app-01:22 db-01:22
    AllowAgentForwarding no
    AllowStreamLocalForwarding no
    X11Forwarding no
    PermitTunnel no
    GatewayPorts no
    PermitTTY no
    ForceCommand /usr/sbin/nologin

في الإعداد أعلاه لاحظ التالي:

  • الحساب jump لا يستطيع فتح Shell بسبب ForceCommand /usr/sbin/nologin وPermitTTY no، ولكن ProxyJump لا يحتاج إلى Shell أصلاً، لأنه يطلب قناة توجيه لا جلسة.
  • AllowTcpForwarding local يسمح بالتوجيه من جهة العميل فقط، فلا يستطيع أحد أن يفتح منفذاً عكسياً على ال Bastion بالخيار -R.
  • PermitOpen يحصر الوجهات في المنفذ 22 على السيرفرين. ولاحظ ما يقوله دليل sshd_config: لا تجري أي مطابقة أنماط ولا أي بحث عن العناوين على الأسماء المكتوبة، أي أن app-01:22 تسمح بالاسم فقط، ومن يطلب العنوان 10.70.0.11 يرفض، وسوف نرى ذلك بعد قليل.
  • LogVerbose سطر سوف نشرحه في قسم السجلات.
  • الحساب ops يملك Shell عمداً، ووجوده هنا فقط لنرى خطر Agent Forwarding على Jump Server يدخل إليه المديرون، فلا تضعه على Bastion حقيقي.

وعلى السيرفرين الداخليين إعداد أقصر، أهم ما فيه السطر الأخير الذي يقبل المستخدم admin من عنوان ال Bastion فقط:

# /etc/ssh/sshd_config.d/target.conf on app-01 and db-01
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
LogLevel VERBOSE
AllowUsers [email protected]

وأخيراً ملف compose.yaml، وفيه شبكتان: lab-bastion-edge تمثل الإنترنت، وlab-bastion-mgmt شبكة داخلية للإدارة:

name: lab-bastion
services:
  bastion:
    build: .
    image: lab-bastion-sshd:1
    container_name: lab-bastion-gw
    environment:
      SSH_USERS: "jump ops"
    volumes:
      - ./keys:/keys:ro
      - ./bastion.conf:/etc/ssh/sshd_config.d/bastion.conf:ro
    ports:
      - "127.0.0.1:22220:22"
    networks:
      edge: {}
      mgmt:
        ipv4_address: 10.70.0.10
  app-01:
    image: lab-bastion-sshd:1
    container_name: lab-bastion-app01
    hostname: app-01
    environment:
      SSH_USERS: "admin"
    volumes:
      - ./keys:/keys:ro
      - ./target.conf:/etc/ssh/sshd_config.d/target.conf:ro
    networks:
      mgmt:
        ipv4_address: 10.70.0.11
        aliases: [app-01]
  db-01:
    image: lab-bastion-sshd:1
    container_name: lab-bastion-db01
    hostname: db-01
    environment:
      SSH_USERS: "admin"
    volumes:
      - ./keys:/keys:ro
      - ./target.conf:/etc/ssh/sshd_config.d/target.conf:ro
    networks:
      mgmt:
        ipv4_address: 10.70.0.12
        aliases: [db-01]
networks:
  edge:
    name: lab-bastion-edge
  mgmt:
    name: lab-bastion-mgmt
    internal: true
    ipam:
      config:
        - subnet: 10.70.0.0/24

ولاحظ أن ال Bastion وحده على الشبكتين، وأنه وحده ينشر منفذاً، وعلى 127.0.0.1 حتى لا يصل إليه أحد من شبكتك المحلية أثناء التجربة.

التشغيل

ننشئ مفتاحاً للتجربة باسم laptop يمثل حاسوب المدير، ونضع مفتاحه العام للمستخدمين الثلاثة في المجلد keys الذي يقرؤه السيرفرات، ولاحظ أن المفتاح الخاص يبقى خارج هذا المجلد، ثم نكتب ملف إعداد عميل خاصاً بالتجربة ونشغل كل شيء:

ssh-keygen -q -t ed25519 -N '' -C laptop -f laptop
mkdir keys
for u in jump ops admin; do cp laptop.pub keys/$u.pub; done
cat > lab.conf <<'EOF'
Host *
    IdentityFile ./laptop
    IdentitiesOnly yes
    UserKnownHostsFile ./known_hosts
    StrictHostKeyChecking accept-new
    ConnectTimeout 5
EOF
docker compose up -d --build
docker compose ps --format 'table {{.Name}}\t{{.Ports}}'

والمخرج سوف يكون كما يلي:

NAME                PORTS
lab-bastion-app01
lab-bastion-db01
lab-bastion-gw      127.0.0.1:22220->22/tcp

ثلاثة Containers ومنفذ واحد فقط على جهازك. وقبل أن نتابع، لنتأكد من أن sshd فهم إعدادنا كما نريد، فالأمر sshd -T مع الخيار -C يطبع الإعداد النهائي لمستخدم وعنوان محددين بعد تطبيق كتل Match:

docker exec lab-bastion-gw sh -c "sshd -T -C user=jump,host=laptop,addr=203.0.113.5 | grep -E '^(permitopen|allowtcpforwarding|allowagentforwarding|permittty|forcecommand) '"

والمخرج سوف يكون كما يلي:

permittty no
allowtcpforwarding local
allowagentforwarding no
forcecommand /usr/sbin/nologin
permitopen app-01:22 db-01:22

وهذا الأمر مفيد جداً على أي سيرفر حقيقي قبل أن تعيد تشغيل sshd، لأنه يريك ما سوف يطبق فعلاً لا ما تظن أنك كتبته. ولنتأكد أيضاً من أن ال Bastion لا يحمل أدوات:

docker exec lab-bastion-gw sh -c 'which curl wget python3 nc gcc || echo "none found"'

والمخرج سوف يكون كما يلي:

none found

الدخول المباشر يفشل

جهازك المضيف ليس مثالاً جيداً للإنترنت، لأنه هو الذي يشغل شبكات Docker، لذلك نحاول الوصول إلى app-01 من Container مؤقت على شبكة lab-bastion-edge التي تمثل الإنترنت، مرة بالعنوان ومرة بالاسم:

docker run --rm --network lab-bastion-edge --entrypoint ssh lab-bastion-sshd:1 -o ConnectTimeout=5 [email protected] hostname
docker run --rm --network lab-bastion-edge --entrypoint ssh lab-bastion-sshd:1 -o ConnectTimeout=5 admin@app-01 hostname

والمخرج سوف يكون كما يلي:

ssh: connect to host 10.70.0.11 port 22: Connection timed out
ssh: Could not resolve hostname app-01: No address associated with hostname

لا يوجد طريق إلى الشبكة الداخلية، ولا يعرف من في الخارج حتى أسماء السيرفرات. وماذا لو حاول أحد أن يفتح Shell على ال Bastion بالحساب jump؟

ssh -F lab.conf -T -p 22220 [email protected]

والمخرج سوف يكون كما يلي:

This account is currently not available.

المفتاح صحيح والدخول نجح، ولكن ForceCommand أنهى الجلسة فوراً، فهذا الحساب لا يصلح إلا للمرور.

المرور عبر ال Bastion بالخيار -J

الآن نصل إلى app-01 عبر ال Bastion، ونطلب منه أن يطبع اسمه والمتغير SSH_CONNECTION الذي يبين من أين جاء الاتصال:

ssh -F lab.conf -J [email protected]:22220 admin@app-01 'hostname; echo $SSH_CONNECTION'

والمخرج سوف يكون كما يلي:

Warning: Permanently added 'app-01' (ED25519) to the list of known hosts.
app-01
10.70.0.10 33668 10.70.0.11 22

لاحظ أن ssh سجل مفتاح السيرفر باسم app-01 في ملف known_hosts على جهازك، أي أن التحقق من هوية السيرفر الداخلي يحدث على جهازك لا على ال Bastion، ولاحظ أن app-01 يرى الاتصال قادماً من 10.70.0.10 وهو عنوان ال Bastion، وهذا ما يجعل السطر AllowUsers [email protected] يعمل. ولاحظ أيضاً أن الاسم app-01 حله ال Bastion لا جهازك، فجهازك لا يعرف هذا الاسم أصلاً.

ملف الإعداد: ProxyJump في ~/.ssh/config

كتابة -J مع كل أمر متعبة، والطريقة اليومية أن تضع الإعداد في ملف الإعداد. نضيف في بداية lab.conf الأسطر التالية، وفي عملك الحقيقي تضعها في ~/.ssh/config مع عنوان ال Bastion الحقيقي:

Host bastion
    HostName 127.0.0.1
    Port 22220
    User jump

Host app-01 db-01
    User admin
    ProxyJump bastion

ولنسأل ssh عن الإعداد الذي سوف يطبقه على db-01، فالخيار -G يطبعه دون أن يتصل:

ssh -F lab.conf -G db-01 | grep -E '^(user|hostname|port|proxyjump|forwardagent) '

والمخرج سوف يكون كما يلي:

user admin
hostname db-01
port 22
forwardagent no
proxyjump bastion

والآن يكفي الاسم وحده، ويعمل scp بالطريقة نفسها لأنه يقرأ الملف نفسه:

ssh -F lab.conf app-01 hostname
ssh -F lab.conf db-01 hostname
echo "backup-2026-10-08" > note.txt
scp -F lab.conf note.txt db-01:/tmp/ && ssh -F lab.conf db-01 cat /tmp/note.txt

والمخرج سوف يكون كما يلي:

app-01
Warning: Permanently added 'db-01' (ED25519) to the list of known hosts.
db-01
backup-2026-10-08

وإذا أردت أن ترى ما يحدث في الداخل، فشغل الأمر مع -v واختر الأسطر المهمة:

ssh -v -F lab.conf app-01 true 2>&1 | grep -E 'Setting implicit ProxyCommand|Executing proxy|Authenticated to|channel_connect_stdio_fwd'

والمخرج سوف يكون كما يلي:

debug1: Setting implicit ProxyCommand from ProxyJump: ssh -F lab.conf -v -W '[%h]:%p' bastion
debug1: Executing proxy command: exec ssh -F lab.conf -v -W '[app-01]:22' bastion
Authenticated to 127.0.0.1 ([127.0.0.1]:22220) using "publickey".
debug1: channel_connect_stdio_fwd: app-01:22
Authenticated to app-01 (via proxy) using "publickey".

في المخرج أعلاه لاحظ التالي:

  • ProxyJump في الحقيقة يشغل ssh ثانياً بالخيار -W '[app-01]:22'، وهو الخيار الذي يربط مدخل البرنامج ومخرجه بقناة إلى الوجهة، وهذا ما كان الناس يكتبونه بأيديهم في ProxyCommand قبل ظهور ProxyJump.
  • هناك مصادقتان منفصلتان، الأولى مع ال Bastion والثانية مع app-01 «via proxy»، وكلتاهما بمفتاحك الذي لم يغادر جهازك.

نكسر القيود: وجهات غير مسموحة

ماذا يحدث إذا طلب أحدهم السيرفر بعنوانه بدلاً من اسمه، أو حاول أن يصل عبر ال Bastion إلى منفذ آخر مثل 80؟

ssh -F lab.conf -J [email protected]:22220 [email protected] hostname
ssh -F lab.conf -p 22220 -W app-01:80 [email protected] </dev/null

والمخرج سوف يكون كما يلي:

channel 0: open failed: administratively prohibited: open failed
stdio forwarding failed
Connection closed by UNKNOWN port 65535
channel 0: open failed: administratively prohibited: open failed
stdio forwarding failed

الرسالة administratively prohibited تعني أن sshd على ال Bastion رفض فتح القناة بسبب الإعداد، وهي الرسالة التي سوف تراها إذا نسيت إضافة سيرفر جديد إلى PermitOpen. ولاحظ أن الطلب بالعنوان رفض مع أنه يشير إلى السيرفر نفسه المسموح باسمه، والسبب كما ذكرنا أن PermitOpen يقارن النص كما هو، فإما أن تكتب الاسم والعنوان معاً في القائمة، أو أن تتفق مع فريقك على الأسماء فقط.

ماذا تسجل ال Bastion؟

مع LogLevel VERBOSE يسجل sshd بصمة المفتاح لكل دخول، ويسجل الطلبات المرفوضة مع وجهتها، ولكنه لا يسجل الوجهات المسموحة، فهذه الرسالة في كود OpenSSH من مستوى debug. لذلك أضفنا السطر LogVerbose *:server_request_direct_tcpip():* الذي يرفع رسائل هذه الدالة وحدها إلى السجل، مع العلم أن دليل sshd_config يصف LogVerbose بأنه مخصص لتتبع الأخطاء، فاختبره بعد كل ترقية لأن اسم الدالة قد يتغير. وهذا مقتطف من سجل ال Bastion بعد الأوامر السابقة:

docker logs lab-bastion-gw 2>&1 | grep -E 'Accepted publickey|direct_tcpip|denied'
Accepted publickey for jump from 192.168.48.1 port 58118 ssh2: ED25519 SHA256:jWZY5H6ljOihK+THu6nSnZhCeOCsV/znAU99tVPsB0c
debug1: serverloop.c:server_request_direct_tcpip():424 (bin=sshd-session, pid=120): originator 127.0.0.1 port 65535, target app-01 port 22
Accepted publickey for jump from 192.168.48.1 port 58126 ssh2: ED25519 SHA256:jWZY5H6ljOihK+THu6nSnZhCeOCsV/znAU99tVPsB0c
debug1: serverloop.c:server_request_direct_tcpip():424 (bin=sshd-session, pid=128): originator 127.0.0.1 port 65535, target 10.70.0.11 port 22
Received request from 192.168.48.1 port 58126 to connect to host 10.70.0.11 port 22, but the request was denied.

وعلى app-01 يظهر الدخول نفسه قادماً من ال Bastion وبالبصمة نفسها:

docker logs lab-bastion-app01 2>&1 | grep 'Accepted publickey'
Accepted publickey for admin from 10.70.0.10 port 37582 ssh2: ED25519 SHA256:jWZY5H6ljOihK+THu6nSnZhCeOCsV/znAU99tVPsB0c

في السجلات أعلاه لاحظ التالي:

  • بصمة المفتاح SHA256:jWZY… هي التي تربط الدخول إلى ال Bastion بالدخول إلى app-01، ولذلك يجب أن يكون لكل شخص مفتاحه الخاص، فالمفتاح المشترك بين الفريق يجعل هذا السجل بلا قيمة.
  • العنوان 192.168.48.1 هو بوابة شبكة Docker في جهازنا، وعلى Bastion حقيقي سوف ترى العنوان العام للمستخدم.
  • لا يوجد في أي من السجلين أمر واحد مما كتبناه على app-01، وهذا ما ذكرناه في التحذير السابق، فتسجيل الأوامر يحدث على السيرفر الداخلي أو في بوابة تنهي الجلسة.
  • هذه السجلات داخل ال Containers فقط، وفي الإنتاج ترسلها إلى سيرفر مركزي، وقد شرحنا ما يسجل وكيف يحفظ في سجل التدقيق Audit Trail.

خطر Agent Forwarding بمخرج حقيقي

الآن نرى ما وصفه دليل ssh. نشغل ssh-agent جديداً خاصاً بالتجربة ونحمل فيه المفتاح، ثم ندخل إلى ال Bastion بالحساب ops مع -A كما يفعل كثير من المديرين على Jump Server، ونبقي الجلسة مفتوحة عشرين ثانية:

eval "$(ssh-agent -s)" >/dev/null
ssh-add laptop
ssh -F lab.conf -A -p 22220 [email protected] 'echo "ops session: $SSH_AUTH_SOCK"; sleep 20' &

والمخرج سوف يكون كما يلي:

Identity added: laptop (laptop)
ops session: /tmp/ssh-7mFd8bZyun/agent.136

وأثناء الجلسة نلعب دور مخترق وصل إلى root على ال Bastion، وهذا ما يمثله docker exec هنا، فيبحث عن ملفات ال agent ثم يستخدمها ليدخل إلى db-01:

docker exec lab-bastion-gw sh -c 'ls -l /tmp/ssh-*/agent.*'
docker exec lab-bastion-gw sh -c 'export SSH_AUTH_SOCK=$(ls /tmp/ssh-*/agent.*); ssh-add -l; ssh -o StrictHostKeyChecking=accept-new admin@db-01 "hostname; id -un"'

والمخرج سوف يكون كما يلي:

srwxrwxr-x 1 ops ops 0 Oct  8 09:45 /tmp/ssh-7mFd8bZyun/agent.136
256 SHA256:jWZY5H6ljOihK+THu6nSnZhCeOCsV/znAU99tVPsB0c laptop (ED25519)
Warning: Permanently added 'db-01' (ED25519) to the list of known hosts.
db-01
admin

root على ال Bastion لا يملك مفتاحنا، ولم يقرأه من أي ملف، ومع ذلك دخل إلى db-01 باسم admin، لأن ال agent على جهازنا وقع له طلب الدخول عبر الاتصال المعاد توجيهه، وسجل db-01 سوف يظهر هذا الدخول ببصمة مفتاحنا نحن. والآن نكرر الشيء نفسه مع ProxyJump، فندخل إلى app-01 عبر ال Bastion ونبحث أثناء الجلسة عن الملفات نفسها:

ssh -F lab.conf app-01 'sleep 15' &
docker exec lab-bastion-gw sh -c 'ls -l /tmp/ssh-*/agent.*'

والمخرج سوف يكون كما يلي:

ls: cannot access '/tmp/ssh-*/agent.*': No such file or directory

لا يوجد ما يستخدمه المخترق على ال Bastion، لأن ProxyJump لا يعيد توجيه ال agent إلى ال Bastion أصلاً، وال Bastion ينقل بيانات مشفرة لا يفهمها. وبعد ذلك أوقف ال agent الخاص بالتجربة بالأمر ssh-agent -k.

التنظيف

نحذف ال Containers والشبكتين بالأمر down، ثم ال Image التي بنيناها، ثم ملفات التجربة:

docker compose down
docker image rm lab-bastion-sshd:1
rm -f laptop laptop.pub known_hosts note.txt lab.conf
rm -rf keys

أي حل يناسبك؟

حالتكالحل الأنسبوالسبب
سيرفر أو اثنان على VPS، وتديرهما وحدكSSH بالمفاتيح مع جدار حماية، أو VPN صغيرال Bastion هنا سيرفر إضافي تحميه دون فائدة كبيرة
عدة سيرفرات Linux وفريق صغيرBastion مع ProxyJump وحساب لكل شخص وشهادات قصيرة العمرباب واحد بسيط، ولا يحتاج إلى برنامج على أجهزة الفريق
كل السيرفرات عند مزود سحابي كبيرSession Manager أو Azure Bastion أو IAPلا تدير سيرفراً، والصلاحيات من IAM الذي تديره أصلاً
خوادم WindowsRD Gateway أو Guacamole أو Azure Bastion، وليس RDP مكشوفاًRDP لا يصلح للإنترنت مباشرة
جهة ملزمة بتسجيل الجلسات والموافقات، أو فريق كبيربوابة وصول مميز مثل Teleport أو Warpgate أو JumpServerتعرف المستخدم وتسجل الجلسة وتصدر صلاحيات مؤقتة

أخطاء شائعة

  • Bastion وما زالت السيرفرات مكشوفة: يبنى ال Bastion ثم يبقى المنفذ 22 مفتوحاً على السيرفرات «احتياطاً»، فيصبح لديك ستة أبواب بدل خمسة. أغلق المنافذ العامة وتحقق من الخارج.
  • مفاتيح خاصة على ال Bastion: مفتاح فريق محفوظ عليه حتى يدخل منه الجميع إلى السيرفرات، وهو أول ما سوف يأخذه من يصل إليه. استخدم ProxyJump حتى تبقى المفاتيح على أجهزة أصحابها.
  • ForwardAgent yes في Host *: يعيد توجيه ال agent إلى كل سيرفر تدخل إليه، ومنها ال Bastion، وقد رأيت النتيجة في التجربة.
  • حساب مشترك: حساب jump واحد بمفتاح واحد يستخدمه الفريق كله، فلا تعرف من دخل. الحساب قد يكون مشتركاً، ولكن المفتاح أو الشهادة لكل شخص، أو حساب لكل شخص.
  • ال Bastion يتحول إلى سيرفر أدوات: يثبت عليه أحدهم عميل قاعدة البيانات «مؤقتاً»، ثم سكربتات النسخ الاحتياطي، ثم يصبح Jump Server بكل مخاطره. راجع الحزم دورياً.
  • السجلات على ال Bastion وحده: من يصل إلى root يحذفها، والضوابط تطلب حفظها 12 شهراً على الأقل، فأرسلها إلى سيرفر مركزي.
  • Bastion واحد بلا بديل: إذا توقف لا يستطيع أحد إصلاح السيرفرات، فجهز طريق طوارئ موثقاً ومراقباً.

الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

  • كل سيرفر يفتح منفذ الإدارة للإنترنت باب يحتاج إلى تحديث ومراقبة، والحل أن يبقى باب واحد مقوى والسيرفرات خلفه دون منافذ عامة.
  • Bastion Host بتعريف NIST حاسوب صمم ليصمد أمام الهجمات، ويقف على الحافة ويمرر الاتصال، أما Jump Server فيعمل عليه المدير داخل منطقة إدارة، والاسمان يستخدمان كثيراً للشيء نفسه، فاسأل هل تمر عبره أم تعمل عليه.
  • استخدم ssh -J وProxyJump في ~/.ssh/config، فالجلسة مشفرة من جهازك إلى السيرفر الداخلي والمفتاح لا يغادر جهازك، ولا تفعل Agent Forwarding إلى ال Bastion لأن root عليه يستطيع أن يستخدم مفتاحك ما دمت متصلاً.
  • قو ال Bastion بالمفاتيح فقط وMFA، وحساب دون Shell مع PermitOpen بأسماء الوجهات، وأقل عدد من الحزم، وسجلات ترسل إلى خارجه، وشهادات قصيرة العمر.
  • ال Bastion عبر ProxyJump يعرف من دخل وإلى أين، ولا يرى الأوامر، فإذا احتجت تسجيل الجلسات فسجلها على السيرفرات أو استخدم بوابة وصول مميز.
  • في السحابة استخدم Session Manager أو Azure Bastion أو IAP، ولخوادم Windows استخدم RD Gateway أو Guacamole بدلاً من RDP مكشوف.
  • الضوابط 2-2-3-2 و2-2-3-4 و2-5-3-1 و2-5-3-5 و2-12-3-2 في NCA ECC تقودك إلى هذا التصميم، والمدقق يطلب دليلاً على أن السيرفرات لا تقبل الإدارة إلا من الباب الواحد.
دليلإدارة الهوية والوصول IAM والوصول المميز PAMمن يملك صلاحية root على سيرفراتك ومنذ متى، وكيف تضبط حسابات المديرين بصلاحيات مؤقتة وجلسات مسجلة.دليلتأمين سيرفر VPS على Ubuntu من أول دخولقبل أن تثبت أي تطبيق على سيرفرك الجديد، هذه الخطوات تغلق أبرز الثغرات وأولها الدخول بمفتاح SSH بدل كلمة المرور.دليلما هو النفق Tunnel في الشبكات؟ ومتى تحتاج إليهكيف تصل إلى قاعدة بيانات لا تراها من الإنترنت، وتنشر خدمة من خلف CGNAT دون فتح منافذ، ومتى تحتاج إلى VPN بدل النفق.دليلما هو VPN؟ الشبكة الخاصة الافتراضية من المشكلة إلى الأنواعلماذا تحتاج شبكة خاصة افتراضية VPN، وكيف تغلف حزمك وتشفرها داخل نفق، وأي نوع منها يناسب فريقك.دليلتقسيم الشبكة Network Segmentation بال VLAN وال VPCلماذا تصل كاميرا مخترقة إلى قاعدة بياناتك، وكيف تمنع ذلك بتقسيم الشبكة إلى مناطق لا يمر بينها إلا ما تسمح به.دليلالضوابط الأساسية للأمن السيبراني NCA ECC بلغة بسيطةكيف تقرأ الضوابط الأساسية ECC دون أن تضيع في أرقامها، وعلى من تنطبق، وما المطلوب من جهتك فعلاً في كل مكون.

سجل التحديثات

  • أكتوبر 2026: كتابة المقال، وتنفيذ التجربة العملية بثلاثة Containers على Debian 13.7 مع OpenSSH 10.0p2، وعميل OpenSSH 9.9p1، وDocker Engine 29.8.0 مع Docker Compose v5.5.1.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.