DNS في بيئة الإنتاج مع BIND 9 وUnbound
خروج

الدرس 1 من 12 · التحليل والأدوات

التحليل من الداخل: الإحالات وسجلات glue والتخزين المؤقت الإيجابي والسلبي

45 د Debian 12 الإصدارات BIND 9.18, BIND 9.20, Unbound 1.17+

الهدف

أن تعرف كيف تقرأ ما يفعله المحلّل (resolver) بين سؤال الجهاز والجواب النهائي، لتتعرّف من النظرة الأولى على الإحالة، والجواب الموثوق (authoritative)، والجواب الخارج من الذاكرة المؤقتة (cache)، والجواب السلبي. هذا هو أساس كل تشخيص جادّ لأعطاب DNS.

أهم النقاط

  • يطرح الجهاز سؤالًا تكراريًا (recursive) على محلّله؛ ويطرح المحلّل أسئلة غير تكرارية على الخوادم الموثوقة ويتتبّع الإحالات (referrals).
  • الإحالة = لا جواب، ولا علامة aa، وسجلات NS في قسم AUTHORITY، وغالبًا عناوينها (glue) في قسم ADDITIONAL.
  • سجلات glue إلزامية عندما يحمل خادم الأسماء اسمًا داخل المنطقة التي يخدمها (ns1.fes.masterlinux.lab للمنطقة fes.masterlinux.lab).
  • تحتفظ الذاكرة المؤقتة أيضًا بالأجوبة السلبية (NXDOMAIN وNODATA) طوال TTL سجل SOA المُعاد، مع سقف هو آخر حقل في SOA.
  • الجواب عبر UDP الكبير جدًا يُقتطع (العلامة tc) فيعيد العميل المحاولة عبر TCP: يجب أن يكون المنفذ 53/TCP مفتوحًا.

1. مهنتان ونوعان من الأسئلة

قدّمت دورة «الشبكات وVPN» المحلّل والخادم الموثوق. لننظر الآن إلى الحزم التي يتبادلانها.

السؤال من يرسله البت rd (recursion desired) ما ننتظره
تكراري الجهاز، نحو محلّله 1 الجواب النهائي (أو خطأ)
تتابعي (iterative) المحلّل، نحو الخوادم الموثوقة 0 جواب أو إحالة

باستعمال dig نعيد إنتاج سؤال المحلّل بإيقاف التكرار:

dig @10.20.0.53 www.fes.masterlinux.lab +norec    # كما يفعل المحلّل
dig @10.20.0.10 www.fes.masterlinux.lab           # كما يفعل الجهاز (rd=1)

الخادم الموثوق المضبوط جيدًا يرفض التكرار: يجيب بـ status: REFUSED (أو دون العلامة ra) عندما نسأله عن اسم ليس في مناطقه. والخادم الذي يقبل التكرار لكل الإنترنت هو محلّل مفتوح، أي مضخِّم للهجمات.

2. الإحالة وسجلات glue

لوكالة MasterLinux في فاس منطقتها الفرعية الخاصة fes.masterlinux.lab، تديرها خوادمها ns1.fes وns2.fes. خادم المنطقة الأم (ml-dns-01، 10.20.0.53) لا يعرف www.fes.masterlinux.lab، لكنه يعرف من يجب أن يُسأل:

;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 3
;; AUTHORITY SECTION:
fes.masterlinux.lab.     86400 IN NS ns1.fes.masterlinux.lab.
fes.masterlinux.lab.     86400 IN NS ns2.fes.masterlinux.lab.
;; ADDITIONAL SECTION:
ns1.fes.masterlinux.lab. 86400 IN A  10.30.0.53
ns2.fes.masterlinux.lab. 86400 IN A  10.30.0.54

ثلاث علامات تقول «إحالة»: ANSWER: 0، وغياب العلامة aa، وسجلات NS لمنطقة أدقّ من منطقة الخادم.

لولا السطران في ADDITIONAL لوقع المحلّل في مأزق: للوصول إلى ns1.fes.masterlinux.lab يجب أن يسأل… ns1.fes.masterlinux.lab نفسه. هذه العناوين المكتوبة في المنطقة الأم تسمّى glue. ولا حاجة إليها إلا إذا كان اسم الخادم تحت المنطقة المفوَّضة؛ أما مع خادم مثل ns.hebergeur.ma فيحلّ المحلّل هذا الاسم على حدة.

في المنطقة الأم يُكتب التفويض هكذا:

; db.masterlinux.lab (المنطقة الأم)
fes        IN NS  ns1.fes.masterlinux.lab.
fes        IN NS  ns2.fes.masterlinux.lab.
ns1.fes    IN A   10.30.0.53      ; glue
ns2.fes    IN A   10.30.0.54      ; glue

يجب أن تنشر المنطقة الفرعية سجلات NS نفسها في ملفها. عندما تختلف القائمتان، تتبع بعض المحلّلات الأم وتتبع أخرى الفرع: فنحصل على أعطاب متقطعة يصعب إعادة إنتاجها. نتحقق بـ:

dig @10.20.0.53 fes.masterlinux.lab NS +norec   # ما تقوله الأم (إحالة)
dig @10.30.0.53 fes.masterlinux.lab NS +norec   # ما يقوله الفرع (aa)

3. المسار الكامل مع تقليص الأسئلة

على الإنترنت تبدأ السلسلة من خوادم الجذر. المحلّلات الحديثة (BIND 9.18+ وUnbound) تطبّق تقليص الأسئلة (QNAME minimisation): لا تسأل الجذر إلا عن NS ma. وليس عن الاسم الكامل www.boutique.exemple.ma.؛ فلا يرى كل خادم إلا ما يحتاجه.

résolveur ──NS ma.?────────────► racine        ── renvoi vers les serveurs de .ma
résolveur ──NS exemple.ma.?────► serveurs .ma  ── renvoi vers ns1.exemple.ma
résolveur ──A www.exemple.ma.?─► ns1.exemple.ma ── réponse, drapeau aa

النتيجة العملية: خادم موثوق سيّئ البرمجة يجيب بشكل خاطئ عن الأسماء الوسيطة (أو عن نوع لا يعرفه) يُفسد التحليل لدى المحلّلات الحديثة فقط. في BIND، الخيار qname-minimization relaxed; (القيمة الافتراضية) يعود إلى السلوك القديم عند حدوث مشكلة.

4. التخزين المؤقت الإيجابي: TTL الذي ينقص

يحتفظ المحلّل بكل جواب طوال مدة TTL. وعندما يعيد تقديمه، يذكر المدة المتبقية:

;; flags: qr rd ra; ANSWER: 1
www.fes.masterlinux.lab. 2712 IN A 10.30.1.80
  • لا توجد aa: الجواب آتٍ من ذاكرة مؤقتة.
  • المنطقة تنشر TTL قدره 3600؛ تبقّى منه 2712: الجواب في الذاكرة المؤقتة منذ 888 ثانية.
  • أعد تنفيذ الأمر: الرقم ينقص. وإذا عاد إلى 3600 فالمحلّل قد جلبه من جديد.

إعدادان في المحلّل يُفسدان هذا الحساب: max-cache-ttl (BIND) أو cache-max-ttl (Unbound) يضعان سقفًا للقيم الطويلة جدًا، وcache-min-ttl (Unbound) يرفع القيم الصغيرة. رفع TTL يوفّر قليلًا من الحركة، لكن الترحيل لا ينتشر بالسرعة التي ينتظرها الفريق: تجنّبه.

5. التخزين المؤقت السلبي: NXDOMAIN وNODATA

هناك جوابان سلبيان يجب عدم الخلط بينهما:

الحالة status ANSWER المعنى
NXDOMAIN NXDOMAIN 0 الاسم غير موجود، لأي نوع كان
NODATA NOERROR 0 الاسم موجود، لكن ليس بهذا النوع (مثلًا لا يوجد AAAA)

في الحالتين يرفق الخادم سجل SOA للمنطقة في AUTHORITY. ويحتفظ المحلّل بالجواب السلبي طوال TTL سجل SOA هذا، الذي لا يتجاوز أبدًا آخر حقل في SOA (حقل «minimum»، الذي تغيّر معناه عبر السنين ليصبح مدة التخزين المؤقت السلبي):

masterlinux.lab. 300 IN SOA ns1.masterlinux.lab. hostmaster.masterlinux.lab. 2026102001 3600 900 1209600 300
                 ^^^                                                                                  ^^^
          TTL du cache négatif                                                    plafond du cache négatif

حالة واقعية: ينشئ الفريق paiement.masterlinux.lab على الساعة 10:00. كان أحد المطوّرين قد اختبره على الساعة 9:58 فتلقّى NXDOMAIN. إذا كان آخر حقل في SOA يساوي 86400، فسيجيبه محلّله «غير موجود» حتى اليوم التالي. اجعل هذا الحقل بين 300 و3600 ثانية، وأفرغ الذاكرة المؤقتة عند الاستعجال (rndc flushname paiement.masterlinux.lab على BIND، وunbound-control flush paiement.masterlinux.lab على Unbound).

6. SERVFAIL: المحلّل يستسلم

SERVFAIL الصادر عن محلّل يعني «لم أستطع الحصول على جواب موثوق»: كل الخوادم الموثوقة غير قابلة للوصول، أو التفويض معطوب، أو فشل التحقق من DNSSEC. ويخزّن المحلّل هذه النتيجة أيضًا بضع ثوانٍ (servfail-ttl في BIND، ثانية واحدة افتراضيًا) حتى لا يلحّ.

لمعرفة السبب، اسأل الخوادم الموثوقة مباشرة مع +norec، ثم أعد السؤال مع +cd (checking disabled): إذا نجح +cd فالسبب هو DNSSEC (الدرس 3.2).

7. EDNS وحجم الأجوبة وTCP

كان DNS «التاريخي» يحدّ أجوبة UDP بـ 512 بايت. يسمح EDNS(0) بالإعلان عن حجم أكبر. ومنذ DNS Flag Day لسنة 2020، القيمة الموصى بها هي 1232 بايت: ما فوقها قد تتجزّأ الحزم، والأجزاء كثيرًا ما تضيع أو تُحجب.

dig @10.20.0.53 masterlinux.lab DNSKEY +dnssec +bufsize=1232
# ;; flags: qr aa tc ...      <- tc : جواب مقتطع، dig يعيد المحاولة عبر TCP تلقائيًا
dig @10.20.0.53 masterlinux.lab DNSKEY +dnssec +tcp     # فرض TCP

الجدار الناري (firewall) الذي لا يسمح إلا بـ 53/UDP يُفسد الأجوبة الكبيرة (DNSSEC، قوائم TXT الطويلة) وعمليات نقل المناطق: افتح دائمًا 53/UDP و53/TCP.

8. المحلّل المحلي للجهاز (stub)

على الجهاز، تقرأ مكتبة C الملف /etc/resolv.conf:

nameserver 10.20.0.10              # حتى 3 خوادم، تُجرَّب بالترتيب
nameserver 10.20.0.11
search fes.masterlinux.lab masterlinux.lab   # لواحق تضاف إلى الأسماء القصيرة
options timeout:2 attempts:2 rotate
  • timeout:2: ثانيتان من الانتظار لكل خادم (5 افتراضيًا، وهي مدة لا تنتهي عندما يكون الأول معطّلًا).
  • attempts:2: عدد الدورات الكاملة على القائمة.
  • rotate: يوزّع الأسئلة على الخوادم بدل البدء دائمًا بالأول.
  • ndots:n: الاسم الذي يحتوي على أقل من n نقاط يُجرَّب أولًا مع لواحق search. يضع Kubernetes القيمة ndots:5: فيولّد طلب api.exemple.ma عدة أسئلة لا فائدة منها قبل السؤال الصحيح. كتابة الاسم بنقطة نهائية (api.exemple.ma.) تتجنّب ذلك.

على Ubuntu والأنظمة التي تستعمل systemd-resolved، يشير /etc/resolv.conf إلى 127.0.0.53: والخوادم الحقيقية تُقرأ بـ resolvectl status. وعلى Rocky، يعيد NetworkManager كتابة هذا الملف انطلاقًا من الاتصال (nmcli con mod … ipv4.dns).

حسب الإصدار

النقطة BIND 9.16 (Rocky 9، الحزمة bind) BIND 9.18 (Debian 12 وUbuntu 24.04، والحزمة bind9.18 على Rocky 9) BIND 9.20 (Debian 13)
تقليص الأسئلة relaxed افتراضيًا relaxed افتراضيًا relaxed افتراضيًا
حجم EDNS المعلن افتراضيًا 1232 1232 1232
الأجوبة المنتهية الصلاحية (serve-stale) ممكنة، معطّلة ممكنة، معطّلة ممكنة، معطّلة

أخطاء متكررة

  • قراءة TTL المحلّل كأنه TTL المنطقة: إنه TTL متبقٍّ. اسأل الخادم الموثوق (+norec) لقراءة القيمة الحقيقية.
  • الخلط بين NXDOMAIN وNODATA: «لا يوجد AAAA» ليس «الاسم غير موجود». سجل A يعمل مع غياب AAAA أمر عادي.
  • آخر حقل في SOA كبير جدًا: اسم أُنشئ «متأخرًا» يبقى غير قابل للعثور عليه لساعات.
  • 53/TCP مغلق: كل شيء يعمل… إلا DNSSEC والأجوبة الكبيرة وعمليات النقل.

التطبيق العملي (Lab)

تتلقى ملتقطات dig أُخذت على شبكة MasterLinux. ستتعرّف على إحالة وسجلات glue الخاصة بها، وتحسب منذ متى يوجد جواب في الذاكرة المؤقتة، وتصنّف أجوبة سلبية، وتكتب ملف resolv.conf لخوادم وكالة فاس.

اختبار قصير

  1. بأي ثلاث علامات نتعرّف على إحالة في مخرجات dig؟
  2. متى تكون سجلات glue ضرورية؟
  3. ما الفرق بين NXDOMAIN وNODATA؟
  4. كم يبقى الجواب السلبي في الذاكرة المؤقتة؟
  5. لماذا يجب فتح 53/TCP في الجدار الناري؟
الأجوبة
  1. لا يوجد جواب (ANSWER: 0)، ولا توجد العلامة aa، وتوجد سجلات NS لمنطقة أدقّ في AUTHORITY.
  2. عندما يكون اسم خادم الأسماء داخل المنطقة المفوَّضة (ns1.fes.masterlinux.lab للمنطقة fes.masterlinux.lab).
  3. NXDOMAIN: الاسم غير موجود إطلاقًا. NODATA: الاسم موجود لكن ليس له سجل من النوع المطلوب (status: NOERROR مع جواب فارغ).
  4. طوال TTL سجل SOA المرفق بالجواب، والذي لا يتجاوز أبدًا آخر حقل في SOA.
  5. الأجوبة المقتطعة (العلامة tc) وDNSSEC وعمليات نقل المناطق تمرّ عبر TCP.

تمارين تطبيقية

تُنجَز على Debian 12

أنجز هذا التطبيق العملي على جهازك

الطرفية طرفيتك: آلة افتراضية بنظام لينكس (VirtualBox أو VMware أو Hyper-V أو WSL…) أو Docker/Podman. أداة ml تحضّر التمرين وتصحّح عملك، ويُؤكَّد الدرس هنا تلقائيًا.

أنشئ حسابًا مجانيًا للحصول على أمرك الشخصي وتصحيح تطبيقاتك العملية تلقائيًا.

إنشاء حسابي

المهام المطلوبة

  1. حلّل الملف /root/captures/renvoi.txt (جواب من خادم المنطقة الأم). في /root/analyse/reponses.txt اكتب سطرين: zone_deleguee=… (اسم المنطقة الفرعية المفوَّضة) وglue_ns1=… (العنوان المقدَّم لـ ns1 في هذه المنطقة الفرعية).
    عرض تلميحلا توجد علامة aa ولا جواب، لكن توجد سجلات NS في قسم AUTHORITY: إنها إحالة. العناوين الموجودة في ADDITIONAL هي سجلات glue.
    عرض الحل
    mkdir -p /root/analyse && printf '%s\n' 'zone_deleguee=fes.masterlinux.lab' 'glue_ns1=10.30.0.53' >> /root/analyse/reponses.txt

    التحقق : f=/root/analyse/reponses.txt; grep -qx 'zone_deleguee=fes.masterlinux.lab' $f && grep -qx 'glue_ns1=10.30.0.53' $f

  2. في /root/captures/cache.txt يجيب المحلّل من ذاكرته المؤقتة. أضف إلى /root/analyse/reponses.txt السطر ecoule=… : عدد الثواني التي مرّت منذ دخول هذا الجواب إلى الذاكرة المؤقتة.
    عرض تلميحقيمة TTL التي يعرضها المحلّل هي المدة المتبقية. TTL الأصلي ناقص TTL المتبقي.
    عرض الحل
    mkdir -p /root/analyse && echo ecoule=888 >> /root/analyse/reponses.txt

    التحقق : grep -qx 'ecoule=888' /root/analyse/reponses.txt

  3. صنّف الملتقطات الثلاثة a.txt وb.txt وc.txt. أضف إلى /root/analyse/reponses.txt: autoritaire=… (حرف الجواب الصادر عن الخادم الذي يملك المنطقة)، وnodata=… (حرف الجواب «الاسم موجود لكن ليس بهذا النوع»)، وnegatif=… (المدة بالثواني التي يمكن لمحلّل أن يحتفظ فيها بجواب a.txt في ذاكرته المؤقتة).
    عرض تلميحابحث عن العلامة aa؛ NOERROR مع ANSWER: 0 تعني NODATA؛ التخزين المؤقت السلبي يدوم بقدر TTL سجل SOA المُعاد في AUTHORITY.
    عرض الحل
    mkdir -p /root/analyse && printf '%s\n' 'autoritaire=a' 'nodata=c' 'negatif=300' >> /root/analyse/reponses.txt

    التحقق : f=/root/analyse/reponses.txt; grep -qx 'autoritaire=a' $f && grep -qx 'nodata=c' $f && grep -qx 'negatif=300' $f

  4. حضّر الملف /root/analyse/resolv.conf لخوادم وكالة فاس: محلّلان (nameserver 10.20.0.10 ثم nameserver 10.20.0.11)، وسطر البحث «search fes.masterlinux.lab masterlinux.lab»، والسطر «options timeout:2 attempts:2 rotate».
    عرض تلميحأربعة أسطر بهذا الترتيب: nameserver ثم nameserver ثم search ثم options.
    عرض الحل
    mkdir -p /root/analyse && printf '%s\n' 'nameserver 10.20.0.10' 'nameserver 10.20.0.11' 'search fes.masterlinux.lab masterlinux.lab' 'options timeout:2 attempts:2 rotate' > /root/analyse/resolv.conf

    التحقق : f=/root/analyse/resolv.conf; [ "$(grep '^nameserver' $f | tr -s ' ' | paste -sd, -)" = "nameserver 10.20.0.10,nameserver 10.20.0.11" ] && grep -Eqx 'search +fes\.masterlinux\.lab +masterlinux\.lab' $f && grep -Eq '^options .*timeout:2' $f && grep -Eq '^options .*attempts:2' $f && grep -Eq '^options .*rotate' $f