הבקשה היא תמיד גרסה של אותו משפט: להשאיר את ה‑VPN לגלישה, אבל לתת ל‑SSH או למשימת גיבוי להשתמש בחיבור הרגיל. העצה שמסתובבת בנושא, להעביר את הפורט עם כללי NAT של iptables, לא עושה זאת. כלל NAT משכתב כתובות ופורטים בתוך זרימה; הוא אף פעם לא בוחר באיזה ממשק החבילה יוצאת. ההחלטה הזו שייכת לחיפוש המסלול, ולחיפוש המסלול דרושה עזרה כדי לראות פורט.

למה מסלול רגיל לא יכול להתאים לפורט

לינוקס בוחר מסלול על ידי חיפוש כתובת היעד בטבלה, לאחר סינון טבלאות מועמדות באמצעות כללי מדיניות. הכללים האלה יכולים להתאים לסימון חומת אש, לכתובת מקור, לממשק נכנס או ל‑UID. לרשומת המסלול עצמה אין שדה פורט, כי המסלול נבחר לפני שכותרת התעבורה משנה משהו.

iproute2 והקרנלים הנוכחיים מרחיבים את בורר הכלל: ipproto, sport ו‑dport מתקבלים, ולכן ip rule add ipproto tcp dport 22 table 100 מבטא את הכוונה בשורה אחת. הריצו ip rule add help במארח האמיתי כדי לראות אם הבנייה שלכם מפרטת dport. אם כן, סיימתם בפקודה אחת; אם לא, מסלול הסימון שלמטה הוא המקבילה.

כך או כך, הכלל הוא רק מצביע. טבלה 100 עדיין זקוקה למסלול, וזה החלק שאנשים שוכחים.

שלושה חלקים, או אחד

הצורה המודרנית היא הכלל הבודד שלמעלה ועוד מסלול בטבלה החלופית. הצורה הקלאסית מוסיפה netfilter באמצע: לסמן את החבילה, ואז להתאים את הסימון בכלל. שתיהן נגמרות באותו חיפוש; הן נבדלות במקום שבו הפורט נקרא.

גישת הסימון קיימת מפני ש‑netfilter רואה את כותרת התעבורה וקוד הניתוב לא ראה אותה היסטורית. זה כל המנגנון, והוא מסביר מדוע החלופה היא שלושה חלקים משתפי פעולה ולא פקודה חכמה אחת.

שלושת החלקים לפי הסדר

ראשית, סמנו את הזרימות שברצונכם להסיט, בשרשרת שרצה מוקדם מספיק כדי שהסימון יהיה קיים לפני החלטת המסלול:

nft add table inet vpnexempt
nft 'add chain inet vpnexempt out { type route hook output priority mangle; }'
nft add rule inet vpnexempt out tcp dport 22 meta mark set 0x1

שנית, שלחו חבילות מסומנות לטבלה נפרדת:

ip rule add fwmark 0x1/0x1 table 100

שלישית, תנו לאותה טבלה מסלול ברירת מחדל דרך הקישור הפיזי. הכתובות להמחשה; החליפו אותן בשלכם:

ip route add default via 192.168.1.1 dev wlan0 table 100

דלגו על השלב השלישי והחבילה המסומנת לא מוצאת מסלול בטבלה 100, ולכן היא נכשלת. התסמין נראה כאילו הכלל שבר את הרשת, כשבפועל הוא הסיר את הדרך היחידה שנותרה לחבילה.

מה שומר על זה עובד אחרי החבילה הראשונה

סימונים חלים על חבילות יוצאות. התשובה מגיעה בממשק הפיזי עם הכתובת שהקישור הקצה, והטבלה הראשית עשויה עדיין להעדיף את המנהרה עבור היעד הזה. שני מנגנונים שומרים על עקביות נתיב התשובה: כלל מבוסס מקור עבור כתובת הקישור, ומעקב חיבורים ששומר על ההחלטה לאורך חיי הזרימה. שמירת הסימון ב‑conntrack ושחזורו בתשובה היא הדרך הרגילה להפוך חריגה עם מצב ליציבה.

סינון נתיב הפוך הוא הרוצח השקט השני. אם הקרנל מאמת את נתיב החזרה בקפדנות והתשובה מגיעה בממשק שהטבלה הראשית לא הייתה בוחרת, החבילה נזרקת בלי הודעה. rp_filter ערכי ברירת המחדל משתנים לפי ההפצה ולפי האם הממשק מועבר הלאה, לכן קראו את הערך בפועל במקום להניח שהוא כבוי.

אל תסמנו את התעבורה של המנהרה עצמה. מנהרת WireGuard או UDP OpenVPN אינה TCP 22, ולכן המתכון שלמעלה בטוח, אבל חריגה שנכתבה כהכול למעט יעד המנהרה עלולה לתפוס את החבילות המוצפנות ולשבור את המנהרה באופן שנראה כמו לולאת ניתוב.

בדקו במקום להניח

שאלו את הקרנל באיזה נתיב יבחר, עבור היעד ועבור הפורט, ואז הריצו את ההעברה בפועל:

ip route get 203.0.113.10
ip rule show
nft list ruleset

אם הסימון קיים אבל המונים לא זזים אף פעם, הכלל לא מתאים. המונים הם ההבדל בין תצורה שכתבתם לבין מדיניות שהקרנל מיישם.

מתי זה הכלי הלא נכון

חריגות מבוססות פורט הן שבירות מטבען. שירותים מחליפים פורטים, HTTP/3 מגיע כ‑UDP 443, ויישום אחד יכול לפתוח כמה זרימות מסוגים שונים. אם הדרישה האמיתית היא שהתוכנית הזו תשתמש בקישור הרגיל, שכבת הניתוב היא המקום הלא נכון; מרחב שמות רשת או פרוקסי לכל יישום מתאימים יותר.

אם הדרישה היא שהרשת המשנה הזו תשתמש בקישור הרגיל, דלגו לגמרי על הסימונים והוסיפו מסלול ספציפי יותר. זה פשוט יותר, זה נראה בטבלה אחת, והאדם הבא יכול לבקר את זה בלי לקרוא סט כללים.

רשמו איזו משלוש הדרישות באמת יש לכם לפני שנוגעים בחומת האש. המנגנון שלמעלה מדויק, ודיוק שמוחל על השכבה הלא נכונה הוא רק השבתה ארוכה יותר.

מאמרים קשורים