
تحصين خادم VPS خلال الساعة الأولى
عنوان IPv4 عام يبدأ فحصه خلال دقائق من أول حزمة بيانات يرسلها، على يد أجهزة لم تسمع بك يومًا ولن تسمع. وهذا في الواقع خبر جيد: فمعظم ما يستهدف خادمًا جديدًا هو هجوم عام غير موجّه، وساعة واحدة مدروسة تكفي لإحباط أغلبه تقريبًا. لكن الساعة نفسها، إن نُفّذت بالترتيب الخاطئ، تُقفل الباب في وجهك على خادم لا يستطيع أحد الدخول إليه نيابة عنك.
نمنحك صدفة root خلال نحو 47 ثانية من تأكيد الدفع، ثم نتوقف عن التدخل عمدًا. لا نُثبّت أي عميل مراقبة، ولا ندير جدار الحماية الخاص بك، ولا نحتفظ بنسخة من بيانات اعتمادك — فالمفتاح الذي نملكه هو مفتاح يمكن إرغامنا على تسليمه، وهذا المبدأ يسري على المنصة بأكملها. والنتيجة بسيطة وتستحق أن تُقال بوضوح: أمان خادمك هو الحالة التي تتركه عليها خلال الساعة الأولى.
وفيما يلي تلك الساعة، بالترتيب الذي نتّبعه نحن أنفسنا، على Debian 13 وUbuntu 24.04. إعدادان اثنان يؤديان معظم العمل. أما بقية هذه الصفحة فهي موجودة بسبب ثلاثة أمور تبدو صحيحة، وتجتاز الفحص النهائي في كل دليل تعليمي، لكنها خاطئة بهدوء: إعداد SSH إضافي (drop-in) يُلغى أثره دون تنبيه، وإعداد منفذ يتجاهله sshd المُفعّل عبر socket، ووقت تشغيل حاويات ينشر منافذ من تحت جدار الحماية الخاص بك. كل واحد من هذه الثلاثة أوقع شخصًا فعل كل شيء آخر بشكل صحيح.
من الذي يطرق الباب فعلاً
راقب journalctl -u ssh على خادم متصل بالإنترنت منذ ساعة واحدة، وستجد أن الكمية مثيرة للقلق في أول مرة تراها. لكنها لا ينبغي أن تكون كذلك. فما تراه هو الضجيج الخلفي للإنترنت: مجموعة من عمليات المسح، بعضها أكاديمي وبعضها تجاري وبعضها إجرامي، تُعدّد فضاء عناوين IPv4 بأكمله باستمرار وتُسلّم النتائج إلى برامج تخمين بيانات الاعتماد. عنوانك تمّ الوصول إليه لأنه موجود، بترتيب رقمي، وسيُصار إلى الوصول إليه مجددًا خلال ساعات قليلة مهما فعلت.
هذا يُغيّر شكل المشكلة بطريقة مفيدة. أنت لا تُدافع ضد خصم اختارك بعينك وسيتكيّف مع أفعالك؛ بل تُدافع ضد نص برمجي له ذخيرة ثابتة — root وadmin وubuntu وtest وgit وoracle وpostgres، وعشرون ألف كلمة مرور ظهرت في تسريبات البيانات. لا صبر له، ولا إبداع، ولا اهتمام بخادم لا يستجيب من أول محاولة. تعطيل المصادقة بكلمة المرور لا يُبطئ هذا المهاجم. بل يُخرجه من اللعبة كليًا.
هناك تفصيلان يستحقان المعرفة. أولاً، IPv6 أهدأ بشكل كبير، لأن مساحة /64 لا يمكن مسحها بالكامل — لكن بمجرد أن يصبح سجل AAAA الخاص بك عامًا، أو يظهر عنوان خادمك في ترويسة بريد إلكتروني أو في سجل شفافية شهادات (certificate-transparency)، ينتهي هذا الهدوء. لا تعامل IPv6 أبدًا كمخبأ؛ بل عامله كَكومة قش أصغر. ثانيًا، لعنوان IPv4 ماضٍ. كان له مستأجر قبلك، وإذا كان ذلك المستأجر قد شغّل خادم بريد بشكل سيئ أو استضاف شيئًا أُدرج في قوائم الحظر، فإنك ترث تلك السمعة إلى أن تتلاشى. إن كان البريد الإلكتروني يهمك، تحقق من العنوان في قوائم الحظر المعتادة قبل أن تبني عليه — فهذا فحص من خمس دقائق يوفر عليك أسبوعين من تصحيح مشكلات تسليم البريد.
الإعدادان اللذان يؤديان تسعين بالمئة من العمل
يبدأ كل اختراق حقيقي تقريبًا لخادم صغير من أحد موضعين: كلمة مرور كان بالإمكان تخمينها، أو خدمة كانت تستمع على منفذ ولم يكن ينبغي لها ذلك. الإصلاحان المقابلان هما PasswordAuthentication no وجدار حماية بسياسة افتراضية drop. لا بريق فيهما، ويستغرقان معًا خمس عشرة دقيقة، وقيمتهما تفوق كل ضابط آخر في هذه الصفحة مجتمعًا.
السبب بنيوي لا إحصائي. فكلاهما مغلق افتراضيًا — أي أنهما يفشلان في اتجاه الأمان. لا يمكن اختراق sshd بمفاتيح فقط بهجوم القوة الغاشمة مهما كان عدد المحاولات، لأنه لا يوجد مسار في الكود يقبل كلمة مرور أصلاً. وجدار الحماية ذو الرفض الافتراضي يحمي خدمات لم تُثبّتها بعد، بما فيها قاعدة البيانات التي ستضيفها بعد ثلاثة أشهر وتنسى ربطها بـ localhost. أما كل ما تبقى في قائمة تحقق التحصين فهو تعداد لأشياء سيئة: قائمة بأمور محددة يجب إيقافها، وهي لن تكون أكمل من القائمة نفسها.
لذا إن كنت ستقرأ قسمًا واحدًا فقط ثم تُغلق الصفحة، اقرأ هذا القسم، ونفّذ الخطوات من 2 إلى 5 أدناه، واعتبر أن ساعتك أُنفقت في مكانها الصحيح. أما الباقي فهو مفيد فعلاً لكنه ثانوي فعلاً.
أين يقيم إعداد SSH الآن، والفخ الكامن فيه
على Debian 13 وUbuntu 24.04، يبدأ ملف /etc/ssh/sshd_config بسطر Include /etc/ssh/sshd_config.d/*.conf، وتأتي صور الاستضافة السحابية بملف داخل ذلك المجلد — غالبًا 50-cloud-init.conf — يضبط PasswordAuthentication بالفعل. تعديل الملف الرئيسي وإضافة توجيهاتك الخاصة في نهايته يبدو أمرًا طبيعيًا، لكنه ينتج إعدادًا لا يفعل ما يقوله ظاهريًا.
القاعدة التي تُفاجئ الجميع تقريبًا بخصوص sshd هي: لكل كلمة مفتاحية، تفوز أول قيمة يتم الحصول عليها. وهذا عكس تمامًا كل نظام دمج إعدادات آخر استخدمته على الأرجح. سطر Include يقع قرب بداية الملف الرئيسي، لذا يُقرأ أي ملف إضافي قبل متن sshd_config — وبين الملفات الإضافية، الترتيب الأبجدي هو الفيصل. ملف باسم 10-hardening.conf يتغلب على 50-cloud-init.conf. أما ملف باسم 60-hardening.conf فيخسر أمامه، بصمت، ولن تعرف ذلك من عملية إعادة تشغيل تُبلغ عن نجاحها.
ولهذا السبب لا تثق أبدًا بالملف الذي كتبته للتو. اسأل الخدمة (daemon) عمّا استقرت عليه فعليًا:
sshd -t # syntax check — silence means valid
sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractive|allowusers|port) 'يطبع sshd -T الإعداد الفعلي بعد حل كل الملفات المضمّنة والتجاوزات والقيم الافتراضية. إن كان الناتج يقول passwordauthentication yes، فالمصادقة بكلمة المرور مفعّلة، بغض النظر عمّا يدّعيه أي ملف على القرص. لا شيء آخر يُعتبر تحققًا فعليًا.
الفخ الثاني من العائلة نفسها. يبدأ Ubuntu 24.04 تشغيل sshd عبر تفعيل بالمقبس (socket activation): يملك ssh.socket المنفذ الذي يستمع عليه النظام، ويتم تجاهل Port في sshd_config كليًا. تحقق من ذلك بـ systemctl is-enabled ssh.socket؛ فإن كان مفعّلاً وأردت منفذًا مختلفًا، غيّره عبر systemctl edit ssh.socket وتجاوز بـ ListenStream= — وليس في sshd_config، حيث سيبدو الإعداد صحيحًا دون أن يُحدث أي أثر.
نقل SSH عن المنفذ 22: مسرحية، لكنها مسرحية رخيصة
لنكن صادقين بخصوص هذه النقطة، لأن الإنترنت ليس كذلك. نقل sshd إلى المنفذ 2222 أو 47000 لا يُوقف أي مهاجم كفء. فحص TCP كامل لمضيف واحد يستغرق ثوانٍ، والخدمة تُعلن عن نفسها في اللافتة (banner)، وأي شخص قرر مهاجمتك أنت تحديدًا سيجدها قبل أن ينهي فنجان قهوته.
ما يفعله هذا الإجراء فعلاً هو تقليص سجل المصادقة لديك بنحو خمسة وتسعين بالمئة، ولهذا قيمة حقيقية: إنه الفرق بين سجلات تتصفحها بسرعة وسجلات تقرأها فعلاً. إشارة يمكنك رؤيتها أفضل من إشارة دفنتها. إن كنت تراقب أي شيء إطلاقًا، فملف auth.log هادئ هو أرخص وسيلة لجعل أي خلل ظاهرًا.
التكاليف صغيرة لكنها حقيقية، ولهذا نصفه بأنه اختياري لا مُوصى به. ستنسى المنفذ عندما تعود بعد ثمانية أشهر. ومن الناحية النظرية، يمكن لمنفذ غير قياسي أعلى من 1024 أن تستولي عليه عملية بلا صلاحيات إن توقف sshd عن الاستماع لأي سبب. كما أن جدران الحماية الصارمة للاتصالات الصادرة في شبكات بعض العملاء تحظر المنافذ غير المألوفة، لذا ستعجز أحيانًا عن الوصول إلى خادمك من مكتب أو فندق. وعلى نظام مُفعّل بالمقبس (socket activation) يجب تغييره في المكان الصحيح، كما ورد في القسم السابق. افعلها إن كانت السجلات الهادئة تهمك. لكن لا تفعلها ثم تشعر بالأمان، ولا تفعلها أبدًا بديلاً عن الاعتماد على المفاتيح فقط.
الرفض الافتراضي، ومجموعة القواعد التي نشغّلها فعليًا
جدار الحماية الذي يُعدّد ما يجب حظره هو مجرد نظام أرشفة. أما جدار الحماية الذي يُعدّد ما يُسمح به فهو ضابط أمني حقيقي. هذا الفرق هو اللعبة بأكملها، لأن النوع الثاني وحده يغطي الخدمة التي ستُثبّتها الشهر المقبل، ومنفذ التصحيح (debug) الذي فتحته يوم جمعة، والحاوية التي قررت كشف نفسها للعالم.
على منصتنا لديك طبقتان مستقلتان. مرشّح الحافة (edge filter) اختياري، يُضبط لكل خادم من لوحة التحكم ويُطبَّق على مستوى المضيف الفعلي (hypervisor)، بحيث لا تصل الحركة المحظورة إلى خادمك الافتراضي أصلاً — وهو مفيد لقواعد الطبقة الرابعة التي تريد تطبيقها قبل أن تُنفق نواة نظامك دورة معالجة عليها، ولإبقاء خادم افتراضي مُخترَق بعيدًا عن متناول أي أحد أثناء عملك عليه. أما جدار حماية الخادم الافتراضي فهو ملكك بالكامل: nftables أو iptables أو pf أو أي ما تأتي به صورتك. نحن لا نلمسه إطلاقًا. استخدم الطبقتين إن كان الخادم مهمًا؛ واستخدم الثانية على الأقل دائمًا.
هناك أمران في مجموعة القواعد بالخطوة 5 يستحقان الشرح، لأن معظم مجموعات القواعد المنسوخة من مصادر أخرى تُخطئ فيهما. لا تُسقط كل حركة ICMP. يبدو ذلك مرتبًا لكنه يُعطّل اكتشاف path-MTU، ما ينتج عنه أسوأ نوع من الأعطال: الطلبات الصغيرة تعمل، والاستجابات الكبيرة تتجمد، ولا شيء في سجلاتك يُشير إلى جدار الحماية. اقبل على الأقل destination-unreachable وtime-exceeded وparameter-problem. لا تُصفِّ ICMPv6 حسب النوع ما لم تكن تعرف القائمة تمامًا. يعتمد IPv6 على ICMPv6 لاكتشاف الجيران والإعلان عن الموجّهات؛ احظره بشكل واسع وسيموت اتصال IPv6 لديك بطريقة تبدو وكأنها مشكلة توجيه. قبول كل حركة ICMPv6 على خادم واحد مقايضة معقولة، وهذا بالضبط ما تفعله مجموعة القواعد أدناه.
تحذير واحد قبل تشغيل flush ruleset: إن كان Docker مُثبّتًا، فهذا السطر يزيل قواعد NAT والتصفية التي كتبها Docker، وتتوقف شبكة الحاويات إلى أن تُعيدها systemctl restart docker. حمّل مجموعة قواعدك أولاً، ثم أعد تشغيل Docker ثانيًا، واقرأ القسم التالي قبل أن تنشر أي منفذ حاوية واحد.
المنافذ التي لم تكن تعلم أنها مفتوحة
اسأل الخادم عمّا يستمع عليه فعلاً. لا عمّا تظن أنك ثبّته — بل عمّا هو مرتبط بمنفذ الآن:
ss -tulpn | grep -vE '127\.0\.0\.1|\[::1\]'كل ما ينجو من هذا الفلتر يمكن الوصول إليه من مكان آخر غير الخادم نفسه. على صورة قياسية، الاكتشافات المعتادة هي rpcbind على المنفذ 111 (لا شيء تُشغّله يحتاجه)، ومستمع exim4 متبقٍّ من التثبيت الأساسي، وكعب systemd-resolved غير ضار طالما بقي على loopback لكنه يتحول إلى مُحلِّل مفتوح (open resolver) إن لم يكن كذلك. أما الخطيرة فتصل لاحقًا، مع برمجيات ثبّتها أنت عن قصد: PostgreSQL على 0.0.0.0:5432 لأن دليلاً تعليميًا قال بتعديل listen_addresses، وRedis بلا كلمة مرور لأن Redis لم يكن له كلمة مرور افتراضية طوال معظم تاريخه، وعقدة Elasticsearch، ومُصدِّر Prometheus، ودفتر Jupyter. كل واحدة من هذه كانت الخطوة الأولى في اختراق حقيقي مرات عديدة.
وهنا الفخ الذي يقع فيه الحذرون. لا يطلب Docker الإذن من جدار حمايتك. فعندما تكتب -p 5432:5432، يُدرج Docker قواعد DNAT في سلسلة PREROUTING من جدول nat، والتي تُقيّمها النواة قبل أن تصل الحزمة إلى سلسلة input لديك أصلاً؛ ثم تُحوَّل الحركة إلى الحاوية. سياسة الرفض الافتراضية لسلسلة input لا تُستشار إطلاقًا، ويُظهر ufw status أن المنفذ مغلق، بينما تكون قاعدة البيانات على الإنترنت العام. هذا سلوك موثّق لـ Docker، وهو على هذا الحال منذ عقد كامل، وقد كشف عددًا هائلاً من قواعد البيانات.
الإصلاح سطر واحد، وهو العادة التي تستحق أن تُبنى:
# wrong — reachable from the entire internet, whatever your firewall says
docker run -p 5432:5432 postgres
# right — bound to loopback, reachable only through an SSH tunnel or a proxy
docker run -p 127.0.0.1:5432:5432 postgresوأخيرًا، احصل على رأي خارجي. فحص المنافذ من داخل الخادم يُخبرك بما تظنه النواة؛ أما الفحص من مكان آخر فيُخبرك بما يراه العالم، وهو الرقم الوحيد المهم. شغّل nmap -Pn -p- <your-ip> من حاسوبك المحمول، لا من الخادم نفسه، وقارن النتيجة بالقائمة التي كنت تتوقعها.
التحديثات التلقائية: فعّلها، وكن جادًا في ذلك
النافذة الزمنية التي تُعرّض الخوادم الصغيرة للاختراق ليست ثغرة اليوم صفر. بل هي الأسابيع الأربعة بين إعلان ثغرة CVE للعموم مع إثبات مفهوم عملي، وبين المرة التالية التي تُصادف فيها تسجيل الدخول. أدوات الهجوم تستوعب أي خلل عن بُعد جديد خلال أيام؛ والخادم الذي تُصلحه «عندما تجد وقتًا لذلك» يبقى معرّضًا طوال تلك الفترة بأكملها، وكل مُشغّل صادق يعرف كم تطول تلك الفترة فعلاً.
الاعتراض على التحديثات التلقائية هو الخوف من كسر شيء ما، وهذا اعتراض يستحق أن يُؤخذ بجدية — ثم أن يُحصر في نطاقه. اقصر الأتمتة على حزمة الأمان (security) من توزيعتك، حيث يقوم القائمون على الصيانة بترحيل الإصلاحات إلى النسخة المُعبّأة نفسها بدلاً من إصدار إصدارات جديدة من المنبع. تحديث أمني على Debian لحزمة openssl هو بناء مُصحح لنفس النسخة التي تُشغّلها بالفعل؛ خطر أن يُغيّر سلوكها أصغر بكثير من خطر ترك ثغرة عن بُعد مفتوحة لشهر كامل. أما ترقيات الميزات فتبقى يدوية، حيث ينبغي أن تكون.
الجزء الذي يتجاهله الناس هو إعادة التشغيل. مكتبة libssl مُصححة على القرص لا تفيد بشيء العملية التي حمّلت النسخة القديمة عند الإقلاع، وتحديث النواة (kernel) لا يفعل شيئًا إطلاقًا حتى تُقلع بها. إما أن تقبل بإعادة تشغيل غير مراقَبة في نافذة زمنية تختارها أنت، أو ثبّت needrestart ودعه يُخبرك بالخدمات التي لا تزال تعمل بمكتبات محذوفة — لكن افعل أحد الأمرين. عبارة «التحديثات التلقائية مفعّلة» بينما يقرأ uptime 340 يومًا هي وهم مريح، وهو الوهم الأكثر شيوعًا الذي نراه.
fail2ban أو CrowdSec أو لا شيء على الإطلاق
يقرأ fail2ban سجلاتك، ويلاحظ الإخفاقات المتكررة من عنوان معيّن، ويحظره لفترة. يفعل CrowdSec الشيء نفسه، لكنه يُشارك الأحكام عبر شبكة من المشاركين، بحيث يمكنك حظر عنوان أساء التصرف في مكان آخر قبل أن يصل إليك. كلاهما برنامج جيد. لكن لا واحد منهما يفعل ما يظنه معظم الناس على خادم SSH يعتمد على المفاتيح فقط.
بمجرد أن يكون PasswordAuthentication مضبوطًا على no، لا يمكن لهجوم القوة الغاشمة على SSH أن ينجح. ليس «من غير المرجح أن ينجح» — بل لا يوجد مسار كود يسمح بذلك أصلاً. لذا فإن حظر عنوان بعد خمسة إخفاقات لا يمنع شيئًا؛ بل يُقلّل حجم السجلات وقدرًا ضئيلاً من استهلاك المعالج. هذه فائدة حقيقية، لكنها ليست فائدة أمنية، والمقايضة ليست مجانية: فقفص (jail) مكتوب بشكل سيئ يراقب السجل الخطأ قد أقفل الباب في وجه مدراء أنظمة أكثر مما أقفله في وجه مهاجمين.
المكان الذي تستحق فيه هذه الأدوات مكانتها فعلاً هو طبقة أعلى، على الخدمات التي تكشفها فعلاً: نموذج تسجيل دخول، لوحة إدارة ووردبريس، واجهة برمجية (API) بحدود معدل، خادم بريد بمصادقة SMTP AUTH. تلك الخدمات تقبل كلمات مرور فعلاً، ويمكن فعلاً تعريضها لهجمات القوة الغاشمة، وقائمة الحظر هي بالضبط الضابط المناسب لها. لذا حكمنا ضيق ومحدد: تجاوز قفص SSH. ضع CrowdSec أو fail2ban أمام الشيء الذي يملك حقل كلمة مرور. وأيًا كان اختيارك، ضع عنوان إدارتك الخاص في القائمة البيضاء أولاً — فخيار ignoreip موجود من أجل تلك الأمسية التي ستتذكرها لسبب خاطئ لو لم تفعل.
اجعل الخادم يُخبرك عندما يتغيّر
الوقاية هي ما يمكنك فعله خلال ساعة. أما الاكتشاف فهو ما يُخبرك أن تلك الساعة لم تكن كافية. لا حاجة لأن يكون معقدًا، وعلى خادم واحد، تُغطي ثلاثة أمور رخيصة معظم الأرضية.
احتفظ بالسجلات. على كثير من الصور، يعيش السجل (journal) في /run ويتبخر عند إعادة التشغيل، ما يعني أن سجل أي حادثة يختفي في إعادة التشغيل التي تليها. إنشاء /var/log/journal إصلاح من سطر واحد وأعلى الأمور قيمة في هذا القسم.
اجعل النظام يُخبرك بعمليات تسجيل الدخول. سطر واحد في /etc/ssh/sshrc يُشغّل logger عند بدء كل جلسة لا يُكلّف شيئًا ويمنحك سجلاً نظيفًا يمكن البحث فيه (greppable) منفصلاً عن ثرثرة sshd نفسه. تحذير واحد يُوقع الناس في الخطأ: يُشغّل sshd الملف ~/.ssh/rc بدلاً من /etc/ssh/sshrc إن كان لدى المستخدم واحد، لذا يُتجاوز الملف العام للنظام تحديدًا في حساب هو الأكثر احتمالاً لامتلاك ملف dotfile مخصص. إن احتجت خطافًا (hook) لا يمكن حجبه، استخدم pam_exec بدلاً من ذلك.
اعرف كيف كان نظام الملفات في اليوم الأول. يُسجّل AIDE بصمات (hashes) ملفاتك الثنائية ومكتباتك وملفات الوحدات (unit files) ويُبلغ عمّا تغيّر منذ ذلك الحين. المشكلة الجوهرية، والمُتجاهَلة عادة، هي أن قاعدة بيانات مُخزّنة على الخادم نفسه الذي تراقبه يمكن لمن اخترقه أن يُعيد توليدها، وعندها يُبلغ الفحص «لا تغييرات» إلى الأبد. انسخ قاعدة البيانات خارج الخادم، أو سجّل بصمتها في مكان آخر على الأقل، عندها تستحق الأداة العشرين دقيقة التي تأخذها. أما إن تُركت في مكانها فهي مجرد شعور زائف بالطمأنينة.
يستحق المبدأ العام أن يُذكر بمفرده: السجل الذي لا يمكنك الوثوق به هو ذلك الموجود على الخادم المُخترَق. أي شيء تنوي الاعتماد عليه فعلاً — سجل، قاعدة بيانات تكامل، نسخة احتياطية — ينبغي أن تكون له نسخة في مكان لا يتحكم فيه المهاجم نفسه. خادم صغير ثانٍ في ولاية قضائية أخرى إجابة مشروعة على ذلك، ويكفي خادم بخمسة دولارات فقط.
ما لا يفعله هذا الإجراء
كل ما سبق هو عمل على المحيط الخارجي فقط، ويستحق الأمر أن نكون دقيقين بشأن هذا الحد حتى لا تخلط بين باب أمامي مُقفل وخزنة حقيقية.
لا يحمي البيانات على خادم قيد التشغيل. النواة المُحصّنة وجدار الحماية المُغلق لا قيمة لهما أمام أي شخص يملك وصولاً إلى المضيف الفعلي (hypervisor)، ولا أمام محتويات الذاكرة العشوائية أثناء عمل الخادم. إن كان قلقك هو القرص عندما يكون الخادم مُطفأً أو مُصادَرًا أو خارج الخدمة، فذلك هو تشفير القرص، وهو إجراء مختلف بنمط فشل مختلف — راجع تشفير قرص VPS باستخدام LUKS.
لا يجعلك مجهول الهوية. يمكن لخادم مُحصّن بشكل مثالي أن يُعلن عن مالكه رغم ذلك، عبر سجل WHOIS، أو مفتاح SSH مُعاد استخدامه، أو وسم تحليلات، أو عميل SSH يتصل من المنزل دون قفزة وسيطة، أو شهادة تربط بين هويتين. الدفع بعملة Monero ثم تسجيل الدخول من عنوانك الشخصي يُبطل أثر ذلك الدفع بالكامل. نمط الفشل هذا له صفحته الخاصة: الأخطاء التي تكشف هويتك.
لا يُصلح تطبيقك. حقن SQL، أو نقطة نهاية إدارية بلا مصادقة، أو اعتمادية تحوي بابًا خلفيًا، لا يهتم أي منها بإعدادات sshd لديك. معظم اختراقات الخوادم المُدارة جيدًا تصل عبر المنفذ الذي فتحته أنت عمدًا، بقصد، لخدمة كتبتها أو ثبّتها بنفسك.
وهذا ليس نسخة احتياطية. برامج الفدية، وأمر rm -rf بمتغير كان فارغًا، وترقية فاشلة، كلها تنتهي بالطريقة نفسها. خذ نسخة، وضعها في مكان آخر، واستعد منها مرة واحدة قبل أن تحتاجها فعلاً — الانضباط نفسه الذي يجعل الانتقال إلى مزوّد استضافة آخر قابلاً للنجاة يجعل يوم ثلاثاء سيئًا قابلاً للنجاة أيضًا.
لا شيء من هذا حجة ضد تلك الساعة. إنه حجة لصالح أن تعرف بالضبط ما الذي اشترته تلك الساعة.
- انشر الخادم بمفتاح، لا بكلمة مرور أبدًا
ولّد زوج المفاتيح على جهازك الخاص، والصق النصف العام في نموذج النشر؛ ستُثبّته الصورة تلقائيًا ولن تملك أبدًا كلمة مرور root لتخسرها. إن كنت تنشر من صورة مُعلَّمة بـ
cloud-init، يمكنك بدلاً من ذلك تمرير إعداد الساعة الأولى بأكمله كبيانات مستخدم (user-data) — حتى 64 كيلوبايت — ويظهر الخادم مُحصّنًا مسبقًا من لحظة إقلاعه.# on your laptop, not on the server ssh-keygen -t ed25519 -a 100 -C "$(whoami)@laptop-cvh" cat ~/.ssh/id_ed25519.pub # paste this into the deploy formاستخدم ed25519 ما لم يرفضه شيء في سلسلة أدواتك، وفي تلك الحالة فإن RSA بـ 4096 بت خيار جيد. احمِ المفتاح الخاص بعبارة مرور وحمّله في عميل (agent)؛ فملف مفتاح بلا عبارة مرور على حاسوب محمول هو بمثابة كلمة مرور مكتوبة على الشاشة. مجموعة المفاتيح نفسها تُحقن في وضع الإنقاذ (rescue mode)، وهذا ما يجعل الخطوة 4 قابلة للاستعادة.
- افتح الجلسة الثانية قبل أن تلمس أي شيء
هذا ليس أمرًا اختياريًا وليس وسواسًا مبالغًا فيه. فمن الآن فصاعدًا ستُغيّر الخدمة (daemon) التي تتصل عبرها وجدار الحماية الذي يسمح لك بالوصول إليها. أبقِ جلسة واحدة مفتوحة وخاملة كحبل نجاة يعيدك إلى الخادم، ونفّذ كل تغيير في جلسة أخرى. إن كان تغيير ما خاطئًا، تبقى الجلسة المفتوحة مُصادَقًا عليها ويمكنها التراجع عنه؛ بينما سيُرفض أي اتصال جديد.
# terminal A — the rope. Log in and leave it alone. ssh -i ~/.ssh/id_ed25519 root@<server-ip> # terminal B — where every command below runs. ssh -i ~/.ssh/id_ed25519 root@<server-ip>اختبر كل تغيير بفتح اتصال ثالث، ولا تفعل ذلك أبدًا بإعادة الاتصال بالجلسة التي تعمل فيها. فإن فشل الاتصال الثالث، يبقى لديك صدفتان (shells) عاملتان ومشكلة بدلاً من أزمة كاملة.
- أنشئ الحساب الذي ستستخدمه فعليًا
الدخول بحساب root عبر SSH مريح لمدة ساعة واحدة بالضبط. بعد ذلك، يمنحك حساب مُسمّى مع صلاحيات sudo سجلَّ تدقيق، ويحميك من أمر مكتوب بالخطأ يُنفَّذ بصلاحيات كاملة، ويتيح لك تعطيل حساب مُخترَق دون تعطيل الخادم بأكمله.
adduser --disabled-password --gecos "" deploy install -d -m 0700 -o deploy -g deploy /home/deploy/.ssh cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys chown deploy:deploy /home/deploy/.ssh/authorized_keys chmod 0600 /home/deploy/.ssh/authorized_keys usermod -aG sudo deploy # a long random password, stored in your manager, used only by sudo passwd deployاضبط تلك كلمة المرور. فحساب sudo لم تُضبط له كلمة مرور قط لا يمكنه استخدام sudo، واكتشاف هذا بعد أن تكون قد عطّلت تسجيل دخول root هو الطريقة الكلاسيكية لإقفال الباب في وجه نفسك رغم أن كل ملف صحيح. تحقق قبل المتابعة: من طرفية جديدة، نفّذ
ssh deploy@<server-ip>، ثمsudo -v. يجب أن يعمل كلاهما. - اكتب ملف SSH الإضافي، ثم اسأل الخدمة عمّا قرأته
ملف إضافي مُرقّم يُرتَّب مبكرًا يتغلب على أي ملف جاءت به صورة الاستضافة السحابية، وفقًا لقاعدة الترتيب أعلاه. اكتبه، وتحقق من صحة صيغته، واقرأ الإعداد الفعلي الناتج، ثم أعد التحميل بعد ذلك فقط.
cat > /etc/ssh/sshd_config.d/10-hardening.conf <<'EOF' PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes AuthenticationMethods publickey PermitEmptyPasswords no AllowUsers deploy MaxAuthTries 3 LoginGraceTime 20 X11Forwarding no AllowAgentForwarding no AllowTcpForwarding no # remove this line if you use ssh -L / -D tunnels ClientAliveInterval 300 ClientAliveCountMax 2 EOF chmod 0644 /etc/ssh/sshd_config.d/10-hardening.conf sshd -t # silence means the syntax is valid sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication|allowusers) ' systemctl reload sshاقرأ ناتج
sshd -Tذاك قبل إعادة التحميل، لا بعده. يجب أن يقولpermitrootlogin noوpasswordauthentication no. إن لم يفعل، فملفك يُتجاوَز من قِبل ملف يُرتَّب أبكر منه — نفّذls /etc/ssh/sshd_config.d/وأعد تسمية ملفك ليأتي لاحقًا. الآن افتح طرفية ثالثة وسجّل الدخول باسمdeploy. لا تُغلق حبل النجاة إلا عندما ينجح ذلك. - حمّل جدار حماية برفض افتراضي
يأتي nftables مُثبّتًا مسبقًا مع كلا التوزيعتين، ويستبدل مجموعة قواعد iptables بملف واحد يمكن قراءته. اضبط المنافذ المقبولة وفقًا للخدمات التي تُشغّلها فعلاً — تفترض القائمة أدناه وجود SSH وخادم ويب فقط، ولا شيء غيرهما.
apt install -y nftables cat > /etc/nftables.conf <<'EOF' #!/usr/sbin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority filter; policy drop; ct state established,related accept ct state invalid drop iif lo accept # ICMP must live: dropping it blackholes path-MTU discovery. ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded, parameter-problem } accept ip6 nexthdr icmpv6 accept tcp dport 22 ct state new accept tcp dport { 80, 443 } ct state new accept limit rate 5/minute burst 5 packets log prefix "nft-drop: " level info } chain forward { type filter hook forward priority filter; policy drop; } chain output { type filter hook output priority filter; policy accept; } } EOF nft -c -f /etc/nftables.conf # check before you commit to it systemctl enable --now nftables nft list ruleset | head -40إن كان Docker مُثبّتًا، فإن
flush rulesetيُزيل أيضًا قواعد Docker: نفّذsystemctl restart dockerبعد ذلك وتأكد من أن حاوياتك لا تزال تستجيب. ثم، من حاسوبك المحمول، نفّذnmap -Pn -p- <server-ip>وتحقق من أن قائمة المنافذ المفتوحة تُطابق المنافذ التي سمحت بها للتو — لا أكثر ولا أقل. - فعّل التحديثات الأمنية غير المراقَبة
اقتصر على حزمة الأمان فقط، مع نافذة إعادة تشغيل تختارها أنت بدلاً من نافذة ستستمر في تأجيلها. اختر ساعة هادئة لمستخدميك، وليس عند بداية الساعة بالضبط، حتى لا تتصادم مع مهام cron الخاصة بالجميع.
apt install -y unattended-upgrades needrestart cat > /etc/apt/apt.conf.d/51-cvh-auto <<'EOF' // Debian. On Ubuntu, replace the two Origins-Pattern lines with: // "${distro_id}:${distro_codename}-security"; Unattended-Upgrade::Origins-Pattern { "origin=Debian,codename=${distro_codename}-security,label=Debian-Security"; "origin=Debian,codename=${distro_codename},label=Debian-Security"; }; Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-WithUsers "false"; Unattended-Upgrade::Automatic-Reboot-Time "04:17"; Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1"; EOF unattended-upgrade --dry-run --debug | tail -25 systemctl status unattended-upgrades --no-pagerإن كانت إعادة التشغيل غير المراقَبة غير مقبولة فعلاً في حالتك، اضبطها على
"false"ودعneedrestart -bيُبلغك بالخدمات التي لا تزال تعمل بمكتبات محذوفة وما إذا كانت هناك نواة أحدث مُثبَّتة — ثم تصرّف بناءً على ذلك. غير المقبول هو ألا تفعل أيًا من الأمرين. - أغلق ما هو مفتوح للاستماع، وتحقق من الخارج
عدّد كل ما هو مرتبط بشيء غير loopback، وأزل ما لا تستخدمه، وتأكد من النتيجة من خادم آخر غير هذا الخادم.
ss -tulpn | grep -vE '127\.0\.0\.1|\[::1\]' # the usual leftovers on a base image systemctl disable --now rpcbind.socket rpcbind 2>/dev/null apt purge -y exim4-base exim4-config exim4-daemon-light 2>/dev/null apt autoremove --purge -y # what each running unit is allowed to reach systemd-analyze security --no-pager | head -20بالنسبة لأي شيء يجب أن يبقى يعمل لكن لا يحتاج إلى جمهور — قاعدة بيانات، ذاكرة تخزين مؤقت (cache)، لوحة إدارة، مُصدِّر مقاييس — اربطه بـ
127.0.0.1في إعداده الخاص وتواصل معه عبر نفق SSH:ssh -L 5432:127.0.0.1:5432 deploy@<server-ip>. هذا أفضل بشكل قاطع من فتح منفذ والأمل في أن تكون مصادقة الخدمة نفسها سليمة، وهو النمط الذي ينبغي اللجوء إليه افتراضيًا. - سجّل خط الأساس وفعّل أجهزة الإنذار
عشر دقائق لا تُثمر إلا في اليوم الذي يحدث فيه خطأ ما — وهو اليوم الذي لن تستطيع فيه إعادة بناء أي من ذلك من الذاكرة.
# persistent journal, so a reboot stops erasing the evidence mkdir -p /var/log/journal systemd-tmpfiles --create --prefix /var/log/journal systemctl restart systemd-journald # a clean, greppable line per SSH session cat > /etc/ssh/sshrc <<'EOF' logger -t ssh-login "user=$USER from=${SSH_CONNECTION%% *} tty=${SSH_TTY:-none}" EOF # file-integrity baseline — then get the database off the machine apt install -y aide aideinit mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db sha256sum /var/lib/aide/aide.db # copy this hash somewhere else last -n 20 ; lastb -n 20 # who got in, and who triedاختم الأمر بتدوين أربعة أشياء، بعيدًا عن الخادم: بصمة مفتاح مضيف SSH من
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub، وبصمة AIDE أعلاه، والمنافذ التي فتحتها عمدًا، ومكان وجود المفتاح الخاص. تلك الملاحظة هي ما يُحوّل صباحًا سيئًا إلى قائمة تحقق بدلاً من تحقيق جنائي.
الضوابط، مُرتّبة بحسب ما تمنحك إياه فعليًا
| الضابط | ما يمنعه | ما لا يمنعه | التكلفة | الحكم |
|---|---|---|---|---|
| SSH بمفاتيح فقط (PasswordAuthentication no) | كل برنامج تخمين بيانات اعتماد على الإنترنت، بشكل دائم وبحكم البنية | أي شخص يملك مفتاحك الخاص، أو ثغرة في sshd نفسه | 5 دقائق، مرة واحدة | غير قابل للتفاوض |
| جدار حماية برفض افتراضي (nftables) | الخدمات التي نسيت أنها تستمع، وكل خدمة تُثبّتها لاحقًا عن طريق الخطأ | الهجمات ضد المنافذ التي فتحتها عمدًا | 10 دقائق، مرة واحدة | غير قابل للتفاوض |
| التحديثات الأمنية غير المراقَبة | نافذة الأيام-n — الأسابيع بين ظهور ثغرة علنية ودخولك التالي | ثغرات اليوم صفر، وأي شيء يحتاج إعادة تشغيل لم تُجدولها أبدًا | 5 دقائق زائد نافذة إعادة تشغيل | غير قابل للتفاوض |
| مستخدم مُسمّى مع sudo بدلاً من root | الأوامر المكتوبة بالخطأ بصلاحيات كاملة، والعمليات التي تعمل كـ root دون سبب | تصعيد صلاحيات محلي بيد شخص موجود بالفعل داخل الخادم | 3 دقائق | يستحق العناء |
| خط أساس لتكامل الملفات (AIDE) | التعديلات الصامتة على الملفات الثنائية للنظام ومكتباته وملفات الوحدات | لا شيء إطلاقًا، إن بقيت قاعدة البيانات على الخادم نفسه الذي تراقبه | 20 دقيقة زائد تخزين خارج الخادم | يستحق العناء إن كان الخادم مهمًا |
| نقل SSH عن المنفذ 22 | نحو 95% من حجم سجل المصادقة لديك | أي شخص يُشغّل فحص منافذ، أي كل من يهم أمره | دقيقتان، زائد الأمسية التي ستنسى فيها المنفذ | اختياري، من أجل سجلات هادئة |
| fail2ban / CrowdSec على sshd | المخالفين المتكررين، وما يُهدرونه من معالج على حسابك | لا شيء، بمجرد تعطيل المصادقة بكلمة المرور | 10 دقائق، زائد خطر إقفال حقيقي على نفسك | تجاوزه على SSH؛ استخدمه على تطبيقك |
أسئلة تستحق الإجابة
بعد كم من الوقت من النشر يبدأ الفحص؟
خلال دقائق عادةً. فبرامج المسح المنتشرة عبر الإنترنت تفحص فضاء عناوين IPv4 بأكمله باستمرار، لذا يصل الفحص إلى العنوان الجديد في الجولة التالية بصرف النظر عمّا يعمل عليه. افترض أنك تحت الفحص منذ اللحظة التي يستجيب فيها الخادم لأول حزمة بيانات — ولهذا يحدث التحصين في الساعة الأولى، لا في نهاية الأسبوع الأولى.
هل ما زلت بحاجة إلى fail2ban إن كان تسجيل الدخول بكلمة المرور معطّلاً؟
على SSH، لا. فمع PasswordAuthentication no لا يوجد مسار كود يمكن لهجوم القوة الغاشمة أن يفوز به، لذا فإن الحظر بعد خمسة إخفاقات لا يمنع شيئًا — بل يُقلّل ضجيج السجلات، وهذا أمر له قيمة لكنه ليس أمانًا. أما المكان الذي تستحق فيه هذه الأدوات مكانتها فهو أمام أي شيء يقبل كلمة مرور فعلاً: نموذج تسجيل دخول على الويب، لوحة إدارة، مصادقة SMTP AUTH. إن ثبّت واحدة منها، ضع عنوانك الخاص في القائمة البيضاء أولاً.
ufw أم firewalld أم nftables؟
أي منها، طالما كانت السياسة الافتراضية drop وتفهم ما نشرته فعلاً. ufw هو الأكثر سهولة ويكتب قواعد nftables تحت الغطاء على إصدارات Debian وUbuntu الحالية. أما nftables الخام فهو ملف واحد يمكن قراءته ومقارنته وإدارته بنظام تحكم إصدارات، ولهذا نستخدمه نحن. الاختيار بين الثلاثة أقل أهمية بكثير من السياسة الافتراضية نفسها — ولا شيء من الثلاثة يُغيّر حقيقة أن Docker ينشر المنافذ من تحتها جميعًا.
هل يجب أن أنقل SSH عن المنفذ 22؟
فقط من أجل سجلات أهدأ. فهو لا يُوقف أي مهاجم كفء — الفحص الكامل يستغرق ثوانٍ واللافتة (banner) تُعرّف الخدمة. لكنه يُزيل فعلاً معظم الضجيج من سجل المصادقة لديك، ما يجعل الحالات الشاذة الحقيقية ظاهرة. تعامل معه كنظافة سجلات، لا كضابط أمني أبدًا، وتحقق مما إذا كان sshd لديك مُفعّلاً بالمقبس (socket-activated) قبل تغييره: على Ubuntu 24.04 يُتجاهل توجيه Port في sshd_config وينتمي المنفذ إلى ssh.socket.
هل ستُعطّل التحديثات التلقائية موقعي في الرابعة فجرًا؟
إن اقتصرت على حزمة الأمان، فنادرًا جدًا. تلك الحزم هي إصلاحات مُرحّلة إلى النسخة التي تُشغّلها بالفعل، لا إصدارات جديدة من المنبع. الخطر الواقعي هو إعادة التشغيل، لا الترقيع نفسه — لذا اختر النافذة الزمنية بنفسك، واضبط Automatic-Reboot-WithUsers على false حتى ينتظر بينما يكون أحدهم مسجّلاً دخوله، وإن كانت الخدمة لا تستطيع فعلاً إعادة التشغيل دون مراقبة، شغّل needrestart -b على جدول زمني وتصرّف بناءً على ما يُبلغه. الموقف الوحيد الذي لا يمكن الدفاع عنه هو تحديثات تلقائية مع وقت تشغيل (uptime) يُقاس بالسنوات.
أقفلت الباب في وجه نفسي. ما خياراتي؟
أقلع الخادم في وضع الإنقاذ (rescue mode) من لوحة التحكم. يُشغّل هذا الوضع بيئة Alpine في الذاكرة العشوائية بنفس مفاتيح SSH الخاصة بحسابك، مع قرصك غير مُركَّب عند /dev/vda، بحيث يمكنك تركيبه، وإصلاح ملف SSH الإضافي أو ملف جدار الحماية، ثم فك تركيبه وإعادة التشغيل. لا يمس إقلاع الإنقاذ نفسه أي شيء على القرص. بصمة مفتاح مضيف الإنقاذ تختلف عن بصمتك المعتادة — وهذا متوقع، وليس اعتراضًا للاتصال.
هل تشغيل Lynis أو نص برمجي لتحصين CIS بدلاً من هذا الدليل فكرة جيدة؟
كأداة تدقيق، نعم؛ كبديل، لا. Lynis رأي ثانٍ مفيد فعلاً وسيجد أمورًا لا تذكرها هذه الصفحة. أما نصوص إصلاح CIS الآلية فهي أمر مختلف تمامًا: فهي تُطبّق مئات التغييرات المصممة لأسطول من محطات عمل الشركات، وعدد منها يُعطّل الخادم بطرق يصعب تتبعها بعد أسابيع. نفّذ الساعة يدويًا أولاً، حتى تفهم ما يفعله خادمك، ثم شغّل أداة تدقيق واقرأ نتائجها واحدة تلو الأخرى.
هل يجعل التحصين خادمي مجهول الهوية؟
لا، والخلط بين الأمرين خطأ شائع ومكلف. التحصين يتحكم في من يستطيع الدخول؛ أما إخفاء الهوية فيتحكم في من يستطيع معرفة أن الخادم مِلكك. خادم مُقفل بإحكام تام قد يُسرّب مع ذلك ملكيته عبر سجل WHOIS، أو مفتاح SSH مُعاد استخدامه، أو وسم تحليلات، أو تسجيل دخول إداري من عنوانك المنزلي. الدفع بعملة Monero ثم الاتصال مباشرة من اتصالك الشخصي يُبطل أثر ذلك الدفع بالكامل — وهذا مشروح في الأخطاء التي تكشف هويتك.
Keep exploring
تشفير قرص VPS باستخدام LUKS
النصف الآخر من المهمة: هذه الصفحة تُغلق الشبكة، وتلك تحمي القرص عندما يكون الخادم مُطفأً.
أخطاء إخفاء الهوية التي تكشفك
خادم مُحصّن بشكل مثالي لكنه لا يزال يُخبر الجميع بمالكه. الإخفاقات هنا سلوكية لا تقنية.
إعداد VPS بخدمة WireGuard
أول خدمة تستحق أن تُوضع خلف جدار الحماية الذي بنيته للتو — وباب خاص إلى كل ما ربطته بـ localhost.
Deploy your offshore server.
اختر منطقة. اختر خطة. الصق مفتاحاً. ادفع. الـ 47 ثانية القادمة على حسابنا.