السجلّ الذي يصفك أفضل من سجلّ تصفّحك
قبل أن يستطيع جهازك إرسال بايتة مشفّرة واحدة إلى موقع، عليه أن يسأل أحدًا أين يقع ذلك الموقع. هذا السؤال يسافر في العلن، إلى مُحلِّل لم تخترْه أنت على الأرجح، والإجابة عن مَن يحتفظ به تقرّر كم من حياتك مكتوب في مكان ما. سجلّ التصفّح قائمة بالصفحات التي اخترتَ أنت الاحتفاظ بها. أما سجلّ المُحلِّل فهو كل شيء: كل موقع، وكل تطبيق يتحقق من التحديثات، وكل خدمة سحابية يتحدث هاتفك معها بينما تنام، وكل نطاق في كل رسالة بريد فتحتها، بالترتيب، مع طوابع زمنية، سواء كان إنسان ينظر إلى الشاشة أم لا.
اقرأ أسبوعًا واحدًا منه وستستطيع إعادة تركيب شخص كامل: المصرف الذي يتعامل معه، وشركة الطيران التي حجز معها للتو، والصيدلية، وتطبيق المواعدة، ونظام تتبّع المتقدّمين لدى مسؤول التوظيف، والساعة التي يصحو فيها والساعة التي يتوقف فيها. لا شيء من ذلك يتطلّب كسر أي تشفير. الأسماء وحدها تحمله، والأسماء هي الجزء الوحيد من المعاملة الذي لا يزال، في معظم الإعدادات، يُسلَّم إلى طرف ثالث بحكم التصميم.
اليوم، ذلك الطرف الثالث هو أيًا كان مَن وجّهك إليه عقد DHCP — مزوّد خدمة الإنترنت، أو رب عملك، أو المقهى. الأشخاص الذين يلاحظون هذا يتحوّلون عادةً إلى مُحلِّل عام، وهو تحسّن حقيقي في السلامة وتحرّك جانبي في الخصوصية: أنت لم تُزل المُراقِب، بل غيّرت هوية الشركة التي تؤدي هذا الدور، واستبدلتَ شركة اتصالات مُنظَّمة بشبكة توصيل محتوى قريبة من الإعلانات أو بمنظمة غير ربحية قد تتغيّر سياستها بتغيّر تمويلها. الجيدون منهم ينشرون سياسات احتفاظ صادقة، وبعضهم يلتزم بها فعلاً. لكن السياسة وعدٌ بشأن السلوك، وملف الإعدادات تصريحٌ بشأن القدرة. لا يمكنك تدقيق وعد. يمكنك تدقيق عشرين سطرًا كتبتَها أنت بنفسك، على جهاز تستأجره أنت، في بلد اخترته أنت.
SP·02التحويل ينقل السجلّ. العودية تُفتِّته.
تنتهي كل مقالة تقريبًا تقول "شغّل خادم DNS خاصًا بك" ببناء مُحوِّل: ذاكرة تخزين مؤقت صغيرة على شبكتك تُمرِّر كل ما لا تعرفه إلى 1.1.1.1 أو 9.9.9.9. وهذا أمر مفيد فعلاً — سريع، وخمسة أسطر فقط، ويمنع شبكتك المحلية من مراقبتك. لكنه يترك السجلّ المُجمَّع في مكانه تمامًا. كل اسم تبحث عنه لا يزال يصل إلى شركة واحدة، ومُعلَّم الآن بعنوان IP الوحيد لمُحلِّلك، ما يجعل تتبّع مصدر التدفّق أسهل لا أصعب.
المُحلِّل العودي يقوم بالعمل بنفسه عوضًا عن تفويضه لغيره. عند سؤاله عن news.example.io، يستعلم من خادم جذر عن الجهة التي تُشغّل .io، ثم يستعلم من خوادم .io عن الجهة التي تُشغّل example.io، ثم يسأل ذلك المُشغِّل مباشرةً. ثلاث محادثات مع ثلاثة أطراف غير مرتبطة، لا يعمل أي منها في مجال التجميع، و — وهذا هو الجزء المهم — لا يرى أي منها مطلقًا تدفّق استعلاماتك كاملاً. مُشغِّلو الجذر يرون رشحًا خفيفًا من طلبات TLD. أما Verisign فيرى أن أحدًا في عنوانك لمس شيئًا تحت .com. والخادم المرجعي لأي نطاق يرى حركة المرور التي كان سيراها دائمًا، لأنك على وشك الاتصال به في كل الأحوال.
تُشدِّد ميزة QNAME minimisation هذا الأمر إلى حدّ بعيد، وUnbound الحديث يفعل ذلك افتراضيًا. فبدلاً من إرسال الاسم كاملاً إلى كل خادم في السلسلة — وهذا ما فعلته المُحلِّلات لثلاثين عامًا — يرسل إلى كل خادم فقط التسمية التي يحتاجها للإجابة: io. إلى الجذر، وexample.io. إلى خوادم .io، وبعد ذلك فقط الاسم الكامل news.example.io. إلى المُشغِّل المخوَّل به. تتوقف السلسلة الهرمية عن كونها بثًّا لنواياك وتصبح ما رُسمت لتكون عليه أصلاً: تفويضًا.
والمقايضة حقيقية وتستحق أن تُذكر بصراحة. عملية بحث عودية بارِدة تستغرق عدة رحلات ذهاب وعودة بينما يكفي المُحوِّل رحلة واحدة، فالزيارة الأولى لنطاق غير مألوف أبطأ بشكل ملموس. أنت ترث مسؤولية ذاكرة تخزين مؤقت كانت في الأصل مشكلة شخص آخر. ولن تستفيد بعد الآن من ذاكرة تخزين مؤقت مشتركة سخّنها ملايين الأشخاص الآخرين. في المقابل، السجلّ الكامل، المرتَّب، المُوقَّت، لكل ما بحثتَ عنه، يتوقف عن الوجود في أي مكان خارج قرصك أنت.
SP·03ما يُخفيه هذا، وما لا يُخفيه بوضوح
النطاق الصادق لهذا أضيق مما تقترحه الحملات التسويقية حول DNS الخاص، ومعرفة حدوده هي ما يمنعك من اتخاذ قرار سيئ بدافع شعور جيد فقط.
هو يُزيل شيئًا واحدًا: سجلّ الأسماء المُجمَّع الذي يحتفظ به مراقب واحد. وهذا شيء كبير، لأن المُجمَّع هو ما له قيمة تجارية وما يُطلَب بالجملة. لكنه ليس كل شيء.
هو لا يُخفي الاتصال. فبعد أن يُحلَّ الاستعلام، يظل جهازك يفتح جلسة إلى ذلك العنوان، وأي شخص يراقب صلتك الصاعدة يرى عنوان IP الوجهة. وبالنسبة لموقع على بنية تحتية مخصّصة، عنوان IP هو الهوية بعينها. كما أنه لا يُخفي اسم المضيف على السلك: فما لم يدعم الطرفان Encrypted Client Hello، تظل مصافحة TLS تحمل اسم الخادم بنص صريح، وهي المعلومة نفسها التي كان سيحصل عليها مُحلِّلك.
هو لا يُخفي الاستعلامات عن مزوّد الاستضافة. وهذا هو الجزء الذي يُخطئ فيه الناس، فيستحق أن يُقال دون مواربة: حركة مرور مُحلِّلك الصاعدة — الأسئلة التي يطرحها على الجذر وخوادم TLD والخوادم المرجعية — تُغادر VPS عبر منفذ UDP 53، بلا تشفير، والشبكة التي يقف خادمك عليها تستطيع قراءتها كاملةً. فلا يوجد DoT إلى الجذر. أنت لم تحذف المُراقِب بقدر ما نقلتَه، من مزوّد إنترنت استهلاكي يبيع البيانات ويرد على أوامر الاستدعاء القضائي في بلدك، إلى شبكة استضافة في ولاية قضائية اخترتَها بعناية، تشهد تدفّقًا مُجزَّأً بفضل QNAME minimisation. وهذا تحسّن حقيقي، وهو جدال حول أي قانون ينطبق على السلك أكثر من كونه شيئًا يستطيع ملف إعدادات أن يحسمه.
وهو لا يمنحك حشدًا. فمُحلِّل يستخدمه بيت واحد يُسنِد كل استعلام فيه إلى ذلك البيت دون أي لبس. أمام خصم عالمي يرصد بشكل سلبي، يكون المُحلِّل المشترك المزدحم فعلاً المخبأ الأفضل. أمام مزوّد إنترنتك، وربّ عملك، وسماسرة البيانات الذين يشترون بيانات تتبّع المُحلِّلات، والطلبات الروتينية بالجملة التي تحدث فعلاً للناس العاديين، مُحلِّلك الخاص أفضل — بشرط أن تكون القفزة الأخيرة من أجهزتك داخل نفق. شغِّل هذا مع نفق WireGuard الخاص بك، لا بدلاً منه. بمفرده، مُحلِّل خاص يكاد فقط ينقل بياناتك الوصفية من مكان لآخر. خلف نفق، فإنه يُغلق القناة الوحيدة التي يتركها النفق مفتوحة.
SP·04الخطأ الذي يحوّل مُحلِّلك إلى سلاح شخص آخر
هناك طريقة واحدة بالضبط لإخطاء هذا الأمر بشكل خطير، ومن السهل الوقوع فيها بالخطأ، وتقع تبعاتها على الغرباء قبل أن تقع عليك.
مُحلِّل يجيب أي أحد هو مُحلِّل مفتوح، والمُحلِّل المفتوح مُضخِّم. DNS يعمل عبر UDP، وعناوين المصدر في UDP يسهل انتحالها بلا أي عناء، ويمكن لاستعلام صغير أن يُنتج ردًّا أكبر منه بأضعاف مضاعفة. يرسل المهاجم إلى جهازك سؤالاً من 60 بايت مع عنوان ضحية منتحَل كمُرسِل؛ فيرسل جهازك بأمانة ردًّا أكبر بعشرات المرات إلى الضحية. افعل ذلك من عدة آلاف من المُحلِّلات المفتوحة في وقت واحد وتنقطع الضحية عن الإنترنت، بعد أن استقبلت فيضانًا يبدو — بدقّة، على مستوى الحزم — أنه قادم منك. أنت لست الهدف. أنت المسدَّس، وحركة المرور في تقرير الحادثة تخصّك.
وما يتبع ذلك غير مشرِّف: تقارير سوء استخدام من شبكات لم تسمع بها من قبل، ومزوّد يُسقط التوجيه إلى عنوانك (nullroute) ليحمي عبوره الخاص، ومحادثة بخصوص حسابك تفضّل ألّا تجريها. تنتهي بك الحال في الطرف المُتسبِّب بالضبط في الحادثة نفسها الموصوفة في دليل الساعة الأولى لهجمات DDoS عندنا، ولا توجد نسخة من تلك القصة ينجو فيها وقت تشغيل خادمك.
قُفلان مستقلّان يمنعان ذلك، وتحتاج كليهما، لأن كل واحد منهما يغطّي فشل الآخر. الأول هو إعداد access-control الخاص بـUnbound نفسه، والذي ينبغي أن يرفض (refuse) الإنترنت كله على عائلتي IP، ثم يسمح (allow) صريحًا لـloopback ولشبكة نفقك الفرعية — قائمة رفض افتراضي، لا قائمة سماح بذيل متساهل. والثاني هو أين تستمع الخدمة (daemon) على الإطلاق: اربطها بـ127.0.0.1 وعنوان النفق، لا بـ0.0.0.0 أبدًا، واحتفظ بالمنفذ 53 مغلقًا على الواجهة العامة عند جدار الحماية.
والفخّ هو أن تفعل الأول فقط. قاعدة access-control لا تجعل المنفذ يختفي؛ فالاستعلام المرفوض يبقى حزمة استُقبلت وحزمة أُرسلت، وعنوانك يظل يظهر في المسوحات التي تُخرِّط المُحلِّلات المفتوحة، وتعديل واحد لاحق على الفقرة الخطأ يحوّل الرفض إلى إجابة. الربط وجدار الحماية يجعلان هذا الخطأ مستحيلاً من ناحية البنية، لا بُعد سطرِ إعدادٍ واحد فقط. وإذا كان الجهاز يُشغّل حاويات أيضًا، أعد قراءة كيف ينشر Docker المنافذ متجاوزًا جدار حمايتك قبل أن تفترض أن القاعدة التي كتبتها هي القاعدة النافذة فعلاً.
افتراضيات Unbound معقولة. لكنها ليست خاصة.
يأتي Unbound مضبوطًا مسبقًا من أجل الصحة والاستقرار، وهذا هو الافتراضي الصحيح لبرنامج يُنشره في الغالب مزوّدو خدمة الإنترنت. حفنة من الإعدادات تحوّله إلى شيء مبني لمن يُشغّله هو. لا شيء منها غريب أو معقّد؛ هي فقط مُعطَّلة، أو غير مُنحازة لخيار معيّن، بحالتها الافتراضية.
كفّ عن الإجابة عن أسئلة بخصوص نفسك. بشكل افتراضي، يُبلِّغ المُحلِّل بسرور عن إصدار برنامجه واسم مضيفه عبر صنف CHAOS — أي version.bind وhostname.bind — وهذا استطلاع مجاني لأي شخص يقرر ما إذا كان جهازك يستحق الانتباه. الإعدادان hide-identity وhide-version لا يُكلّفان شيئًا ويُزيلان بصمة.
افشل مغلقًا، لا مفتوحًا. التحقق من DNSSEC هو الفارق بين مُحلِّل يكتشف إجابة منتحَلة ومُحلِّل يُقدّمها. يرفض harden-dnssec-stripped قبول إجابة غير موقَّعة لمنطقة يُفترض أن تكون موقَّعة، ويُغلق harden-glue وharden-below-nxdomain منفذين كلاسيكيين لتسميم الذاكرة المؤقتة، ويسمح aggressive-nsec للمُحلِّل بالإجابة عن أسماء غير موجودة مباشرةً من سجلّات رفض مخزَّنة مؤقتًا بدلاً من السؤال مجدَّدًا. يُضيف use-caps-for-id تبديلاً عشوائيًا لحالة الأحرف كعشوائية إضافية ضد الانتحال الأعمى — رخيص، وأحيانًا غير مُتوافق مع خادم مرجعي سيئ البناء، وهذا يستحق أن تعرفه قبل أن تُنفق بعد ظهر كامل على نطاق لن يُحلّ.
لا تكتب شيئًا. لا يُسجّل Unbound الاستعلامات إلا إذا طُلب منه ذلك، لكن الإعدادات التي تفعل ذلك تبعد سطرًا واحدًا غير معلَّق عليه، وبعض حزم التوزيعات تُشحَن بافتراضي أكثر ثرثرة. اضبط verbosity: 0 وحدِّد log-queries: no صريحًا، حتى تكون النيّة ظاهرة في الملف لا مُستنتَجة من غيابها. ثم تذكّر الجزء الذي ليس إعدادًا: الذاكرة المؤقتة نفسها سجلّ. يطبع أمر unbound-control dump_cache على جهاز يعمل تاريخًا حديثًا لما بحث عنه ذلك الجهاز، وهو يعيش في ذاكرة جهاز يستطيع شخص آخر الوصول إليه. وهو ينتهي من تلقاء نفسه، وهذا هو السبب الكامل وراء التوتّر الخفيف بين أعمار الذاكرة المؤقتة القصيرة والخصوصية، وهو السبب في ألّا تُبقي مُحلِّلاً يعمل لأشهر على مضيف لا تثق به على الإطلاق.
خزِّن مؤقتًا عن قصد. كل إجابة تُقدَّم من الذاكرة المؤقتة هي مراقبة لا تحدث أبدًا في الاتجاه الصاعد، فالذاكرة المؤقتة السليمة ميزة خصوصية لا ميزة سرعة فقط. يُجدِّد prefetch السجلات الشائعة قبل انتهاء صلاحيتها، فتتوقف الحالة الشائعة عن لمس الشبكة كليًا؛ ويُبقيك serve-expired متصلاً حين يكون خادم مرجعي غير قابل للوصول لفترة قصيرة. ورفع cache-min-ttl يُقلّل الثرثرة الصاعدة أكثر، لكنه يتجاوز خيارات متعمَّدة من مُشغّلي النطاقات — فقيم TTL المنخفضة هي كيفية عمل شبكات توصيل المحتوى والتبديل عند العطل — فحدّ أدنى من دقيقة أو دقيقتين معقول، وساعة كاملة ستُحاصرك في النهاية على عنوان ميت.
طريقان للدخول: النفق، أو DNS-over-TLS
على أجهزتك أن تصل إلى المُحلِّل بطريقة ما، والاختيار بين الخيارين المعقولين هو في الغالب سؤال عمّا أنت راغب في نشره.
النفق هو الجواب الأفضل لِمَا يقارب الجميع. إذا كان حاسوبك المحمول وهاتفك يحتفظان بجلسة WireGuard مع ذلك الخادم بالفعل، يستطيع المُحلِّل الاستماع على عنوان النفق والتحدث بـDNS العادي على المنفذ 53. حركة المرور مشفّرة بالفعل ومُصادَق عليها بالفعل من طرف النفق، فلا توجد شهادة يجب الحصول عليها، ولا منفذ جديد مفتوح على الإنترنت، ولا حزمة TLS مكشوفة للغرباء، و — وهذا الجزء المُقلَّل من قيمته — لا اسم مضيف في أي مكان. يترك دليل WireGuard على هذا الموقع العملاء موجَّهين إلى DNS = 9.9.9.9 فقط لأنه لم يكن هناك شيء أفضل يُكتب هناك بعد. وهذا ما يُكتب في ذلك السطر بدلاً منه: عنوان النفق لخادمك أنت.
أما DNS-over-TLS فهو للجهاز الذي لا يستطيع الاحتفاظ بنفق. حقل Private DNS في Android هو أقوى حالة استخدام له — فهو يُطبَّق على مستوى النظام كله، وينجو من إعادة التشغيل، ويغطّي تطبيقات لا تستطيع لمسها بطريقة أخرى. والتكلفة هي اسم مضيف بشهادة صالحة، والشهادة تعني قيدًا عامًا ودائمًا في سجلّات شفافية الشهادات (Certificate Transparency) يربط ذلك الاسم باللحظة التي أنشأته فيها. وسيربط DNS السلبي (Passive DNS) الاسمَ بعنوان الخادم بعد ذلك. وإذا كانت غاية التمرين هي إبقاء اسمك بعيدًا عن البنية التحتية، فاستخدم اسم مضيف لا يقود إلى أي مكان قريب منك وسجّله بالعناية الموصوفة في تسجيل نطاق دون الكشف عن هويتك — وليس نطاقًا فرعيًا للنطاق الذي تستخدمه لكل شيء آخر، فذلك يربط الاثنين بشكل دائم.
وهناك تكلفة ثانية أدقّ. نقطة نهاية DoT التي تستطيع الهواتف المتجوّلة الوصول إليها لا يمكن تقييدها بعنوان المصدر، فهي بحكم تعريفها مُحلِّل يستطيع الغرباء استخدامه إن عرفوا الاسم. وهي ليست مُضخِّمًا — فـTLS عبر TCP يتطلّب مصافحة مكتملة، فلا يمكن انتحال المصدر ولا يوجد شيء لتنعكس منه — لكنها سعة تتنازل عنها، وخدمة تستحق أن يُمسَح عنها. أبقِ حدود المعدّل لكل عنوان IP مُفعَّلة، واختر اسم مضيف لن يخمّنه أحد، وعامِلْه كاستثناء متعمَّد لجهاز أو اثنين لا كالباب الافتراضي.
DNS-over-HTTPS خيار ثالث، وهو عادةً الخيار الخاطئ هنا. فهو يحتاج خادم ويب أمام المُحلِّل، وهذا أجزاء متحرّكة أكثر وسطح هجوم أكبر، من أجل فائدة واحدة هي أن يكون غير قابل للتمييز عن حركة مرور الويب. تلك الفائدة حاسمة إن كنت تتجاوز شبكة تحجب DoT، وغير ذات صلة إن لم تكن كذلك.
SP·07الحجب منتج مختلف؛ فاحسم أمرك قبل أن تُضيفه
عاجلاً أو آجلاً سيقترح أحدهم إضافة قوائم حجب، والعرض جذّاب فعلاً: الترشيح عند المُحلِّل يغطّي كل جهاز على النفق، بما في ذلك التلفاز الذكي وتطبيقات الهاتف التي لا تستطيع أي إضافة الوصول إليها. وعلى الأجهزة المحمولة خاصةً، هو المكان العملي الوحيد للتدخّل. وهو أيضًا الميزة الأكثر ترجيحًا لجعلك تفقد الثقة بصمت في بنيتك التحتية الخاصة، ويستحق أن تفهم السبب قبل أن يحدث هذا لا خلاله.
التكلفة الأولى أن الأعطال لا تبدو كأعطال. مُحلِّل معطَّل يُعلن عن نفسه؛ أما نطاق محجوب فيظهر كزرّ دفع لا يفعل شيئًا، أو تطبيق عالق على أيقونة تحميل دوّارة، أو رسالة بريد لا تصل أبدًا. تظهر العلامة أسابيع بعد أن ثبّتَّ القائمة، على جهاز لم تكن تُفكّر فيه، ولا شيء يربطها بقرار DNS اتخذته في شهر آخر. كل قائمة حجب تُثبّتها هي سياسة كتبها شخص غريب وتُطبَّق بصمت على بيتك. إذا أضفت واحدة، دوِّن أنك فعلت، واجعلها صغيرة وموثوقة، واحتفظ بطريقة بأمر واحد لإيقافها — فأول خطوة لتصحيح أي خلل غير مفهوم على شبكتك تصبح "تجاوز المرشِّح وأعد المحاولة"، وتلك الخطوة يجب أن تكون رخيصة.
التكلفة الثانية أنه لا يفعل ما يأمله الناس. تطبيق بعنوان مُحلِّل مُرمَّز داخل الشيفرة، أو بعميل DoH خاص به مُدمَج، لا يسأل مُحلِّلك شيئًا على الإطلاق؛ بل يفتح اتصالاً بعنوان IP مُرمَّز مسبقًا وينتهي الأمر. والمتصفحات تحلّ الأسماء بشكل متزايد عبر DoH خاص بها إلا إذا طُلب منها غير ذلك. حجب DNS طبقة نظافة تُزيل كثيرًا من ضجيج التتبّع والإعلانات منخفض الجهد، وهو ليس ضابطًا أمنيًا، لأن أي شيء عدائي يلتف حوله بحكم تصميمه.
إن كنتَ تريده على أي حال، فضِّل منطقة محلية صغيرة في Unbound على خدمة (daemon) ثانية — فـlocal-zone: "tracker.example." always_nxdomain لا يحتاج برنامجًا إضافيًا، ولا واجهة ويب على منفذ يتوجّب عليك الدفاع عنه بعد ذلك، ولا خدمة جديدة يمكن أن تنهار وتسحب معها تحليل أسمائك. اجعل توصيف وظيفة الجهاز في سطر واحد: هو يحلّ الأسماء. كل مسؤولية إضافية تعطيها له هي طريقة أخرى لتوقّف كل شيء عن العمل في آنٍ واحد.
المُحلِّل الذي تُشغّله اعتمادية تملكها أنت
عندما يتعطّل موقع تستضيفه، يعجز بعض الناس عن قراءة شيء ما. لكن عندما يتعطّل مُحلِّلك، لا يعمل شيء — لا المتصفح، ولا البريد، ولا مدير الحزم، ولا التطبيق الذي كان سيُخبرك أن الخادم معطَّل. إنها الخدمة الأكثر حِملاً بين كل ما يمكن أن تضعه على جهاز رخيص، وهي تتعطّل بطرق لا تبدو كأنها متعلقة بـDNS.
يستحق أنماط العطل الواقعية أن تُعرَف مسبقًا لأن كل واحد منها له توقيع مختلف. يُعاد تشغيل VPS ولم يكن Unbound مُفعَّلاً عند الإقلاع أبدًا، فيعمل كل شيء إلى أن تحدث أول إعادة تشغيل غير مخطَّطة. قائمة حجب ضخمة تدفع الذاكرة المؤقتة إلى swap على مثيل بسعة 1 GB فيختار قاتل OOM المُحلِّل. نطاق ما يكسر توقيعات DNSSEC الخاصة به، فيرفض مُحلِّلك المضبوط بشكل صحيح الإجابة بينما يستمر كل من يستخدم مُحلِّلاً لا يتحقق في التصفّح — جهازك مُحقّ والموقع لا يزال يبدو معطَّلاً لك، وهي خمس دقائق مُحيِّرة إن كنت قد نسيت أنك تتحقق. أو ينقطع النفق، ولأن DNS = 10.66.0.1 لا يوجد إلا داخل النفق، لا يملك الجهاز أي مُحلِّل على الإطلاق ويُبلِّغ أنه غير متصل.
لحسن الحظ أن سبل التخفيف رخيصة. فعِّل الخدمة عند الإقلاع واختبرها فعليًا بإعادة تشغيل بدلاً من الافتراض. أعطِ العملاء مُحلِّلاً ثانويًا حتى يتراجع النفق الميت بدلاً من التوقّف — مُحلِّل عام في تلك الخانة تنازل خصوصية صغير وصريح لا يُطبَّق إلا حين يكون مُحلِّلك الخاص غير قابل للوصول، وهو عادةً المقايضة الصحيحة. أبقِ serve-expired مُفعَّلاً حتى لا يتحوّل انقطاع صاعد قصير إلى انقطاع خاص بك. افحص المُحلِّل من مكان آخر، لا من الجهاز نفسه، وهي الطريقة الوحيدة لتلاحظ الفرق بين "معطَّل" و"غير قابل للوصول".
واحتفظ بنسخة من الإعدادات. الأمر كله بضعة عشرات من الأسطر التي استغرقت منك بعد ظهر كامل لضبطها بشكل صحيح ولن تتذكرها بعد عام؛ فمكانها في نسخك الاحتياطية المشفّرة خارج الموقع إلى جانب مفاتيح WireGuard، حتى تكون إعادة البناء عشرين دقيقة لا بعد ظهر ثانٍ. وهذا هو الملخّص الصادق لهذا التمرين كله: جهاز صغير يقوم بمهمة واحدة، بسعر أرخص خطة في القائمة، يستبدل وعدًا بشأن الاحتفاظ بترتيب لا يُنشأ فيه السجلّ مطلقًا.
SP·09خطوة بخطوة
-
01
ابدأ من جهاز مُحصَّن مسبقًا
المُحلِّل خدمة صغيرة وهادئة، وهذا ما يجعل تثبيته على أي جهاز متاح أمرًا مُغريًا. لا تفعل ذلك — هذا الجهاز سيرى كل اسم يبحث عنه كل جهاز على نفقك، فهو يستحق نفس المعاملة التي تُعطيها لأي شيء آخر يحمل أسرارًا. اختر أصغر خطة تريدها؛ 1 GB من الذاكرة يكفي بيتًا كاملاً، لأن أحجام الذاكرة المؤقتة أدناه تُقاس بعشرات الميغابايتات. اعمل خلال الساعة الأولى بعد النشر قبل أي شيء آخر: مستخدم مُسمّى، وSSH بالمفتاح فقط، ورفض افتراضي على عائلتي IP، وتحديثات أمان تلقائية.
ثم ثبِّت Unbound. تأتي حزمة التوزيعة بتلميحات الجذر ومرساة ثقة DNSSEC الجذرية وتُفعِّل استبدالها الدوري التلقائي، وهذه واحدة من الحالات القليلة التي تُنقذك فيها النسخة المُعبَّأة فعليًا من فئة كاملة من الأعطال المستقبلية.
sudo apt update && sudo apt install -y unbound dnsutils unbound -V | head -n 3 # the packaged trust anchor the resolver will validate against sudo ls -l /var/lib/unbound/root.key
إذا كان
root.keyغائبًا، فإن حزمتك لم تُشغّلunbound-anchorوسيفشل التحقق مغلقًا على كل شيء. أنشئه مرة واحدة بتشغيلsudo -u unbound unbound-anchor -a /var/lib/unbound/root.keyقبل الاستمرار. -
02
استرجِع المنفذ 53 من systemd-resolved
في معظم التوزيعات الحالية، يمتلك شيء ما المنفذ 53 فعلاً: يُشغّل
systemd-resolvedمستمعًا وسيطًا على127.0.0.53، و/etc/resolv.confرابط رمزي يشير إليه. سيرفض Unbound أن يبدأ، أو سيبدأ ولا يرتبط بشيء مفيد، إلى أن يُحلّ ذلك. انظر قبل أن تُعدِّل.ss -ulpn 'sport = :53' ls -l /etc/resolv.conf
اقرأ هذه الفقرة قبل تشغيل الكتلة التالية: بين تعطيل المستمع الوسيط وبدء تشغيل Unbound، لا يملك هذا الجهاز أي DNS يعمل على الإطلاق. أنهِ الخطوتين 3 و4 في جلسة واحدة، ولا تُشغّل عملية
aptفي نافذة أخرى أولاً — فستتعلّق عند البحث عن اسم وستُشخِّصها خطأً كأنها المُحلِّل الذي لم تُشغِّله بعد.# stop resolved from holding the port (appends inside the [Resolve] section) printf 'DNSStubListener=no\n' | sudo tee -a /etc/systemd/resolved.conf sudo systemctl restart systemd-resolved # point the host at the resolver it is about to run sudo rm -f /etc/resolv.conf printf 'nameserver 127.0.0.1\noptions edns0 trust-ad\n' | sudo tee /etc/resolv.conf # the port should now be free ss -ulpn 'sport = :53'
-
03
اكتب إعدادًا يُحلِّل عوديًا ولا يحتفظ بشيء
اترك الإعداد المُعبَّأ كما هو وأضف ملفك الخاص في دليل الملفات التكميلية، حتى لا تُراجِع ترقيةُ حزمة قراراتِك بصمت أبدًا. كل سطر أدناه يتعلّق إما برفض الغرباء، أو بالتحليل العودي من الجذر، أو بعدم الاحتفاظ بسجلّات. استبدل
10.66.0.1و10.66.0.0/24بعنوان النفق وشبكته الفرعية من خادم WireGuard الخاص بك إذا اختلفا.sudo tee /etc/unbound/unbound.conf.d/private-resolver.conf > /dev/null <<'EOF' server: # listen for the host itself and for the tunnel — never on 0.0.0.0 interface: 127.0.0.1 interface: 10.66.0.1 port: 53 # default-deny: refuse the internet, then allow what you trust access-control: 0.0.0.0/0 refuse access-control: ::/0 refuse access-control: 127.0.0.0/8 allow access-control: 10.66.0.0/24 allow # recurse from the root and send each server only the label it needs qname-minimisation: yes harden-dnssec-stripped: yes harden-below-nxdomain: yes harden-glue: yes aggressive-nsec: yes use-caps-for-id: yes # answer nothing about the software or the host hide-identity: yes hide-version: yes # keep no query log, and say little to the journal verbosity: 0 log-queries: no log-replies: no # a warm cache is an upstream observation that never happens cache-min-ttl: 120 cache-max-ttl: 86400 prefetch: yes prefetch-key: yes serve-expired: yes # belt and braces if a rule above is ever loosened ratelimit: 1000 ip-ratelimit: 100 # sizing for a small instance num-threads: 2 so-reuseport: yes msg-cache-size: 32m rrset-cache-size: 64m EOFلاحظ ما لا يوجد في الملف: لا يوجد
forward-zone. غيابه هو ما يجعل هذا مُحلِّلاً عوديًا لا ذاكرة تخزين مؤقت أمام ذاكرة شخص آخر. إذا لصقتَ لاحقًا مقتطفًا من درس تعليمي يضيف واحدًا، فقد نقضتَ بصمت كل غاية هذا التمرين. -
04
شغِّله، ثم أثبِت أن DNSSEC يتحقق فعلاً
تحقّق من الصياغة قبل إعادة تشغيل أي شيء — على جهاز يشير
resolv.confالخاص به الآن إلى Unbound، خطأ في الإعدادات يعني عدم وجود أي تحليل أسماء بينما تُصحِّح الخلل.sudo unbound-checkconf sudo systemctl enable --now unbound systemctl --no-pager status unbound | head -n 5
الآن تحقّق من السلوكين المهمّين، لأن المُحلِّل الذي يُجيب ليس كالمُحلِّل الذي يتحقّق. يجب أن يعود الاسم المُوقَّع مع العلَم
ad— بيانات مُصادَق عليها — ويجب أن يفشل اسم بتوقيعات مكسورة عمدًا مغلقًا برمزSERVFAILبدلاً من أن يُحلّ.# should show: flags: qr rd ra ad dig @127.0.0.1 example.com +dnssec | grep -E '^;; flags' # should show: status: SERVFAIL (not NOERROR, not an address) dig @127.0.0.1 dnssec-failed.org | grep -E '^;; ->>HEADER' # and a normal name should simply work dig @127.0.0.1 +short cloudflare.com
إذا أُحلّت المنطقة المكسورة إلى عنوان، فإن التحقق لا يعمل: تأكّد من أن
/var/lib/unbound/root.keyموجود وقابل للقراءة من قِبل مستخدمunbound. وإذا كان كل شيءSERVFAIL، فالسبب المعتاد ساعة مُختلَّة بشكل كبير — فالتوقيعات لها نوافذ صلاحية، وجهاز متأخّر بساعات عن التزامن يرفض الإنترنت كله. -
05
أغلق الباب العام، ولا تفتح إلا النفق
الإعدادات ترفض الغرباء بالفعل. هذه الخطوة تجعل الأمر بحيث لا يوجد شيء يستطيع الغريب الوصول إليه من الأساس — القفل الثاني من القفلين، وهو الذي ينجو من أي تعديل مستقبلي على الأول.
# DNS is reachable from the tunnel interface only sudo ufw allow in on wg0 to any port 53 proto udp sudo ufw allow in on wg0 to any port 53 proto tcp sudo ufw status verbose
ثم تحقّق منه بالطريقة الوحيدة التي تُحتسَب، من جهاز مختلف. الاختبار من الخادم نفسه لا يُثبت شيئًا على الإطلاق — فـloopback مسموح به عمدًا. شغِّل هذا من حاسوبك المحمول مع نفق معطَّل، أو من أي مضيف آخر تملكه.
# must time out. an answer here means you are running an open resolver. dig @SERVER_PUBLIC_IP example.com +time=3 +tries=1 # same question over IPv6, which is the half people forget dig -6 @SERVER_IPV6 example.com +time=3 +tries=1
إذا أعاد أيّهما إجابة، فأوقف واصلح الأمر الآن بدلاً من بعد تقرير سوء الاستخدام. الأسباب المعتادة هي
interface: 0.0.0.0باقٍ في ملف إعدادات مُعبَّأ، أو قاعدة جدار حماية تسمح بالمنفذ 53 عالميًا من تجربة سابقة، أو Docker ينشر منفذ حاوية يتجاوزufwكليًا. -
06
وجِّه أجهزتك إليه
من جانب العميل، هذا تغيير سطر واحد. في إعدادات عميل WireGuard، يصبح سطر
DNSعنوان نفق خادمك بدلاً من مُحلِّل عام — وهذا هو التعديل الذي يُقاعِدDNS = 9.9.9.9من دليل WireGuard.[Interface] PrivateKey = <paste client.key> Address = 10.66.0.2/32, fd86:ea04:1115::2/128 DNS = 10.66.0.1 [Peer] PublicKey = <paste server.pub> Endpoint = YOUR_SERVER_IP:51820 AllowedIPs = 0.0.0.0/0, ::/0 PersistentKeepalive = 25
شغِّل النفق وتأكّد من أمرين مستقلّين من جانب العميل: أن الإجابات تأتي من مُحلِّلك، وأن التحليل العودي يخرج (egress) فعليًا من VPS الخاص بك لا من مكان آخر. الفحص الثاني هو المفيد —
whoami.akamai.netيُعيد عنوان أي مُحلِّل سأل، فينبغي أن يطبع عنوان IP العام لخادمك ولا شيء آخر.# answers should come from the tunnel address dig example.com | grep -E 'SERVER:' # should print your VPS public IP — this is the leak test dig +short whoami.akamai.net # and the ad flag should still be there, end to end dig example.com +dnssec | grep -E '^;; flags'
-
07
أضِف DNS-over-TLS فقط لجهاز لا يستطيع الاحتفاظ بالنفق
تجاوز هذه الخطوة إلا إذا كان لديك جهاز محدَّد — عادةً هاتف Android، عبر إعداد Private DNS على مستوى النظام — تريد تغطيته دون نفق دائم. يتطلّب ذلك اسم مضيف وشهادة، ويُصبح اسم المضيف هذا سجلاً عامًا ودائمًا في سجلّات شفافية الشهادات (Certificate Transparency)، فاختر اسمًا لا يقود إلى أي مكان قريب من هوياتك الأخرى.
sudo apt install -y certbot sudo ufw allow 80/tcp comment 'certbot, temporarily' sudo certbot certonly --standalone -d dns.example.net sudo ufw delete allow 80/tcp # unbound must be able to read the key sudo usermod -a -G ssl-cert unbound 2>/dev/null || true sudo chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive sudo chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archive
أضِف مستمع TLS كملفٍ تكميليٍّ خاص به، حتى تستطيع حذفه بحركة واحدة إذا غيّرت رأيك.
# NOTE: drop-in files are read in alphabetical order and the last # access-control line for a given prefix wins — so this file must # sort AFTER private-resolver.conf, or its refuse rule overrides # the allow below and every DoT client gets REFUSED. sudo tee /etc/unbound/unbound.conf.d/zz-dot.conf > /dev/null <<'EOF' server: interface: 0.0.0.0@853 interface: ::0@853 tls-service-key: "/etc/letsencrypt/live/dns.example.net/privkey.pem" tls-service-pem: "/etc/letsencrypt/live/dns.example.net/fullchain.pem" # roaming clients have no fixed address, so this endpoint must accept any. # safe only because port 53 stays bound to loopback + wg0 and blocked at # the firewall — TLS over TCP cannot be spoofed, so it cannot be reflected. access-control: 0.0.0.0/0 allow access-control: ::/0 allow EOF sudo ufw allow 853/tcp sudo unbound-checkconf && sudo systemctl restart unboundاختبر من خارج النفق قبل أن تثق به، ثم ضع اسم المضيف في حقل Private DNS في الهاتف. التجديد هو الجزء الذي يتعطّل بصمت بعد ثلاثة أشهر — إذ يستبدل certbot الشهادة لكن Unbound يحتفظ بالقديمة في الذاكرة، فأضِف خطّاف نشر (deploy hook) يُعيد تحميلها.
kdig -d @dns.example.net +tls-ca +tls-host=dns.example.net example.com echo 'systemctl reload unbound' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/unbound.sh sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/unbound.sh


