UFW وIPv6: الباب المفتوح على VPS الخاص بك
قواعد UFW وجدار الحماية السحابي قد يغطيان IPv4 فقط، تاركين خدماتك مكشوفة بالكامل على IPv6. تعرّف على سبب حدوث ذلك على VPS وكيفية سدّ هذه الثغرة.
فخّ جدار حماية IPv6 في جملة واحدة
جدار حمايتك يحمي IPv4. ومن شبه المؤكد أن خادم VPS الخاص بك يملك أيضًا عنوان IPv6 عامًا، وكثير من الخدمات تستمع عليه افتراضيًا. فإذا كان جدار حمايتك يغطي IPv4 فقط، أو كنت تعتمد على جدار حماية سحابي يرشّح IPv4 فقط، فإن كل واحدة من تلك الخدمات يمكن الوصول إليها من الإنترنت بأكمله عبر IPv6 بينما يبدو جانب IPv4 لديك مُحكَم الإغلاق. تختبر منفذًا بأمر curl، وترى اتصالًا مرفوضًا، فتشعر بالأمان. ثم يتصل مهاجم بالمنفذ نفسه عبر IPv6 ويدخل ببساطة.
يوضح هذا الدليل من أين تأتي هذه الثغرة على خادم VPS عادي يعمل بنظام Ubuntu 24.04، وكيف ترى بالضبط ما تكشفه، وكيف تسدّها. UFW ليس الجاني هنا؛ فعلى تثبيت Ubuntu حديث يتولى UFW أصلًا معالجة IPv6. مصدر التعرّض هو الطبقات المحيطة به، وخدمات كنتَ لا تعلم أنها تستمع أصلًا.
لماذا خادمك على IPv6 من الأساس
يأتي كل خادم VPS تقريبًا اليوم مزوّدًا بعنوان IPv6 عام، وغالبًا نطاق /64 كامل، إلى جانب عنوان IPv4 الخاص به. تحقّق من عنوانك:
ip -6 addr show scope global2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
inet6 2001:db8:2a::1/64 scope globalهذا العنوان 2001:db8:2a::1 قابل للتوجيه (routable) من أي مكان على الإنترنت، تمامًا مثل عنوان IPv4 الخاص بك. انظر الآن إلى ما يستمع فعلًا:
sudo ss -tlnpState Recv-Q Local Address:Port Process
LISTEN 0 0.0.0.0:22 sshd
LISTEN 0 [::]:22 sshd
LISTEN 0 127.0.0.1:5432 postgres
LISTEN 0 [::]:8080 docker-proxyاقرأ عمود Local Address بتمعّن. 0.0.0.0:22 تعني «استمع على كل عنوان IPv4». و[::]:22 تعني «استمع على كل عنوان IPv6». أما 127.0.0.1:5432 فمرتبط بعنوان استرجاع ذاتي (loopback) لا يغادر الجهاز أصلًا، وليس عامًا على الإطلاق، لذا فسطر Postgres آمن. أما السطران اللذان يحملان [::] فيجيبان الإنترنت بأكمله عبر IPv6، وسطر docker-proxy هو من النوع الذي تنسى أنك شغّلته.
معظم البرامج الخفية (daemons) ترتبط بـ :: افتراضيًا، لأن مقبس (socket) من نوع :: على Linux يقبل عادة عنوان IPv4 أيضًا. لذا فإن الوضع الافتراضي لأي خادم جديد هو «الإجابة على كلا المكدّسين الشبكيين (stacks)، في كل مكان». وجدار حمايتك هو الشيء الوحيد الذي يقف أمام ذلك، ولهذا فإن جدار حماية لا يرى سوى مكدّس واحد يمثّل مشكلة حقيقية.
من أين تأتي ثغرة IPv6 فعليًا
هناك أربعة مصادر شائعة لها. قد يكون لديك واحد منها فقط، أو قد يجتمع أكثر من واحد منها على الجهاز نفسه في آنٍ واحد.
1. جدار حماية سحابي يرشّح IPv4 فقط. كثير من جدران حماية مزوّدي الخدمة ومنتجات «مجموعات الأمان» (security groups) نشأت حول IPv4، فهي إما تتجاهل IPv6 كليًا أو تتطلب قواعد IPv6 منفصلة عليك إضافتها يدويًا. فإذا كان جدار حمايتك الوحيد هو ذلك الموجود في لوحة تحكم المزوّد ولم يكن يغطي IPv6، فإن خدماتك التي تحمل [::] مفتوحة أيًّا كان ما يقوله عن المنفذ 22 على IPv4. اقرأ توثيق جدار حماية مزوّدك وابحث عن كلمة IPv6 تحديدًا.
2. إعداد iptables يدوي بلا ip6tables. أمر iptables لا يمسّ سوى جداول IPv4. أما IPv6 فله أمر منفصل تمامًا هو ip6tables، بقواعده الخاصة المنفصلة. فإن كتبت سكربت جدار حماية مليئًا بأسطر iptables -A INPUT ... ولم تكتب قط قواعد ip6tables المقابلة لها، يكون جدار حماية IPv6 لديك فارغًا، وسلسلة INPUT فارغة بسياسة افتراضية ACCEPT تسمح بكل شيء:
sudo ip6tables -L INPUT -nChain INPUT (policy ACCEPT)
target prot opt source destinationهذا المخرَج يلخّص الفخّ بأكمله على شاشة واحدة: IPv4 مُرشَّح، وIPv6 يقبل العالم كله.
3. Docker ينشر منافذ متجاوزًا جدار حمايتك مباشرة. حين تنفّذ docker run -p 8080:80، يُدرج Docker قواعده الخاصة قبل قواعد UFW، فيصير المنفذ المنشور قابلًا للوصول حتى حين يقول ufw status إن ذلك المنفذ مرفوض، وعلى إصدارات Docker الحديثة الأمر نفسه ينطبق على IPv6. يشرح لماذا يتجاوز Docker جدار UFW، وكيف تُرشِّح منافذ الحاويات بالشكل الصحيح الآلية وطرق الإصلاح. راجع أساسيات Docker Compose على خادم VPS لمعرفة كيف تُعلَن هذه المنافذ المنشورة.
4. UFW حين يكون IPv6 معطّلاً. يتعامل UFW فعلًا مع IPv6، لكن فقط حين يُطلَب منه ذلك. تحقّق من المفتاح:
grep IPV6 /etc/default/ufwيأتي Ubuntu الحديث بالقيمة IPV6=yes، فيطبّق UFW كل قاعدة على المكدّسين معًا. أما إن رأيت IPV6=no، من صورة نظام قديمة أو دليل قديم، فكل قاعدة UFW كتبتها تخصّ IPv4 فقط، ويبقى IPv6 بلا إدارة.
اطّلع بدقة على ما تكشفه
لا تخمّن. قِسه من الخارج. اسرد أولًا كل ما يستمع لديك ولاحظ كل ما هو مرتبط بـ :::
sudo ss -tlnp | grep '::'ثم، من جهاز آخر، اتصل بعنوان IPv6 العام للخادم وجرّب منفذًا تظن أنه مغلق:
curl -6 -v http://[2001:db8:2a::1]:8080/إن أعاد ذلك صفحة أو لافتة (banner)، فالمنفذ مفتوح على IPv6. أما المنفذ المغلق فيعطيك Connection refused أو مهلة منتهية (timeout). ولرؤية الصورة الكاملة، امسح عنوان IPv6 بأداة nmap من خارج الخادم:
nmap -6 2001:db8:2a::1كل منفذ يبلّغ nmap أنه مفتوح عبر IPv6 هو منفذ يستطيع الإنترنت بأكمله الوصول إليه، أيًّا كانت نتيجة مسحك لـ IPv4. ومقارنة مسحَي IPv4 وIPv6 جنبًا إلى جنب هي أسرع طريقة للعثور على الثغرة: فكل ما هو مفتوح في -6 لكنه مغلق في IPv4 هو خدمة يفوّتها جدار حمايتك.
سدّ الثغرة
اجعل UFW يغطي المكدّسين معًا، واجعل الرفض هو السلوك الافتراضي. تأكد من المفتاح، ثم اضبط سياسة رفض افتراضية للاتصالات الواردة ولا تسمح إلا بما تحتاج إليه:
sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verboseإذا كان UFW نشطًا بالفعل حين بدّلت القيمة إلى IPV6=yes، فإن التغيير لا يسري حتى تنفّذ sudo ufw reload.
يسرد ufw status كل قاعدة مرتين: مرة عادية، ومرة بلاحقة (v6). وحين ترى أسطر (v6)، فهذا يعني أن UFW يرشّح IPv6:
22/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)إن كنت تدير iptables يدويًا، فانسخ كل قاعدة إلى ip6tables كذلك، أو انتقل إلى nftables، التي تغطي جداولها من نوع inet كلًّا من IPv4 وIPv6 في مكان واحد وتزيل هذه الفئة من الأخطاء بالكامل. جدول inet filter واحد في nftables هو الحل الأنظف حين تكتب القواعد بنفسك.
اربط الخدمات التي لا تريدها عامة بعنوان الاسترجاع الذاتي. قاعدة بيانات، أو لوحة إدارة، أو نقطة قياسات (metrics endpoint) نادرًا ما تحتاج إلى عنوان عام أصلًا. اربطها بـ 127.0.0.1 و::1 حتى لا تستمع أبدًا على عنوان قابل للتوجيه من الأساس. بالنسبة لـ Postgres، اضبط listen_addresses = 'localhost'. أما خادم التطبيق، فاربطه بـ 127.0.0.1 وضع بروكسي عكسي (reverse proxy) أمامه. إغلاق نقطة الاستماع أفضل من ترشيحها بجدار حماية، لأنه عندها لا يبقى شيء يمكن الوصول إليه.
لا تعتمد على UFW لحماية المنافذ التي ينشرها Docker. انشر منافذ الحاويات إلى عنوان محدد بدلًا من كل واجهة، مثل -p 127.0.0.1:8080:80، بحيث لا يكون المنفذ قابلًا للوصول إلا من المضيف (host) وممّا توجّهه إليه عمدًا عبر بروكسي. وحين يجب أن تكون حاوية عامة فعلًا، ضعها خلف بروكسي عكسي من Traefik ولا تنشر سوى البروكسي، لا كل تطبيق على حدة.
أضف قواعد IPv6 إلى جدار حماية مزوّدك، أو تقبّل أنه ليس جدار حمايتك فيما يخص IPv6، ودع UFW أو nftables على الخادم نفسه يتولى هذه المهمة بدلًا منه.
تحقّق أنك أغلقت المنافذ فعلًا
أعد تنفيذ الاختبار الخارجي نفسه بعد إجراء تغييراتك:
curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1المنفذ الذي كان يستجيب سابقًا ينبغي أن يرفض الاتصال الآن أو تنتهي مهلته، وينبغي أن يبلّغ nmap أنه مرشَّح (filtered) أو مغلق. وإذا ظل منفذ ما مفتوحًا، فارجع إلى المصادر الأربعة أعلاه: خدمة ما زالت مرتبطة بـ :: بلا قاعدة أمامها، أو قاعدة Docker متقدمة على UFW، أو جدار حماية مزوّد لم يرَ IPv6 قط.
وإبقاء الخدمات الحساسة بمنأى تام عن الإنترنت العام أقوى من ذلك كله. ضع SSH ولوحات الإدارة خلف شبكة WireGuard VPN واحجب منافذها بجدار حماية بحيث لا تجيب إلا عبر النفق، وعندها يتوقف سؤال التعرّض لـ IPv6 عن الانطباق عليها. ولإبطاء عمليات مسح القوة الغاشمة التي تصيب كل ما يبقى عامًا، أضف طبقة Fail2ban أمام SSH فوق جدار حماية يرفض افتراضيًا.
وإذا كانت فكرة المنافذ نفسها جديدة عليك، فـ ما هي المنافذ وكيف تستمع الخدمات عليها هو المدخل الذي ينبغي أن تقرأه أولًا.
FAQ
هل يحظر UFW حركة IPv6 افتراضيًا؟
على تثبيت Ubuntu 24.04 حديث، نعم. يقرأ UFW القيمة IPV6=yes من /etc/default/ufw ويطبّق كل قاعدة على IPv4 وIPv6 معًا، ويعرض ufw status قواعد IPv6 بلاحقة (v6). يظهر الفخّ حين تكون القيمة IPV6=no (من صورة نظام قديمة أو درس قديم)، أو حين تعتمد على جدار حماية مزوّد يرشّح IPv4 فقط، أو حين ينشر Docker منفذًا متجاوزًا UFW. تحقّق من المفتاح بالأمر grep IPV6 /etc/default/ufw.
كيف أتحقق مما يكشفه خادم VPS الخاص بي على IPv6؟
نفّذ sudo ss -tlnp ولاحظ كل مستمع يبدأ عنوانه المحلي بـ [::]، ما يعني أنه يجيب على كل واجهة IPv6. ثم، من جهاز آخر، اختبر عنوان IPv6 العام للخادم مباشرة بالأمر curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/، أو امسحه بالأمر nmap -6 YOUR:IPV6::ADDR. وأي منفذ مفتوح في مسح IPv6 لكنه مغلق في IPv4 هو ثغرتك.
لماذا أستطيع الوصول إلى منفذ حاوية Docker رغم أن UFW يقول إنه محجوب؟
يُدرج Docker قواعد جدار حمايته الخاصة قبل قواعد UFW حين تنشر منفذًا بـ -p، فيصبح المنفذ المنشور قابلًا للوصول رغم أن ufw status يدرجه بصفته مرفوضًا. يحدث هذا على IPv4، وعلى IPv6 أيضًا حين يكون دعم IPv6 في Docker مفعّلًا. انشر إلى عنوان محدد مثل -p 127.0.0.1:8080:80، أو ضع الحاوية خلف reverse proxy ولا تنشر سوى الـ proxy.
هل ما زلت أحتاج إلى جدار حماية IPv6 إذا كان جدار حماية IPv4 لديّ محكمًا؟
نعم. فـ IPv4 وIPv6 مكدّسان شبكيان منفصلان، لكل منهما قواعد جدار حماية خاصة به. مجموعة مثالية من قواعد IPv4 لا تفعل شيئًا لحركة IPv6. وإذا كان لدى خادم VPS الخاص بك عنوان IPv6 عام، وهذا حال جميع الخوادم تقريبًا، فإن أي خدمة تستمع على :: تبقى قابلة للوصول عبر IPv6 حتى توقفها قاعدة جدار حماية لـ IPv6 أو ربط بعنوان الاسترجاع الذاتي.
كيف أجعل خدمة تستمع على IPv4 فقط، أو على localhost فقط؟
اضبط عنوان الربط الخاص بالخدمة في إعداداتها هي. اربطها بـ 127.0.0.1 للاسترجاع الذاتي على IPv4 فقط، أو بـ 0.0.0.0 لكل عناوين IPv4 من دون أي استماع على IPv6. يستخدم Postgres الإعداد listen_addresses، ويستخدم SSH الإعداد ListenAddress، وتعرض معظم خوادم التطبيقات خيار host أو bind. تأكد من النتيجة بالأمر sudo ss -tlnp وتحقق من أن Local Address لم يعد يظهر فيه [::].