موقعان وشبكة فرعية واحدة ومحور WireGuard واحد: اجعلها تعمل
تقع كلتا الشبكتين المحليتين على 10.10.7.0/24، لذا لا يمكن للمحور اختيار موقع بحسب الوجهة. امنح كل خدمة اسماً مستعاراً وترجم عند بوابتها.
كلا الموقعين يستخدم النطاق نفسه للشبكة المحلية، والتقاطع ليس ما يعطّل التصميم فعلاً. يجب على المحور أن يختار الموقع باستخدام عنوان الوجهة فقط، ووجهتان متطابقتان لا تمنحانه شيئاً يختار به. إعادة ترقيم مكتب يعمل لتلبية نفق عادةً مقايضة أسوأ من منح كل خدمة منشورة عنواناً مميزاً يستطيع النفق حمله. التصميم أدناه ينشر خدمة HTTPS واحدة لكل موقع ولا شيء غيرها.
لماذا لا يمثل التقاطع المشكلة الحقيقية
يمرّر WireGuard بحسب الوجهة: تحدد AllowedIPs الخاصة بالنظير أي نظير يستقبل الحزمة. لا يمكن لنظيرين أن يعلنا 10.10.7.0/24 معاً دون أن يفوز أحدهما بكل بحث. هذه خاصية في جدول النظير لا في النفق، ولهذا فإن الحل هنا خطة عناوين وليس حيلة توجيه.
الخطة هي التوقف عن الإعلان عن الشبكة المحلية تماماً. أعلن بدلاً من ذلك عن اسم مستعار لكل خدمة، عنوان واحد لكل موقع، ودع بوابة كل موقع تترجم ذلك الاسم المستعار إلى تطبيقه الحقيقي.
المستخدم يوجّه عنوانين فريدين، ولا يرى أبداً البادئة الملتبسة، ولا يتغير شيء في أي من المكتبين. هذا يحدد الحدود بصدق أيضاً: أنت تنشر خدمة HTTPS واحدة من كل موقع، ولا تدمج شبكتين. تبقى القواعد التالية صغيرة لأن العبارة ضيقة. العبارة الأوسع تحتاج قواعد أوسع، وهكذا تصبح هذه التصاميم معقدة.
امنح كل موقع اسماً مستعاراً يستطيع المستخدم توجيهه
اختر عناوين من نطاق لا يتعارض مع أي شيء على جهاز المستخدم أو المحور أو أي من الشبكتين المحليتين. هنا المحور 10.66.0.1، والمستخدم 10.66.0.10، والبوابتان 10.66.0.21 و10.66.0.22، والاسمان المستعاران 10.77.1.20 و10.77.2.20. ينشر المحور كل اسم مستعار عبر النظير الذي يملكه، لذا فإن حزمة صادرة موجهة إلى اسم مستعار واحد لا يمكنها المغادرة إلا عبر الموقع المطابق:
[Interface]
Address = 10.66.0.1/32
ListenPort = 51820
PrivateKey = HUB_KEY
[Peer]
PublicKey = USER_PUBLIC_KEY
AllowedIPs = 10.66.0.10/32
[Peer]
PublicKey = SITE_A_PUBLIC_KEY
AllowedIPs = 10.66.0.21/32, 10.77.1.20/32
[Peer]
PublicKey = SITE_B_PUBLIC_KEY
AllowedIPs = 10.66.0.22/32, 10.77.2.20/32
يحتاج جانب المستخدم إلى الاسمين المستعارين فقط، ما يبقي إعداد العميل خالياً من بادئتي الشبكة المحلية. لا يذكر أي شيء في ذلك الإعداد أياً من الشبكتين الحقيقيتين:
[Interface]
Address = 10.66.0.10/32
PrivateKey = USER_KEY
[Peer]
PublicKey = HUB_PUBLIC_KEY
Endpoint = hub.example.net:51820
AllowedIPs = 10.77.1.20/32, 10.77.2.20/32
PersistentKeepalive = 25
كل بوابة تعلن عن المحور ومصدر المستخدم، لا شيء غير ذلك، لذا لا يعرف موقع أبداً شبكة الموقع الآخر المحلية. يحافظ Keepalive على تعيين NAT على وصلة رفع الموقع؛ وهو لا يصرّح بالمرور، ولا يزال الجدار الناري يقرر ما يُقبل. نسخ إعداد بوابة إلى الأخرى مقبول ما دام المفتاح الخاص وعنوان الواجهة والاسم المستعار كلها تتغير.
ما يجب أن يسمح به المحور بين نظيرين من نظائره
يمرّر المحور الحزم بين نظيرين من نظائره، وهذا قرار في سلسلة forward لا قرار إدخال. فعّل تمرير IPv4 عبر آلية المنصة المعتادة، ثم اسمح بالزوجين اللذين يحتاجهما التصميم فقط.
القاعدة أدناه تسمح للمستخدم بالوصول إلى اسم مستعار واحد ومنفذ واحد، وتسمح بحركة العودة عبر تتبع الاتصالات. وهي تُسقط كل شيء آخر في مسار forward، وهذا هو المقصود:
nft add table inet hub_transit
nft add chain inet hub_transit forward '{ type filter hook forward priority 0 ; policy drop ; }'
nft add rule inet hub_transit forward ct state established,related accept
nft add rule inet hub_transit forward iifname wg0 oifname wg0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.77.1.20 accept
تفصيلان يستحقان الذكر لأنهما يسببان إخفاقات صامتة. القبول في سلسلة أساسية لا يتجاوز الرفض في سلسلة أخرى، لذا ادمج هذا مع السياسة القائمة بدلاً من تحميله إلى جانبها. وعنوان المصدر يعرّف الجهاز لا الشخص؛ أبقِ مصادقة التطبيق منفصلة عن قبول الشبكة.
حيث يصبح الاسم المستعار عنواناً حقيقياً
يستقبل الموقع A طلباً إلى 10.77.1.20، ويعيد كتابة الوجهة إلى التطبيق الحقيقي، ويعيد كتابة المصدر إلى عنوان شبكته المحلية كي تعود الاستجابة دون مسار إلى النفق. إجراء الترجمتين في مكان واحد يُبقي بقية الشبكة جاهلة بالاسم المستعار. القواعد أدناه هي الآلية بأكملها:
nft add table ip site_a_nat
nft add chain ip site_a_nat pre '{ type nat hook prerouting priority -100 ; policy accept ; }'
nft add chain ip site_a_nat post '{ type nat hook postrouting priority 100 ; policy accept ; }'
nft add rule ip site_a_nat pre iifname wg0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.77.1.20 dnat to 10.10.7.20:443
nft add rule ip site_a_nat post iifname wg0 oifname lan0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.10.7.20 snat to 10.10.7.2
يجب أن تسمح سياسة forward في الموقع A بعد ذلك بالتدفق المترجم من واجهة النفق إلى واجهة الشبكة المحلية، إضافة إلى اتجاه العودة. يرى خطاف forward الوجهة بعد ترجمة الوجهة وقبل إعادة كتابة المصدر، ولهذا تذكر القاعدة عنوان التطبيق الحقيقي. اقرأ هذين الخطافين بهذا الترتيب كلما بدت قاعدة لا تطابق شيئاً. قاعدة forward مكتوبة مقابل الاسم المستعار لا يمكن أن تطابق أبداً، لأن الاسم المستعار لم يعد موجوداً عند ذلك الخطاف.
يكرر الموقع B هذا باسمه المستعار ومفتاحه. يجب ألا يعيد استخدام المفتاح الخاص للموقع A، لأن نظيرين يتشاركان هوية واحدة خطأ إعدادي يصعب رؤيته ويسهل إنشاؤه بنسخ ملف. أبقِ مستندي البوابة متطابقين باستثناء القيم التي يجب أن تختلف، حتى يبرز الفرق عند المراجعة. الملف المنسوخ هو الأصل المعتاد لذلك الخطأ.
لإعادة كتابة المصدر ثمن. يرى التطبيق البوابة كعميله، لذا لم تعد سجلاته تُظهر أي مستخدم اتصل. أبقِ سجل التدقيق الخاص بالبوابة، ولا تُضعف تسجيل الدخول في التطبيق لتعويض هوية شبكية أزالتها الترجمة.
كيف تثبت أن الاسمين المنشورين يستجيبان
امنح كل اسم مستعار اسماً في DNS واضبط التطبيق والشهادة لذلك الاسم، لأن الترجمة لا تعيد كتابة إعادات التوجيه ولا عناوين URL المطلقة المضمّنة. استخدم اسماً واحداً لكل موقع كي لا يُفهم عدم تطابق الشهادة أبداً على أنه خطأ توجيه.
اختبر من العميل الحقيقي، وتأكد أي موقع أجاب، واختبر منفذاً غير مصرّح به أيضاً. استخدم اتصالات جديدة بعد أي تغيير في NAT، لأن الحالة القائمة تحتفظ بالتعيين القديم:
wg show wg0 allowed-ips
sudo nft list ruleset
curl -sS -o /dev/null -w "%{http_code}\n" https://site-a.example.net/
اسم مستعار فارغ أو غير متوقع في الأمر الأول يعني أن اختيار النظير خطأ قبل أن تهم أي قاعدة جدار ناري. إذا كان الاسم المستعار صحيحاً وفشل الاسم رغم ذلك، فقد انتقلت المشكلة إلى DNS والشهادات. بمجرد أن يستجيب الاسمان ويبقى المنفذ الثالث مغلقاً، يفعل التصميم ما يدّعيه: خدمتان منشورتان بشكل ضيق، لا شبكتان محليتان مدموجتان.