چگونه یک کلاینت OpenVPN پشت NAT به خروجی اینترنت تبدیل میشود
خروجی خودش به بیرون وصل میشود، پس روترش به هیچ پورت ورودی نیاز ندارد. دنبال کنید که هاب چه چیزی را مسیریابی میکند، خروجی چه چیزی را هدایت میکند و مسیر بازگشت از کجا میآید.
سه میزبان Linux، یک طراحی: یک هاب OpenVPN با دسترسی عمومی، یک لپتاپ مصرفکننده و یک ماشین خروجی پشت یک روتر خانگی معمولی که چیزی را به داخل هدایت نمیکند. خروجی خودش با هاب تماس میگیرد. سپس ترافیک اینترنت مصرفکننده در لپتاپ وارد تونل میشود و از لینک بالادستی خود خروجی بیرون میرود. هر چیز شگفتانگیز در این چیدمان از یک جزئیات میآید: خروجی اتصال را آغاز کرد، پس مسیر به داخل از قبل وجود دارد و برای ساختنش به هدایت پورت نیازی نیست.
آنچه در ادامه میآید مسیر بسته را دنبال میکند نه ترتیب پیکربندی را، چون پیکربندی تنها وقتی معنا دارد که بدانید کدام مؤلفه کدام تصمیم را میگیرد. هر لایه دقیقاً یک تصمیم دارد و خطاها آنجا جمع میشوند که دو لایه یکی فرض شوند.
دو نشست انتقال، یک مسیر درونی
هر میزبان اتصال بیرونی خودش را به هاب نگه میدارد و هیچیک از این اتصالها از دیگری خبر ندارد. ترافیک مصرفکننده به اینترنت یک جریان درونی جداست که اتفاقاً داخل تونل مصرفکننده حمل میشود و سپس از تونل خروجی دوباره بیرون داده میشود. هاب باید همین جریان درونی را مسیریابی کند و خروجی باید همان را هدایت کند.
outer: consumer --TLS--> hub
outer: exit --TLS--> hub
inner: consumer --tun--> hub --internal--> exit --tun--> uplink
چون هر دو اتصال بیرونی از سمت کلاینت به بیرون میروند، روتر خروجی هرگز SYN ورودی نمیبیند و هرگز به قاعده NAT مقصد نیاز ندارد. فقط باید انتقال برقرارشده را زنده نگه دارد، کاری که برای هر اتصال طولانیمدت انجام میدهد. هیچچیز در اینترنت عمومی لازم نیست اول به خروجی برسد، به همین دلیل آدرس پویا روی آن لینک بالادستی مشکلی نیست. روتر هنوز باید نشست خروجی را مجاز کند، اما این همان اجازهای است که مرورگر از قبل استفاده میکند.
iroute در واقع چه چیزی را داخل سرور نصب میکند
هاب تنها دستگاهی است که تصمیم میگیرد کدام همتا مالک کدام مقصد است، و این تصمیم را در جدول مسیریابی فضای کاربر خود میگیرد، نه در جدول هسته. یک ورودی client-config-dir برای خروجی با نام exit-node میتواند پیشوند پیشفرض را آنجا مطالبه کند:
# /etc/openvpn/server/ccd/exit-node
iroute 0.0.0.0 0.0.0.0
آن خط به فرایند سرور میگوید "بستههای درونی با هر مقصدی متعلق به این کلاینت هستند". این خط مسیر پیشفرضی به سیستمعامل هاب اضافه نمیکند و هاب را به دروازه اینترنت تبدیل نمیکند. مرجع OpenVPN 2.6 مسیریابی درونی مخصوص کلاینت و مسیریابی هسته را بهعنوان دو سازوکار جدا نگه میدارد، و همین جدایی دلیل کارکرد این طراحی بدون دست زدن به مسیر پیشفرض هاب است.
مصرفکننده به دستور آینهای نیاز دارد که بهصورت push تحویل شود تا ترافیک خودش وارد تونل شود نه اینکه از دروازه محلی بیرون برود. با توپولوژی زیرشبکه، استخر یک مسیر متصل روی دستگاه tun خروجی است، پس جهت بازگشت به push اضافی نیاز ندارد؛ چیزی که خروجی نباید دریافت کند تغییرمسیر زیر است، که ترافیک بالادستی خودش را به تونلی برمیگرداند که میخواهد از آن استفاده کند. آن را فقط به مصرفکننده بفرستید و مسیرهای اعمالشده را روی کلاینت بخوانید، نه اینکه به انتخاب فایل CCD اعتماد کنید.
# /etc/openvpn/server/ccd/consumer-01
push "redirect-gateway def1"
هسته خروجی هدایت را انجام میدهد
داخل خروجی، بستهای از مصرفکننده روی رابط tun میرسد که به یک میزبان اینترنت نشانهگذاری شده است. هسته جدول مسیریابی خود را بررسی میکند، لینک بالادستی واقعی را بهعنوان بهترین تطابق مییابد و سپس دو سیاست مستقل اعمال میکند: آیا این بسته اصلاً قابل هدایت است و آیا میتواند با آن آدرس مبدأ از این رابط خارج شود. هدایت یک sysctl است و در بسیاری از توزیعها بهطور پیشفرض خاموش است.
پس خروجی به هدایت IPv4 بهصورت پایدار، قاعدهای که مبدأهای تونل مصرفکننده را به سمت لینک بالادستی بپذیرد و مسیر پاسخ از طریق tun نیاز دارد. فعال کردن هدایت برای کل میزبان رفتار هر چیز دیگری را هم که از آن مسیریابی میشود تغییر میدهد، به همین دلیل قاعده باید مبدأها و رابطها را نام ببرد نه اینکه همه را بپذیرد.
ترجمه مبدأ همان چیزی است که مسیر بازگشت را میخرد
میزبان اینترنت هیچ مسیری به آدرس 10.90.0.x ندارد، پس پاسخها پیش از رسیدن به خروجی دور ریخته میشدند. بازنویسی مبدأ هنگام خروج بسته از لینک بالادستی این را حل میکند و ردیابی اتصال بازنویسی را در بازگشت برمیگرداند.
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.90.0.0/24 -o enp1s0 -j MASQUERADE
ip -4 route get 10.90.0.9 # run on the exit: must point at the tun
قاعده بالا نقطه شروعی برای یک رابط بالادستی است، نه سیاست دیوار آتش. مبدأ را به تخصیصهایی که واقعاً مجاز کردهاید محدود کنید و سیاست ورودی خود خروجی را جدا نگه دارید تا پوشاندن ترافیک تونل بهآرامی سرویسهای محلی را آشکار نکند. روی میزبانی با آدرس بالادستی پویا، پوشاندن شما را از ردیابی دستی آدرس هم بینیاز میکند.
کجا هاب میتواند اعمال کند و کجا نمیتواند
با client-to-client و مسیر داده سنتی فضای کاربر، هاب بستههای درونی را بین همتاها داخل فرایند سرور جابهجا میکند. آنها هرگز زنجیره FORWARD هسته هاب را طی نمیکنند، پس قاعدهای در هسته آنجا نمیتواند عبور همتا به همتا را محدود کند، هرقدر هم با دقت نوشته شده باشد. اعمال باید در سیاست خود سرور زندگی کند: چه کسی مجاز به اتصال است، کدام فایل CCD را میگیرد و آیا مالکیت پیشفرض خروجی اصلاً داده میشود.
به همین دلیل iroute پیشفرض یک تصمیم اعتماد گسترده است. خروجی باید بهطور مشروع ترافیک بازگشتی از آدرسهای دلخواه اینترنت را بپذیرد و باید ماشینی باشد که شما کنترل، وصله و پایش میکنید. دادن همان مالکیت به همتایی که روتر به او نمیسپارید همان خطایی است که این طراحی به آن دعوت میکند.
عضویت کنترل واقعی است. هر کلاینتی که بتواند مسیرهای خودش را پیکربندی کند میتواند تلاش کند از خروجی استفاده کند، پس محدود کردن اینکه چه کسی میتواند وصل شود یا خروجی به کدام همتاها خدمت کند بخشی از طراحی است نه فکری بعدی.
شکست ایمن وقتی خروجی ناپدید میشود
خروجی را آفلاین کنید و مالکیت پیشفرض درونی همراه نشستش ناپدید میشود. چیزی که نباید رخ دهد این است که هاب بیصدا خروجی جایگزین شود: هیچ مسیر پیشفرضی از استخر VPN به لینک بالادستی هاب، و هیچ قاعده پوشاندن گستردهای روی هاب که با کمال میل همان ترافیک را هدایت کند. آن شکست را عمداً آزمایش کنید و سپس بازیابی را با جریانی تازه بسنجید، نه با صفحهای که از قبل باز بود.
# on the consumer, with the exit up and then down
ip -4 route get 1.1.1.1
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com
اگر ترافیک مصرفکننده پس از رفتن خروجی هنوز به اینترنت میرسد، چیز دیگری آن را حمل میکند و مرزی که در نظر داشتید وجود ندارد. با DNS و IPv6 هم بهعنوان تصمیمهای جدا رفتار کنید: مسیر IPv4 بههمراه پوشاندن چیزی درباره ترافیک AAAA نمیگوید، و resolverی که هاب هل میدهد از همانجایی پاسخ میدهد که پرسوجو واقعاً خارج میشود.