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

تنها پورتی که نمی‌توان بست

شنونده VPN باید اتصال‌ها را از نشانی‌هایی بپذیرد که نمی‌توانید پیش‌بینی کنید، پس به‌حکم تعریف یک سرویس عمومی است. این تنها استثنایی است که طراحی بر آن بنا شده: یک پورت UDP برای OpenVPN، یک پورت UDP برای WireGuard، یک پورت TCP برای یک VPN مبتنی بر SSL. هر چیز دیگری به پشت تونل تعلق دارد، یعنی سرویس‌های برنامه باید به نشانی تونل یا به loopback متصل شوند، نه به همه رابط‌ها.

اما اتصال، اعمال نیست. فرایندی که به 0.0.0.0 متصل است تنها وقتی قابل دسترسی است که قاعده‌ای آن را بپذیرد، و سیاست پیش‌فرض جایگزین ترافیکی است که با هیچ قاعده‌ای مطابقت نکرده، نه قاعده‌ای که پورت را اداره می‌کند. یک پذیرش که بالای ردِ شما گذاشته شود همچنان پذیرش است، و بیرون بردن یک سرویس از نشانی عمومی بهبودی مستقل است نه جانشین زنجیره.

ورود، ارسال و NAT به پرسش‌های متفاوتی پاسخ می‌دهند

ترافیک ورودی به خود میزبان مقصد دارد، پس شنونده VPN و مسیر مدیریتی همان‌جا زندگی می‌کنند. ترافیک ارسال از میزبان می‌گذرد و کلاینت‌های تونل به آن نیاز دارند، به‌همراه net.ipv4.ip_forward=1. پیکربندی کلاسیک نیمه‌خراب جریان خروجی را مجاز می‌کند و جهت بازگشت را فراموش می‌کند، پس کلاینت دست‌دادن را کامل می‌کند و بعد چیزی بار نمی‌شود؛ یک قاعده conntrack برای ترافیک برقرار و مرتبط آن جهت را پوشش می‌دهد.

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

IPv6 سیاست دومی است، نه فکری پس از واقع. میزبانی با زنجیره سخت‌گیرانه IPv4 و سیاست باز در ip6tables چیزی را نبسته است؛ سرویس‌ها هنوز از طریق خانواده دیگر در دسترس‌اند. یا مجموعه قواعد را با ip6tables آینه کنید یا IPv6 را عامدانه روی رابط عمومی غیرفعال کنید، و بنویسید کدام را انتخاب کرده‌اید.

پایه‌ای که در یک گذر خوانده می‌شود

با رد کردن شروع کنید و بعد فقط آنچه میزبان لازم دارد را بیفزایید. نخست loopback و پاسخ‌ها، بعد مسیر مدیریتی، بعد شنونده تونل، بعد سیاست ارسال برای کلاینت‌ها:

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p udp --dport 1194 -m conntrack --ctstate NEW -j ACCEPT
sysctl -w net.ipv4.ip_forward=1
iptables -A FORWARD -i tun0 -o eth0 -s 10.8.0.0/24 -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

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

بستن آخرین پورت بدون از دست دادن میزبان

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

تغییر را از نشستی اعمال کنید که از قبل برقرار است، نه از طریق اتصالی که می‌خواهید فیلترش کنید. یک بازگشت زمان‌بندی کنید تا خطا منقضی شود نه اینکه دائمی شود، برای نمونه کاری با systemd-run --on-active=10min که مجموعه قواعد پیشین را بازمی‌گرداند، و در حین آزمودن نشست اول، نشست دوم SSH را باز نگه دارید. بعد بررسی کنید که کار بازگشت واقعاً اجرا می‌شود، چون بازیابی آزموده‌نشده همانند نبودن بازیابی است.

مسیر را بررسی کنید، نه اسکریپت را

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

iptables -S
iptables -t nat -S
ss -lntup
ip -4 addr show

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

یک عادت بیش از مجموعه قواعد ارزش دارد: پس از هر تغییر، مسیر ارسال را با درخواست یک کلاینت و مسیر بازیابی را با نشست دوم ثابت کنید. قفل‌شدنی که به کار نبرده‌اید یک فرض است.

مقالات مرتبط