چرا یک کانتینر Docker به میزبانهای پشت VPN نمیرسد
کانتینرهای پل فضای نام خود را دارند، بنابراین تونل میزبان برای آنها نامرئی است. پیش از ویرایش DNS، شبکه میزبان، دستگاه tun و macvlan را مقایسه کنید.
کانتینری که به هیچ چیز پشت VPN سازمانی نمیرسد معمولاً در فضای نام شبکهای متفاوت از تونل زندگی میکند. مسیرها، سرورهای DNS و نشانههای دیوار آتش کلاینت میزبان به پشته میزبان تعلق دارند؛ کانتینر شبکه پل پشته خود را دارد. همین یک واقعیت بیشتر گزارشهایی را که به تیم پلتفرم میرسد توضیح میدهد، و همچنین دلیل آن است که «VPN خراب است» و «Docker از VPN دور میزند» میتوانند همزمان نادرست باشند. دو پیکربندی بسیار متفاوت پشت یک نشانه پنهاناند، پس پیش از تغییر هر تنظیمی مشخص کنید کدام را دارید.
بفهمید تونل متعلق به فضای نام کیست
در Docker Desktop برای macOS یا Windows کانتینرها درون یک ماشین مجازی Linux اجرا میشوند. کلاینت VPN نصبشده روی رایانه رومیزی تونلی در سیستمعامل رومیزی میسازد و ماشین مجازی هرگز آن را نمیبیند. هیچ پیکربندی کانتینری این را درست نمیکند، زیرا رابطی که کانتینر به آن نیاز دارد اصلاً در فضای نام شبکه آن وجود ندارد.
روی میزبان Linux تونل در فضای نام میزبان زندگی میکند و کانتینرهای پل معمولاً از طریق جدول مسیریابی میزبان با اعمال NAT مبدأ در خروج به آن میرسند. وقتی این مسیر شکست میخورد، علت معمولاً سیاست ارسال، فیلتر مسیر معکوس یا یک زیرشبکه همپوشان است، نه مسیر گمشده. پیش از خواندن بیشتر تأیید کنید در کدامیک از دو جهان هستید:
docker info --format '{{.OperatingSystem}}'
docker run --rm --net=host alpine ip route | head -5
docker run --rm alpine ip route | head -5
تفاوت میان این دو جدول مسیریابی کل مشکل در یک صفحه است. اگر نمای میزبان رابط تونل را داشته باشد و نمای پل نداشته باشد، شما به جداسازی فضای نام نگاه میکنید و بقیه چیزها انتخابی درباره چگونگی عبور از آن است.
شبکه میزبان یک آزمون است، نه یک طراحی
اجرای کانتینر با شبکه میزبان، که Docker آن را بهعنوان اشتراک فضای نام شبکه میزبان مستند میکند، باعث میشود رابط تونل و مسیرهایش را مستقیماً به ارث ببرد. اگر همان درخواست پس از آن موفق شود، نظریه مسیر تأیید میشود و شکست، جداسازی فضای نام است نه اعتبارنامه، DNS یا یک قاعده دسترسی.
docker run --rm --net=host curl -sS -o /dev/null -w '%{http_code}\n' https://api.internal/health
آن را بهعنوان ابزار تشخیص نگه دارید. شبکه میزبان از انتشار پورت و بیشتر جداسازیای که از کانتینر میخواستید صرفنظر میکند، و در Docker Desktop منظور از «host» ماشین مجازی است نه رایانه رومیزی، پس موفقیت یا شکست در آنجا میتواند اگر فراموش کنید کدام میزبان را خطاب کردهاید گمراهتان کند.
دستگاه tun و امتیازاتی که با آن میآید
کلاینت VPN درون کانتینر به یک دستگاه تونل و اجازه پیکربندی آن نیاز دارد. میانبر رایج --privileged است که همه دستگاهها را میدهد و سیاست امنیتی را در سراسر کانتینر شل میکند. این بسیار بیشتر از نیاز این کار است و توجیه آن در یک بازبینی دشوار است.
sudo modprobe tun
docker run --rm -it --device /dev/net/tun --cap-add NET_ADMIN myvpn-client
شکل تنگتر تنها گره دستگاه را نگاشت میکند و فقط توانایی مدیریت شبکه را اضافه میکند. ابتدا ماژول را روی میزبان بارگذاری کنید تا گره وجود داشته باشد. اگر کانتینر راهاندازی شود و کلاینت گزارش دهد که نمیتواند دستگاه تونل را باز کند، پیش از مشکوک شدن به نمایه، نگاشت را بررسی کنید، زیرا آن خطا معمولاً تحتاللفظی است.
این طراحی همچنین یعنی کلاینت برای هر کانتینر اجرا میشود. این پاسخ درست است وقتی هر بار کاری به تونل خود نیاز دارد، و پاسخ نادرست وقتی فقط میخواستید تونل میزبان قابل مشاهده باشد، که تیم پلتفرم احتمالاً فرض میکرد شما همین کار را میکنید.
NAT پل مبدأیی را که VPN میبیند تغییر میدهد
با پل پیشفرض، ترافیک خروجی کانتینر به نشانی میزبان ترجمه میشود. بنابراین پشت VPN طرف سازمانی نشانی تونل میزبان را میبیند نه نشانی کانتینر، قواعد دسترسی بر پایه مبدأ طوری رفتار میکنند که گویی میزبان درخواست را فرستاده، و دو کانتینر برای سرور VPN از هم قابل تشخیص نیستند.
macvlan یا ipvlan در عوض به کانتینر نشانی LAN میدهد، که به کشف در LAN کمک میکند و کانتینر را مانند همتایی روی قطعه نشان میدهد. همچنین تونل میزبان را کاملاً دور میزند، پس چنین کانتینری به پیشوندهای فقط-VPN نمیرسد مگر اینکه VPN از همان LAN هم در دسترس باشد.
هر دو شکل مشروعاند؛ به نیازهای متفاوتی پاسخ میدهند. اگر نیاز این است که «این کانتینر باید به سرویسی فقط-VPN برسد»، بین مسیریابی از طریق میزبان و پایان دادن تونل درون کانتینر انتخاب میکنید. این انتخاب را پیش از استقرار بنویسید، نه پس از نخستین درخواست ناموفق. هر کدام را انتخاب کردید، نشانی مبدأیی را که طرف دور خواهد دید ثبت کنید، زیرا همان چیزی است که قواعد دسترسی و گزارشهایش نشان خواهند داد. طراحیای که نمیتواند آن نشانی را بیان کند کامل نیست.
DNS از دیمن پیروی میکند، نه از تونل
کانتینر پیکربندی resolver خود را هنگام شروع دریافت میکند، کپیشده از دیمن یا از تصویر. نشانیهایی که VPN پس از اتصال هل میدهد برای آن کپی خیلی دیر میرسند و به resolver میزبان هل داده میشوند نه به درون کانتینر.
resolver را برای سرویس صریحاً تنظیم کنید، یا آن را به یک resolver داخلی که از طریق تونل در دسترس است اشاره دهید. کانتینری که یک نام خصوصی را به نشانی عمومی تبدیل میکند معمولاً از resolver عمومی استفاده میکند، نه اینکه از مسیر خراب رنج ببرد، و اصلاح به فایل compose یا پیکربندی دیمن تعلق دارد.
همین استدلال برای مسئله نشانی خروج نیز صدق میکند. اگر هر کانتینر باید از نشانی VPN متفاوتی ظاهر شود، یک تونل میزبان نمیتواند آن را فراهم کند چون NAT آنها را در یک مبدأ جمع میکند. شکلهای عملی یک کلاینت در فضای نام هر کانتینر یا یک sidecar که مالک تونل است و پشتهاش را به اشتراک میگذارد هستند، و هر دو نحوه نشانیدهی برنامه را تغییر میدهند.
فهرست بررسی پیش از بستن تیکت: فضای نامی را که مالک تونل است مشخص کنید، مسیر را با شبکه میزبان اثبات کنید، فقط دستگاه tun مورد نیاز را نگاشت کنید، نشانی مبدأ پس از NAT را به یاد داشته باشید، resolver را صریحاً تنظیم کنید و نشانیهای خروج هر کانتینر را تصمیمی معماری بدانید نه یک پرچم.