میزبان پس از بالا آمدن VPN با تونل کامل همچنان به SSH پاسخ می‌دهد، اما سرویسی که با Docker روی همان ماشین منتشر شده ساکت می‌شود. مشکلی در شنونده نیست: کانتینر در حال اجراست، نگاشت پورت وجود دارد و بسته می‌رسد. کانتینر سالم است و درخواست واقعاً به آن می‌رسد. تفاوت در این است که پاسخ هنگام انتخاب مسیر توسط Linux از کجا شروع می‌شود، و همین تفاوت تعیین می‌کند کدام‌یک از دو طراحی زیر را لازم دارید.

تاپل را از NAT مقصد دنبال کنید

میزبانی را در 192.0.2.10 در نظر بگیرید که TCP 2222 را به کانتینری در 172.30.240.5:22 منتشر می‌کند. درخواستی از 198.51.100.25 پیش از رسیدن به کانتینر ترجمه می‌شود و کانتینر به‌عنوان خودش پاسخ می‌دهد.

WAN request:      198.51.100.25 -> 192.0.2.10:2222
after DNAT:       198.51.100.25 -> 172.30.240.5:22
container reply:  172.30.240.5:22 -> 198.51.100.25

در لحظهٔ تصمیم مسیر در میزبان، پاسخ هنوز آدرس مبدأ کانتینر را حمل می‌کند. ترجمهٔ معکوس بعداً تاپل منتشرشده را بازمی‌گرداند و به همین دلیل بسته دقیقاً در همان لحظه‌ای که مسیر انتخاب می‌شود اشتباه به نظر می‌رسد.

چرا قاعده‌ای که فقط برای میزبان است آن را از دست می‌دهد

قاعده‌ای که جدول WAN را فقط برای بسته‌های 192.0.2.10 انتخاب می‌کند، دیمن‌های خود میزبان را پوشش می‌دهد و این پاسخ فورواردشده را کاملاً از دست می‌دهد. آدرس مبدأ روی پاسخ 172.30.240.5 است، نه میزبان.

اصلاح بیش‌ازحد این است که همه‌چیز را از کانتینر از طریق WAN مسیریابی کنید، که اتصالات خروجی بی‌ربط کانتینر را نیز از VPN معاف می‌کند. این سیاستی متفاوت از آن چیزی است که خواسته شده بود و معمولاً بعداً کسی را غافلگیر می‌کند.

تطبیق پس از DNAT یک قلاب دیگر است

مستندات Docker دربارهٔ دیوارهای آتش اشاره می‌کند که فیلتر معمول کاربر مقصد پس از DNAT را می‌بیند، بنابراین تطبیق پورت منتشرشدهٔ 2222 در یک قاعدهٔ عادی با تطبیق پورت 22 کانتینر یکی نیست. اگر تاپل اصلی مهم است، از سازوکار پشتیبانی‌شدهٔ مقصد اصلی استفاده کنید، نه اینکه حدس بزنید کدام پورت قابل مشاهده است.

این سردرگمی قلاب‌ها قواعدی می‌سازد که درست به نظر می‌رسند و هرگز تطبیق نمی‌یابند. پیش از بازنویسی سیاست، شمارنده‌های خود قاعده را بررسی کنید. قاعده‌ای با صفر برخورد به شما می‌گوید جایی ارزیابی می‌شود که بسته هرگز به آن نمی‌رسد.

طراحی A: حمل تصمیم به‌عنوان وضعیت اتصال

یک نشانهٔ اتصال می‌تواند تصمیم ورودی را از میان ترجمه حمل کند، اما باید پیش از جست‌وجوی مسیری که اهمیت دارد به بسته بازگردانده شود. مراحل عبارت‌اند از:

  1. یک فیلد نشانه و اولویت قاعده رزرو کنید که با Docker، VPN یا مالک سیاست دیگری برخورد نکند.
  2. تنها اتصال WAN جدید موردنظر را در نقطه‌ای پیش از DNAT که ردیابی اتصال در آن موجود است شناسایی کنید.
  3. تصمیم را روی نشانهٔ اتصال ثبت کنید، بدون اینکه خود درخواست اصلی را به جدول WAN مجبور کنید.
  4. در جهت پاسخ، پیش از مسیریابی فقط بیت‌های نشانهٔ خودتان را به بسته بازگردانید.
  5. جدول WAN کامل را به‌اندازهٔ کافی زود انتخاب کنید تا قاعدهٔ VPN نتواند اول برنده شود.

آن جدول به مسیرهای متصل واقعی و درگاه نیاز دارد. همچنین به رفتار مشخصی نیاز دارد وقتی مسیر وجود ندارد. افتادن خاموش در تونل بدترین نتیجه است، چون پیکربندی هنوز کار می‌کند به نظر می‌رسد. به‌جای آن بسته شکست بخورید و آن را ثبت کنید.

طراحی B: پایان دادن روی میزبان

یک فرانت‌اند که به‌طور عمدی در سمت میزبان پیکربندی شده، مانند یک پروکسی TCP یا یک پروکسی معکوس در جلوی کانتینر، پاسخ را از آدرس میزبان سرچشمه می‌دهد. آنگاه قواعدی که برای میزبان نوشته شده‌اند همان‌گونه که انتظار می‌رود عمل می‌کنند و کانتینر روی شبکهٔ خصوصی خودش می‌ماند.

هزینه، یک قطعهٔ متحرک دیگر با پیکربندی، مدیریت آدرس مبدأ و کنترل‌های دسترسی خودش است. معمولاً همان مرز ساده‌تر پشتیبانی‌شده است و اغلب پاسخ درست است وقتی سیاست مسیریابی را فردی نگه می‌داشت که ممکن است برود.

تغییر را بررسی کنید، نه نیت را

از سمت عمومی یک اتصال مجاز تازه باز کنید و تأیید کنید که پاسخ از رابط موردنظر خارج می‌شود. سپس بررسی کنید که یک درخواست بی‌ربط که کانتینر آغاز کرده همچنان از VPN پیروی می‌کند.

برای آزمودن یک قاعده جدول اتصالات ماشین را پاک نکنید: ورودی‌های موجود نگاشت قبلی خود را حفظ می‌کنند و پاک کردن همه‌چیز دیگر روی میزبان را می‌شکند. انتشار یک پورت دسترس‌پذیری را تعریف می‌کند. مسیر پاسخ را تعریف نمی‌کند. طراحی انتخاب‌شده را همراه با نام جدول‌هایش بنویسید تا نفر بعد در هنگام یک رخداد آن را دوباره کشف نکند.

مقالات مرتبط