VPN ב-chroot על Android: למה המנהרה עובדת ולמה האפליקציות מתעלמות ממנה
chroot משנה את פתרון הנתיבים, לא את הגרעין, את מרחב השמות של הרשת או את רישום ה-VPN של Android. מכאן נובעות שתי תוצאות ושתיהן מפתיעות.
מתקינים הפצת לינוקס שרצה ב-chroot על הטלפון, שמים בה לקוח VPN, והמעטפת מכריזה שהמנהרה הושלמה. ואז עוברים לדפדפן והתעבורה יוצאת דרך נתונים סלולריים כאילו לא קרה דבר. או ההפך: כל אפליקציה בטלפון מאבדת קישוריות כי הלקוח הוסיף נתיב ברירת מחדל במרחב השמות ש-Android באמת משתמש בו. שתי התופעות האלה מגיעות מאותו חלק חסר, ואף אחת מהן אינה באג בלקוח ה-VPN.
המעטפת אומרת מחובר, האפליקציות לא מסכימות
התוצאה הראשונה היא הנפוצה. מנהרה במרחב משתמש שנפתחה מ-chroot יכולה להשלים את לחיצת היד, להדפיס את הסטטיסטיקה שלה ולשאת את התעבורה שאותה מעטפת מייצרת, בעוד אפליקציות Android ממשיכות להשתמש ברשת ברירת המחדל של הטלפון. שום דבר לא מיירט אותן, כי שום דבר לא אמר ל-Android שקיים VPN.
התוצאה השנייה היא המסוכנת. אם ה-chroot חולק את מרחב השמות של הרשת של Android, וזה מה שקורה כברירת מחדל, תהליך עם הרשאות מספיקות יכול להוסיף נתיבים וממשק TUN שמשפיעים על הטלפון כולו. אז המנהרה עובדת לכל אפליקציה, כולל אלה שלא התכוונת לנתב, והקישוריות של הטלפון תלויה כעת בדמון בלי ניהול מחזור חיים, בלי מחוון חיבור ובלי מדיניות תמיד-פעיל.
איזו משתי התוצאות קיבלת אפשר לענות בפקודה אחת. השווה את ip route בתוך ה-chroot עם אותה פקודה ממעטפת Android בחוץ. פלט זהה אומר שה-chroot נמצא במרחב השמות של המארח: כל שינוי רשת שהוא עושה חל על הטלפון כולו. פלט שונה אומר שהמשגר יצר עבורו מרחב שמות משלו, והמנהרה שלו בלתי נראית ל-Android מכוח הבנייה.
מה chroot שינה ומה לא
| Object | Effect of a chroot |
|---|---|
| Pathname resolution | Changed: lookups start at the new root |
| Kernel and drivers | Unchanged: the phone's kernel, its TUN driver, its limits |
| Network namespace | Unchanged unless the launcher creates one |
| Android VPN registration | Unchanged: no app consented, no VPN is selected |
קריאת המערכת chroot עושה דבר אחד: היא משנה את הספרייה שמשמשת לפתרון נתיבים. עמוד ה-man אומר במפורש שזה אינו גבול אבטחה, ואינו אומר דבר על ממשקים, נתיבים או מצב VPN. כל מה שאנשים מצפים לו ממכולת לינוקס — מחסנית רשת פרטית, מערכת קבצים מסוננת, מגבלות משאבים — מגיע ממנגנונים אחרים שהמשגר אולי השתמש בהם ואולי לא.
לכן הטענות ש-chroot מבודד VPN עונות בדרך כלל על השאלה הלא נכונה. השאלה הרלוונטית היא באיזה מרחב שמות התהליך רץ ובאיזה תחום SELinux הוא מוגבל, ושתי אלה תכונות של איך הוא הופעל, לא של המילה chroot.
TUN צריך שלושה דברים, וצומת התקן הוא רק אחד
מנהרה מסורתית במרחב משתמש צריכה התקן TUN שהיא יכולה לפתוח, תמיכת גרעין בהתקן הזה והרשאה להגדיר את הממשק והנתיבים. אלה דרישות נפרדות, ויצירת שם נתיב תחת /dev אינה מספקת אף אחת מהן בעצמה:
ls -l /dev/net/tun
ip tuntap add dev tun0 mode tun
ב-Android תמיכת TUN בדרך כלל מהודרת לתוך הגרעין, מפני ששירות ה-VPN של הפלטפורמה עצמה משתמש בה, אבל זה לא אומר שהתהליך שלך רשאי להשתמש בה. הגדרת הממשק דורשת את יכולת ניהול הרשת, ו-SELinux מחליט אם התהליך יכול בכלל להגיע לצומת ההתקן. סירוב נראה כמו שגיאת הרשאה בלי סיבה ברורה, והתשובה נמצאת בעקבות הביקורת:
dmesg | grep avc
logcat -d | grep "avc: denied"
root לא פותר את זה. SELinux מגביל תהליכים שרצים כ-uid 0 בדיוק כמו אחרים, ולכן chroot שהופעל ממעטפת root עדיין יכול לקבל סירוב להתקן שהוא רואה. כיבוי האכיפה כדי להתקין מתקין ישן הוא החלטה לכל הטלפון עם השלכות הרבה מעבר למנהרה, וזה לא תיקון לקובץ בינארי מיושן.
למה אפליקציות Android מתעלמות מהמנהרה שלך
המודל הנתמך של Android הוא אפליקציית VPN שנרשמת דרך שירות ה-VPN של הפלטפורמה, מבקשת מהמשתמש הסכמה פעם אחת ומקבלת מתאר קובץ למנהרה שלה. אחר כך הפלטפורמה מנתבת תעבורה לתוך המתאר הזה ואוכפת את היקף האפליקציות שהמשתמש או המנהל בחרו, ולכן אותו מנגנון יכול להיות לפי אפליקציה, לפי פרופיל ומשולב עם תמיד-פעיל וחסימה בלי VPN. מדריך פיתוח ה-VPN של Android מתאר את לחיצת היד הזו, והנקודה המרכזית היא שזה רישום, לא תחבולת ניתוב.
דמון בתוך chroot לא נרשם אף פעם. אין דיאלוג הסכמה, אין סמל מפתח בשורת המצב, אין רשימת הכללה או החרגה לפי אפליקציה ואין השתתפות במדיניות תמיד-פעיל. לכן האפליקציות ממשיכות להשתמש ברשת ברירת המחדל, והמנהרה נושאת רק את מה שהמעטפת עצמה שולחת.
גם הכיוון השני לא עוזר. דחיפת נתיבים למרחב השמות של המארח לוכדת הכול, כולל תעבורה שהפלטפורמה ציפתה להשאיר מקומית, וזה נעשה בלי שליטה לפי אפליקציה ובלי דרך למשתמש לראות או לבטל. אין הגדרה אמצעית שבה דמון ב-chroot מנתב תעבורה לאפליקציה אחת בלבד.
תאימות בינארית היא חומה נפרדת
גם כשצד הרשת מעניין, לקובץ ההרצה יש דרישות משלו: ארכיטקטורת מעבד, מטעין דינמי, ספריית C ויכולות הגרעין שהוא מצפה להן. Android משתמש ב-bionic ולא ב-glibc, וקובץ בינארי ישן שנבנה להפצת שולחן עבודה עשוי להזדקק לספריות שכבר אינן במכשיר.
קישור שם קובץ ספרייה לגרסה חדשה יותר לא יוצר תאימות ABI, וספריית הצפנה בארכיון מחבילה בת עשר שנים אינה רכיב מתוחזק. אם התוכנית תלויה בזה, התוכנית היא להריץ קוד לא מתוחזק עם הרשאות רשת בטלפון.
הוכח את זה בהקשר של Android ואחר כך נקה
בדוק את מה שבאמת חשוב לך, מהצד שחשוב: פתח את האפליקציה הספציפית, הגע ליעד הפרטי הספציפי, ודא איזה פותר ענה, ואז נתק את המנהרה וודא את ההתנהגות המתועדת. תוצאה ממסוף בתוך chroot היא ראיה על הקשר ההרצה של אותו מסוף ולא יותר.
אם שינית נתיבי מארח במהלך הניסוי, החזר אותם ורשום מה שינית. אחר כך בדוק אם בטלפון מוגדר VPN תמיד-פעיל, מפני שניסוי ישן שהשאיר כלל יכול להיראות כמו תקלה בפלטפורמה חודשים אחר כך.
לצורך אמיתי של גישה מרחוק, השתמש בלקוח הנתמך של השירות או בתכנון שער מאושר. השאלה ששווה לשאול אינה אם אפשר להריץ מתקין עם root, אלא אם מימוש נתמך מעביר את האפליקציות שאתה צריך מעבר לגבול שהתכוונת אליו, בלי להשאיר במכשיר שינויים מורשים בלי בעלים.