چرا قاعده iptables شما هرگز ترافیک VPN پلشده را نمیبیند
قابهای پلشده میتوانند زنجیره IP را که زیر نظر دارید دور بزنند. پیش از افزودن قاعده دیگری، خانواده bridge بومی را با br_netfilter قدیمی مقایسه کنید.
دو ماشین مجازی روی یک میزبان از طریق یک پل به LAN میرسند، و قاعدهای که در زنجیره ip forward قرار گرفته، شمارندهای را نشان میدهد که هرگز حرکت نمیکند. قابها واقعیاند، ترافیک در حال عبور است، و قاعده بهسادگی روی مسیر آن نیست. مقایسه دو روشی که Linux ترافیک پلشده را آشکار میکند این سکوت را توضیح میدهد، و همچنین توضیح میدهد چرا بارگذاری یک ماژول قدیمی پاسخ بدتری از آن چیزی است که به نظر میرسد.
دو مسیر از میان یک میزبان
بسته میتواند به دو روش بسیار متفاوت از این میزبان خارج شود، و این تفاوت تعیین میکند کدام هوکها آن را میبینند. اگر مقصد آنسوی یک درگاه فیزیکی باشد و قاب میان دو درگاه پل سوئیچ شود، میزبان یک قاب Ethernet را هدایت میکند؛ کد مسیریابی مورد پرسش قرار نمیگیرد و هیچ جهش مسیریابیشدهای رخ نمیدهد. اگر مقصد خود میزبان باشد، یا شبکهای که از طریق یک رابط تونل بهدست میآید، بسته مسیریابی میشود و هوکهای لایه IP واقعاً اعمال میشوند. این دو شاخه را در ذهن نگه دارید، زیرا هر پرسش بعدی درباره زنجیرهها و خانوادهها از این ناشی میشود که قاب کدام یک از دو مسیر را انتخاب میکند.
این تمایز تمام داستان پشت قاعدهای است که هیچ چیز را نمیشمارد. یک قاب پلشده از veth9a1 به enp3s0 از پل میگذرد، نه از پشته IP، بنابراین زنجیره ip forward چیزی برای تطبیق نمیبیند. نامهای رابط در یک مثال قدیمیتر تعیین نمیکند کدام مسیر را دارید، و به همین دلیل گام اول این است که به توپولوژی نگاه کنید نه به مجموعه قواعد.
تونل یک مورد سوم را اضافه میکند که شایسته نامگذاری است. یک دستگاه TUN بستههای IP را به میزبان تحویل میدهد، پس آنها مسیریابی میشوند و واقعاً از زنجیرههای IP عبور میکنند، در حالی که یک دستگاه TAP متصل به پل قابها را تحویل میدهد و مانند حالت پل رفتار میکند. نوع دستگاه، نه نام محصول، تعیینکننده است.
دو خانوادهای را که میتوانند یک قاب پلشده را ببینند مقایسه کنید
Linux فیلتر کردن بومی پل را از طریق خانواده bridge در nftables پشتیبانی میکند، و برای بازرسی و دور انداختن قابها در حال حرکت به هیچ ماژول قدیمی نیاز ندارد. خود مستندات پل کرنل این توصیه را مستقیماً ارائه میدهد، که دانستن آن پیش از آنکه نتیجه یک جستوجو به شما بگوید ماژولی بارگذاری کنید ارزش دارد.
| Observation point | What it sees | What it costs |
|---|---|---|
nftables bridge family chains | Ethernet frames forwarded between bridge ports, in both directions | Rules must be written in bridge syntax with link-layer matches |
Legacy br_netfilter plus an ip chain | Bridged IPv4 and IPv6 packets, re-exposed through IP-layer hooks | Existing host rules may begin matching traffic they never saw before |
inet chain for routed traffic | Packets the host routes, including traffic arriving from a TUN device | No visibility into purely switched frames |
استفاده از فرمان nft بهجای iptables بهخودیخود قاعدهای را در خانواده درست قرار نمیدهد. زنجیرهای که در خانواده bridge اعلام شده قابها را میبیند؛ زنجیرهای که در ip، ip6 یا inet اعلام شده آنها را نمیبیند، مهم نیست کدام فایل دودویی آن را ساخته باشد. خانواده ویژگی زنجیره است، نه ابزاری که آن را تایپ کرده است.
مسیر قدیمی متفاوت کار میکند و همین دلیل آن است که باید مقایسه کرد نه اینکه از روی عادت طرفی را انتخاب کرد. بارگذاری br_netfilter و فعال کردن تنظیمات bridge-nf-call ترافیک IPv4 و IPv6 پلشده را دوباره از طریق هوکهای لایه IP آشکار میکند، تا یک قاعده معمولی iptables سرانجام بتواند آن را تطبیق دهد. قابها تغییر نمیکنند؛ مجموعه هوکهایی که از آن عبور میکنند تغییر میکند، که یعنی هر قاعده موجود میزبان اکنون مخاطبان تازهای دارد.
پیش از افزودن قاعده، مسیر مؤثر را بخوانید
سه فرمان مشخص میکنند کجا هستید: پل به چه چیزی متصل است، آیا ماژول قدیمی فعال است، و چه زنجیرههایی از قبل در خانواده bridge وجود دارند. پیش از نوشتن هر چیزی آنها را اجرا کنید، زیرا پاسخ تعیین میکند کدام یک از دو طرح را گسترش میدهید. رد کردن این گام همان روشی است که یک قاعده درست در زنجیرهای میافتد که هیچ چیز از آن عبور نمیکند.
bridge link show
lsmod | grep br_netfilter
nft list ruleset | grep -A4 "chain forward"
خروجی موارد را بهروشنی جدا میکند. درگاههایی که زیر پل فهرست شدهاند به شما میگویند کدام رابطها سوئیچ میشوند؛ نتیجه خالی lsmod یعنی هیچگونه دیدی در لایه IP از قابهای پلشده وجود ندارد؛ یک زنجیره forward موجود در خانواده bridge یعنی صاحب سیاست از قبل وجود دارد و قاعده شما به آنجا تعلق دارد نه به زنجیره تازه خودش.
سپس قاعدهای اضافه کنید که خواندن دوبارهاش آسان باشد، با یک شمارنده و بدون تغییر حکم، و یک جریان نماینده را از پل عبور دهید. شمارندهای که حرکت میکند ثابت میکند قاب به قاعده رسیده است. همین تنها چیزی است که ثابت میکند، پس مسیر ردشده را هم دنبال کنید، چون سیاستی که همه چیز را میپذیرد هم شمارنده متحرک تولید میکند.
nft add rule bridge filter forward iifname "veth9a1" oifname "enp3s0" counter acceptکجا ماژول قدیمی رفتار را تغییر میدهد
فعال کردن مسیر قدیمی روی یک پل در حال کار، کرداری خنثی در افزودن دید نیست. قواعدی که پیشتر برای ترافیک پلشده دستنیافتنی بودند دستیافتنی میشوند، از جمله قواعد میزبان که برای رابط دیگری نوشته شدهاند، و یک سیاست drop پیشفرض در زنجیره ip forward میتواند ناگهان بر قابهایی اعمال شود که قبلاً دستنخورده عبور میکردند. روی میزبانی که از طریق همان پل مدیریت میشود، نتیجه قفلشدن بدون کنسول است. این یک حالت خرابی فرضی نیست؛ پیامد معمولی گسترش مجموعه هوکها روی ماشینی است که از مسیری به آن میرسید که همین حالا گسترش دادهاید.
توالی ایمنتر این است که تصمیم بگیرید کدام خانواده مالک سیاست است، قواعد را همانجا بنویسید، و پیش از تغییر هر sysctl یک مسیر بازیابی مستقل نگه دارید. اگر دو خانواده هر دو قاعده دارند، مستند کنید کدام مرجع است؛ دو سیاست همپوشان همان روشی است که یک بسته در یک جا پذیرفته و در جای دیگر دور انداخته میشود بدون یک فایل واحد برای خواندن.
جداسازی خودش هنوز طرحی کامل میخواهد. اجازه دادن یک منبع به یک مقصد فقط ثابت میکند که یک جریان کار میکند، در حالی که نیاز معمولاً این است که یک VM نتواند به همسایگان یا میزبان برسد. شمارندههای قواعد رد مهمتر از شمارنده قاعده پذیرش هستند، چون شمارنده پذیرش همانی است که از قبل انتظار داشتید حرکت کند.
در پایان، حالت ورودی میزبان را جدا نگه دارید. ترافیکی که به IP خود پل نشانه رفته به input میرسد نه forward، و قاعده در forward هرگز آن را نمیبیند، هر خانوادهای که انتخاب کنید. سه مورد، دو خانواده، یک درس: مسیر هوک را تعیین میکند، و هوک تعیین میکند که آیا قاعده شما اصلاً وجود دارد یا نه.