VPN

‫Android מספק למפתחים ממשקי API כדי שיוכלו ליצור פתרונות של רשת וירטואלית פרטית (VPN). אחרי שתקראו את המדריך הזה, תדעו איך לפתח ולבדוק לקוח VPN משלכם למכשירים עם Android.

סקירה כללית

רשתות VPN מאפשרות למכשירים שלא נמצאים פיזית ברשת לגשת אליה באופן מאובטח.

ב-Android יש לקוח VPN מובנה (PPTP ו-L2TP/IPSec), שלפעמים נקרא VPN מדור קודם. ב-Android 4.0 (רמת API ‏14) הושקו ממשקי API כדי שמפתחי אפליקציות יוכלו לספק פתרונות VPN משלהם. אתם אורזים את פתרון ה-VPN באפליקציה שאנשים מתקינים במכשיר. בדרך כלל, מפתחים יוצרים אפליקציית VPN מאחת מהסיבות הבאות:

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

בשאר המדריך הזה מוסבר איך לפתח אפליקציות VPN (כולל VPN זמינות תמידית ו-VPN לכל אפליקציה), ולא מוסבר על לקוח ה-VPN המובנה.

חוויית משתמש

‫Android מספק ממשק משתמש (UI) שעוזר להגדיר, להפעיל ולהפסיק את פתרון ה-VPN. בנוסף, ממשק המשתמש של המערכת מודיע למשתמש במכשיר על חיבור פעיל דרך VPN. ב-Android מוצגים הרכיבים הבאים בממשק המשתמש של חיבורים דרך VPN:

  • לפני שאפליקציית VPN יכולה לפעול בפעם הראשונה, המערכת מציגה תיבת דו-שיח של בקשת חיבור. בתיבת הדו-שיח, המשתמש במכשיר מתבקש לאשר שהוא סומך על ה-VPN ולקבל את הבקשה.
  • במסך הגדרות ה-VPN (הגדרות > רשת ואינטרנט > VPN) מוצגות אפליקציות ה-VPN שבהן המשתמש אישר בקשות חיבור. יש כפתור להגדרת אפשרויות המערכת או להסרת ה-VPN.
  • במגש ההגדרות המהירות מוצג חלונית מידע כשהחיבור פעיל. לחיצה על התווית פותחת תיבת דו-שיח עם מידע נוסף וקישור להגדרות.
  • בשורת הסטטוס מופיע סמל של VPN (מפתח) שמציין שהחיבור פעיל.

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

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

שירות VPN

האפליקציה מחברת את הרשת של המערכת של המשתמש (או של פרופיל העבודה) לשער VPN. כל משתמש (או פרופיל עבודה) יכול להפעיל אפליקציית VPN אחרת. אתם יוצרים שירות VPN שהמערכת משתמשת בו כדי להפעיל ולהפסיק את ה-VPN, ולעקוב אחרי סטטוס החיבור. שירות ה-VPN שלכם יורש מ-VpnService.

השירות פועל גם כקונטיינר לחיבורים של שער ה-VPN ולממשקי המכשירים המקומיים שלהם. מופע השירות שלכם קורא ל-methods של VpnService.Builder כדי ליצור ממשק מקומי חדש.

איור 1. איך VpnService מחבר את הרשת של Android לשער ה-VPN
תרשים ארכיטקטורת בלוקים שמראה איך VpnService יוצר ממשק TUN מקומי ברשת המערכת.

האפליקציה מעבירה את הנתונים הבאים כדי לחבר את המכשיר לשער ה-VPN:

  • קורא חבילות IP יוצאות מתיאור הקובץ של הממשק המקומי, מצפין אותן ושולח אותן לשער ה-VPN.
  • כותב מנות נכנסות (שהתקבלו ופוענחו משער ה-VPN) לתיאור הקובץ של הממשק המקומי.

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

הוספת שירות

כדי להוסיף שירות VPN לאפליקציה, צריך ליצור שירות Android שעובר בירושה מ-VpnService. מצהירים על שירות ה-VPN בקובץ המניפסט של האפליקציה, ומוסיפים את הפרטים הבאים:

  • מגנים על השירות באמצעות ההרשאה BIND_VPN_SERVICE כדי שרק המערכת תוכל להיות מקושרת לשירות.
  • מפרסמים את השירות באמצעות מסנן ה-Intent‏ "android.net.VpnService" כדי שהמערכת תוכל למצוא את השירות.

בדוגמה הזו אפשר לראות איך להצהיר על השירות בקובץ המניפסט של האפליקציה:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
</service>

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

הכנת שירות

כדי להכין את האפליקציה להפוך לשירות ה-VPN הנוכחי של המשתמש, צריך להפעיל את VpnService.prepare(). אם המשתמש במכשיר עדיין לא נתן הרשאה לאפליקציה, השיטה מחזירה Intent של פעילות. משתמשים ב-intent הזה כדי להתחיל פעילות מערכת שמבקשת הרשאה. המערכת מציגה תיבת דו-שיח שדומה לתיבות דו-שיח אחרות של הרשאות, כמו גישה למצלמה או לאנשי הקשר. אם האפליקציה כבר מוכנה, השיטה מחזירה את הערך null.

רק אפליקציה אחת יכולה להיות שירות ה-VPN שמוכן לשימוש. תמיד צריך להפעיל את VpnService.prepare() כי יכול להיות שמישהו הגדיר אפליקציה אחרת בתור שירות ה-VPN מאז שהאפליקציה שלכם הפעילה את השיטה בפעם האחרונה. מידע נוסף מופיע בקטע מחזור החיים של השירות.

קישור שירות

אחרי שהשירות פועל, אפשר ליצור ממשק מקומי חדש שמחובר לשער VPN. כדי לבקש הרשאה ולחבר את השירות לשער ה-VPN, צריך לבצע את השלבים הבאים לפי הסדר:

  1. קוראים ל-VpnService.prepare() כדי לבקש הרשאה (במקרה הצורך).
  2. קוראים ל-VpnService.protect() כדי לשמור את שקע המנהרה של האפליקציה מחוץ ל-VPN של המערכת ולמנוע חיבור מעגלי.
  3. מפעילים את DatagramSocket.connect() כדי לחבר את שקע המנהרה של האפליקציה לשער ה-VPN.
  4. מפעילים את השיטות של VpnService.Builder כדי להגדיר ממשק TUN מקומי חדש במכשיר לתנועת VPN.
  5. מפעילים את VpnService.Builder.establish() כדי שהמערכת תגדיר את ממשק ה-TUN המקומי ותתחיל לנתב את התנועה דרך הממשק.

שער VPN בדרך כלל מציע הגדרות לממשק TUN המקומי במהלך לחיצת היד. האפליקציה מפעילה שיטות של VpnService.Builder כדי להגדיר שירות, כמו בדוגמה הבאה:

// Configure a new interface from our VpnService instance. This must be done
// from inside a VpnService.
val builder = Builder()

// Create a local TUN interface using predetermined addresses. In your app,
// you typically use values returned from the VPN gateway during handshaking.
val localTunnel = builder
    .addAddress("192.168.2.2", 24)
    .addRoute("0.0.0.0", 0)
    .addDnsServer("192.168.1.1")
    .establish()

הדוגמה בקטע VPN לכל אפליקציה מציגה הגדרת IPv6 שכוללת אפשרויות נוספות. כדי ליצור ממשק חדש, צריך להוסיף את הערכים הבאים של VpnService.Builder:

addAddress()
מוסיפים לפחות כתובת IPv4 או IPv6 אחת יחד עם מסכה של תת-רשת שהמערכת מקצה ככתובת ממשק TUN מקומית. בדרך כלל, האפליקציה מקבלת את כתובות ה-IP ואת המסכות של תת-הרשת משער VPN במהלך לחיצת היד.
addRoute()
כדי שהמערכת תשלח תנועה דרך ממשק ה-VPN, צריך להוסיף לפחות נתיב אחד. המסלולים מסוננים לפי כתובות היעד. כדי לקבל את כל התנועה, מגדירים נתיב פתוח כמו 0.0.0.0/0 או ::/0.

ה-method‏ establish() מחזירה מופע של ParcelFileDescriptor, שבו האפליקציה משתמשת כדי לקרוא ולכתוב חבילות אל המאגר של הממשק וממנו. השיטה establish() מחזירה את הערך null אם האפליקציה לא מוכנה או אם מישהו מבטל את ההרשאה.

מחזור החיים של שירות מנוהל

האפליקציה צריכה לעקוב אחרי הסטטוס של ה-VPN שנבחר במערכת ושל כל החיבורים הפעילים. עדכנו את ממשק המשתמש (UI) של האפליקציה כדי שהמשתמש במכשיר יהיה מודע לכל שינוי.

הפעלת שירות

אפשר להפעיל את שירות ה-VPN בדרכים הבאות:

  • האפליקציה מפעילה את השירות – בדרך כלל כי משתמש לחץ על כפתור התחברות.
  • המערכת מפעילה את השירות כי האפשרות VPN שפועל כל הזמן מופעלת.

האפליקציה מפעילה את שירות ה-VPN על ידי העברת Intent אל startService(). מידע נוסף זמין במאמר הפעלת שירות.

המערכת מפעילה את השירות ברקע באמצעות קריאה ל-onStartCommand(). עם זאת, ב-Android יש הגבלות על אפליקציות שפועלות ברקע בגרסה 8.0 (רמת API‏ 26) ומעלה. אם אתם תומכים ברמות ה-API האלה, אתם צריכים להעביר את השירות לחזית באמצעות קריאה ל-Service.startForeground(). מידע נוסף זמין במאמר הפעלת שירות בחזית.

הפסקת שירות

מי שמשתמש במכשיר יכול להפסיק את השירות דרך ממשק המשתמש של האפליקציה. עצרו את השירות במקום רק לסגור את החיבור. המערכת גם מפסיקה חיבור פעיל כשהמשתמש במכשיר מבצע את הפעולות הבאות במסך ה-VPN באפליקציית ההגדרות:

  • מתנתק מאפליקציית ה-VPN או מוחק אותה
  • משבית את ה-VPN שפועל תמיד בחיבור פעיל

המערכת קוראת ל-method‏ onRevoke() של השירות, אבל יכול להיות שהקריאה הזו לא תתבצע ב-thread הראשי. כשהמערכת מפעילה את ה-method הזו, ממשק רשת חלופי כבר מנתב את התנועה. אפשר להיפטר בבטחה מהמשאבים הבאים:

  • סוגרים את שקע המנהרה המוגנת לשער ה-VPN באמצעות קריאה ל-DatagramSocket.close().
  • סוגרים את תיאור הקובץ של החבילה (אין צורך לרוקן אותו) על ידי קריאה ל-ParcelFileDescriptor.close().

VPN שפועל כל הזמן

מערכת Android יכולה להפעיל שירות VPN כשהמכשיר מופעל, ולהשאיר אותו פועל כל עוד המכשיר פועל. התכונה הזו נקראת VPN עם זמינות תמידית והיא זמינה ב-Android מגרסה 7.0 (רמת API‏ 24) ואילך. מערכת Android מנהלת את מחזור החיים של השירות, אבל שירות ה-VPN שלכם אחראי לחיבור לשער ה-VPN. ה-VPN שפועל תמיד יכול גם לחסום חיבורים שלא משתמשים ב-VPN.

חוויית משתמש

ב-Android מגרסה 8.0 ואילך, המערכת מציגה את תיבות הדו-שיח הבאות כדי להודיע למשתמש במכשיר על זמינות תמידית של VPN:

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

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

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

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

זיהוי תמידי

ב-Android אין ממשקי API שמאשרים אם המערכת הפעילה את שירות ה-VPN. אבל, כשהאפליקציה מסמנת את כל מופעי השירות שהיא מפעילה, אפשר להניח שהמערכת הפעילה שירותים לא מסומנים בשביל VPN עם זמינות תמידית. לדוגמה:

  1. יוצרים מכונה של Intent כדי להפעיל את שירות ה-VPN.
  2. מסמנים את שירות ה-VPN על ידי הוספת תוספת ל-Intent.
  3. ב-method‏ onStartCommand() של השירות, מחפשים את הדגל בתוספות של הארגומנט intent.

חיבורים חסומים

המשתמש במכשיר (או אדמין ממחלקת IT) יכול לאלץ את כל תעבורת הנתונים להשתמש ב-VPN. המערכת חוסמת כל תנועה ברשת שלא משתמשת ב-VPN. המשתמשים במכשיר יכולים למצוא את המתג חסימת חיבורים ללא VPN בחלונית האפשרויות של ה-VPN ב"הגדרות".

ביטול זמינות תמידית

אם האפליקציה לא תומכת כרגע ב-VPN עם זמינות תמידית, אפשר לבטל את ההסכמה (ב-Android מגרסה 8.1 ואילך) על ידי הגדרת המטא-נתונים של השירות SERVICE_META_DATA_SUPPORTS_ALWAYS_ON לערך false. בדוגמה הבאה של קובץ מניפסט של אפליקציה אפשר לראות איך מוסיפים את רכיב המטא-נתונים:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
     <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
             android:value=false/>
</service>

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

VPN לכל אפליקציה

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

אפליקציית ה-VPN צריכה להגדיר את הרשימות לפני שהחיבור נוצר. אם אתם צריכים לשנות את הרשימות, אתם צריכים ליצור חיבור חדש דרך VPN. כדי להוסיף אפליקציה לרשימה, היא צריכה להיות מותקנת במכשיר.

// The apps that will have access to the VPN.
val appPackages = arrayOf(
    "com.android.chrome",
    "com.google.android.youtube",
    "com.example.a.missing.app"
)

// Loop through the app packages in the array and confirm that the app is
// installed before adding the app to the allowed list.
val builder = Builder()
for (appPackage in appPackages) {
    try {
        packageManager.getPackageInfo(appPackage, 0)
        builder.addAllowedApplication(appPackage)
    } catch (e: PackageManager.NameNotFoundException) {
        // The app isn't installed.
    }
}

// Complete the VPN interface config.
val localTunnel = builder
    .addAddress("2001:db8::1", 64)
    .addRoute("::", 0)
    .establish()

אפליקציות מורשות

כדי להוסיף אפליקציה לרשימת האפליקציות המותרות, צריך להפעיל את הפונקציה VpnService.Builder.addAllowedApplication(). אם הרשימה כוללת אפליקציה אחת או יותר, רק האפליקציות שברשימה משתמשות ב-VPN. כל שאר האפליקציות (שלא מופיעות ברשימה) משתמשות ברשתות המערכת כאילו ה-VPN לא פועל. כשרשימת האפליקציות המותרות ריקה, כל האפליקציות משתמשות ב-VPN.

אפליקציות לא מורשות

כדי להוסיף אפליקציה לרשימת האפליקציות הלא מורשות, מפעילים את השיטה VpnService.Builder.addDisallowedApplication(). אפליקציות שלא אושרו משתמשות ברשת המערכת כאילו ה-VPN לא פועל, וכל שאר האפליקציות משתמשות ב-VPN.

מעקף VPN

אתם יכולים לאפשר לאפליקציות לעקוף את ה-VPN ולבחור רשת משלהן. כדי לעקוף את ה-VPN, צריך להפעיל את VpnService.Builder.allowBypass() כשמגדירים ממשק VPN. אי אפשר לשנות את הערך הזה אחרי שמפעילים את שירות ה-VPN. אם אפליקציה לא מקשרת את התהליך או את השקע שלה לרשת ספציפית, התנועה ברשת של האפליקציה ממשיכה דרך ה-VPN.

לאפליקציות שמתחברות לרשת ספציפית אין חיבור כשמישהו חוסם תנועה שלא עוברת דרך ה-VPN. כדי לשלוח תנועה דרך רשת ספציפית, אפליקציות קוראות לשיטות כמו ConnectivityManager.bindProcessToNetwork() או Network.bindSocket() לפני שהן מתחברות לשקע.

קוד לדוגמה

פרויקט קוד פתוח של Android כולל אפליקציה לדוגמה שנקראת ToyVPN. האפליקציה הזו מראה איך להגדיר ולחבר שירות VPN.