למה כלל ה-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. אם שתי משפחות מחזיקות בכללים, תעדו איזו מהן סמכותית; שתי מדיניויות חופפות הן הדרך שבה חבילה מתקבלת במקום אחד ונזרקת במקום אחר בלי קובץ אחד לקרוא.
הבידוד עצמו עדיין צריך עיצוב מלא. מתן רשות למקור אחד אל יעד אחד מוכיח רק שזרימה אחת עובדת, בעוד הדרישה היא בדרך כלל שמכונה וירטואלית לא תוכל להגיע לשכנותיה או למארח. מונים בכללי הדחייה חשובים יותר מהמונה בכלל הקבלה, משום שמונה הקבלה הוא זה שכבר ציפיתם שיזוז.
לבסוף, החזיקו את מקרה הקלט של המארח בנפרד. תעבורה שמופנית לכתובת ה-IP של הגשר עצמו מגיעה ל-input, לא ל-forward, וכלל ב-forward לעולם לא יראה אותה, לא משנה איזו משפחה תבחרו. שלושה מקרים, שתי משפחות, לקח אחד: הנתיב קובע את ההוק, וההוק קובע אם הכלל שלכם בכלל קיים.