ממשקי API ל-iOS VPN: NEVPNManager, ספק מנהרה או פרופיל?
בחרו את ה-API לפי התעבורה שאיתה עליכם לדבר, ואז הוכיחו שהמנהרה נושאת תעבורה: סדר save-load-start, הרשאות provider וכללי on-demand.
שלושה מוצרים שונים עונים לשם iOS VPN, והם אינם חולקים API. Personal VPN רוכב על תעבורת IPsec ו-IKEv2 המובנות במערכת. Custom tunnel מריץ פרוטוקול משלכם בתוך network extension. Configuration profile מגדיר את האחד או את השני ויכול להוסיף התנהגות on-demand. בחירה לפי התעבורה שאיתה אתם חייבים לדבר מהירה יותר מבחירה לפי המדריך שהתקמפל ראשון.
הריצו את הבדיקות שלהלן לפי הסדר, כי כל שלב מניח שהקודם הפיק תצורה תקינה.
שלב 1: בחירה לפי התעבורה
אם השרת הוא IKEv2 או IPsec בסגנון Cisco, NEVPNManager מספיק ותעבורת המערכת מגיעה בחינם. אם השרת מדבר OpenVPN, WireGuard או כל דבר אחר, NEVPNManager לא יכול לעזור: אין לו נקודת חיבור לפרוטוקול של צד שלישי. המקרה הזה דורש NETunnelProviderManager עם packet tunnel provider שמממש את הפרוטוקול. Profile רק מפיץ תצורה, והוא אינו יכול להוסיף תעבורה שהמערכת ההפעלה חסרה.
PPTP אינו יעד במערכות הנוכחיות. IPsec עם סוד משותף עדיין מצפה לשם הקבוצה ב-remoteIdentifier, לפריט keychain שמחזיק את הסוד, ולאימות מורחב כשהשרת מבקש שם משתמש. כל חלק חסר מופיע כאותה שגיאת תצורה גנרית, אז הוסיפו את כולם לפני שתתחילו לדבג משהו אחר.
שלב 2: כיבוד סדר השמירה, הטעינה וההפעלה
NEVPNManager כותב להעדפות, קורא אותן חזרה, ורק אז מתחיל. מלאו את אובייקטי הפרוטוקול והמנהל, קראו ל-saveToPreferences, קראו ל-loadFromPreferences, וקראו ל-startVPNTunnel מתוך load completion handler. התחלה לפני שהטעינה חוזרת מייצרת שגיאת תצורה לא חוקית, NEVPNErrorDomain code 1, בתצורה שאחרת תקינה.
הצורה קצרה מספיק כדי לשמור אותה במקום אחד:
manager.protocolConfiguration = proto
manager.isEnabled = true
manager.saveToPreferences { }
manager.loadFromPreferences { manager.connection.startVPNTunnel() }startVPNTunnel חוזר כשהבקשה מתקבלת, לא כשהמנהרה עולה. עקבו אחרי NEVPNStatusDidChange וקראו את connection.status למצב האמיתי. אפליקציה שמניחה הצלחה בנקודת הקריאה מדווחת על VPN מחובר בזמן שה-extension עדיין מנהל משא ומתן. מצב connecting הוא נורמלי לרגע; מצב שלעולם לא יוצא ממנו הוא הכשל ששווה לתעד ביומן.
שלב 3: לתת לתהליך ה-provider את מה שהוא צריך
תעבורה מותאמת רצה בתהליך נפרד עם bundle identifier משלו ו-entitlement משלו. גם אפליקציית ה-container וגם ה-provider זקוקים ל-entitlement של network-extension בזמן החתימה, וגם בנייה של Developer ID מחוץ ל-App Store עדיין חייבת לשאת אותו ב-provisioning profile. entitlement חסר נראה כמו באג פרוטוקול כי ה-extension פשוט נכשל בעלייה.
אין socket descriptor ציבורי לשאול. NEPacketTunnelProvider נותן לכם NEPacketTunnelFlow עם read, write ו-cancel, וקוד שמגיע לנתיבי מפתח פרטיים נשבר בין מהדורות. ה-provider ממשיך לרוץ כשהאפליקציה ברקע, כך שאפליקציית ה-container לא צריכה מצב רקע בשביל זה.
שלב 4: on-demand הוא סט כללים, לא מתג
isOnDemandEnabled רק מחמש את הכללים; NEOnDemandRuleConnect ואחיו מחליטים מתי המנהרה עולה. כלל evaluate-connection עם probe URL טוב רק כמו ה-URL הזה. אם ה-probe עונה בלי המנהרה, הכלל מסיק שהרשת הפרטית כבר נגישה ולעולם לא מתחבר, אז כוונו אותו למשהו שעונה רק מבפנים.
Always-on היא הבטחה נפרדת. היא דורשת מכשיר בפיקוח ופרופיל מנוהל, ומשתמש ללא פיקוח יכול להסיר פרופיל מתי שירצה. אל תשלחו עיצוב שתלוי ב-always-on אלא אם הצי בפיקוח.
שלב 5: להוכיח את המסלול, לא את התווית
סריקה אחר ממשקי tun, tap, ppp או ipsec מוכיחה ש-VPN מוגדר, לא שהבקשה שלכם השתמשה בו. NWPathMonitor עם usesInterfaceType(.other) אומר שממשק וירטואלי זמין, ו-viability handler יכול לבדוק מארח אחד. אף אחד מהם לא מוכיח את מסלול היציאה, אז שלבו אותם עם בקשה למשאב שרק המנהרה יכולה להגיע אליו. שמות ממשקים הם עזר אבחוני, לעולם לא שער שקלוגיקה של אפליקציה צריכה להסתעף עליו.
לבדיקת שטח, השוו את הכתובת הציבורית עם המנהרה ובלעדיה ומשכו דף פנימי בלבד. קריאת סטטוס יכולה לדווח מחובר בזמן שה-provider מנהל משא ומתן, אז תנו לתוצאת המשאב להחליט והתייחסו לסטטוס כראיה תומכת.