لماذا لا يستطيع التطبيق تشغيل ملف تعريف VPN محفوظ على Android أو iOS
بناء نفق خاص بك وتشغيل ملف تعريف محفوظ مهمتان مختلفتان. قارن ما تسمح به Android وiOS وسطح المكتب فعلاً للتطبيق.
تُصنَّف وظيفتان مختلفتان تماماً تحت مسمى VPN البرمجي، وواحدة فقط منهما متاحة لتطبيق عادي على أي من المنصتين المحمولتين. الأولى هي تشغيل ملف تعريف سبق أن حفظه المستخدم؛ والثانية هي بناء نفق داخل عمليتك أنت. تمنحك Android الثانية عبر VpnService وتمنحك iOS الثانية عبر استحقاق Network Extension، بينما تبقى الأولى لدى النظام أو مالك الجهاز أو خادم MDM. إن تحديد أي منهما تحتاجه فعلاً يجعل المشروع أصغر عادة.
وظيفتان تحملان الاسم نفسه
تنشيط ملف تعريف محفوظ يعني أن تطلب من نفق قائم أن يبدأ: مضيف وبيانات اعتماد وبروتوكول، وكلها مهيأة مسبقاً في مكان يراه المستخدم. أما بناء نفق فيعني أن كودك يملك الواجهة وحلقة الحزم والبروتوكول. تبدو الوظيفتان متشابهتين في قائمة مهام المنتج ولا تشتركان تقريباً في أي تنفيذ.
لا تعرض أي من المنصتين المحمولتين الوظيفة الأولى لتطبيق SDK. الاتصال الذي يظهر دون أن يفعل المستخدم شيئاً لا يمكن تمييزه عن البرمجيات الخبيثة، لذلك تضع المنصتان بوابة موافقة أمام أي تطبيق ينقل حركة المرور.
Android: بوابة موافقة، ولا واجهة برمجة لملفات التعريف
VpnService موجودة منذ Android 4.0 وتتيح للتطبيق إنشاء واجهة tun ثم قراءة الحزم وكتابتها. يجب الإعلان عن الخدمة باستخدام BIND_VPN_SERVICE، وتُنتج VpnService.prepare() مربع حوار النظام الذي يطلب من المستخدم السماح بها. نموذج المنصة، ToyVpn، هو أوضح بيان للشكل المقصود: كودك يملك الواجهة وكل ما يمر عبرها.
ما لا تفعله هو إضافة إدخال إلى شاشة إعدادات VPN في النظام. لا توجد واجهة برمجة عامة لذلك، ويحفظ تطبيق الإعدادات ملفاته التعريفية في مخزن لا يستطيع تطبيق عادي الوصول إليه. استدعاء الفئات المخفية من تطبيق عادي ينتج ملفات تعريفية لا يسردها النظام أبداً، ولهذا يتوقف الكود الذي بدا يعمل في إصدار ما عن العمل بصمت في الإصدار التالي.
المسار المُدار منفصل. يمكن لمالك الجهاز أو مالك ملف التعريف تعيين حزمة VPN دائمة التشغيل عبر واجهات سياسة الجهاز، ويمكن لأداة مؤسسية دفع ملف تعريف يوافق عليه المستخدم بعد ذلك. هذه سلطة إدارة أجهزة، وليست إذناً يطلبه التطبيق وقت التثبيت. إن كان عميلك مؤسسة، فهذا هو المسار الذي تخطط له؛ وإن كان عميلك شخصاً يثبت التطبيق من متجر، فهذا غير متاح إطلاقاً.
iOS: الاستحقاق هو الحد
تقسم iOS العمل نفسه بطريقة مختلفة. ينفذ عميل VPN مزود نفق حزم أو وكيل تطبيق ضمن إطار Network Extension، ويجب منح الاستحقاق المطابق لحساب المطور. يوجد إعداد VPN شخصي عبر NEVPNManager للحالة الأبسط، ولا يزال يعمل ضمن نموذج الأذونات نفسه. توثق Apple السطح كله في مرجع Network Extension، ويستحق القراءة قبل تحديد نطاق العمل.
لا توجد طريقة مدعومة لفتح شاشة إعدادات VPN أو قراءة إعدادات المستخدم المحفوظة أو بدء أحدها. ما يمكن للعميل فعله هو شحن ملف تعريف إعداد ليثبته المستخدم، ثم استخدام النفق الناتج عبر استدعاءات شبكة عادية. يُعيَّن النفق لكل تطبيق من خادم MDM عبر قاعدة عند الطلب، ولا يختاره التطبيق وقت التشغيل.
وجود عميل VPN من مورّد كبير ليس دليلاً على أن حسابك يستطيع شحن الشيء نفسه. تُطلب تلك الاستحقاقات لكل حساب مطور وتُراجع، لذا يقع خطر الجدول الزمني على Apple لا على خطة السبرنت لديك.
سطح المكتب يصل أبعد، لكن الملكية لا تتغير
Windows هو الحالة المتساهلة. ينشئ Add-VpnConnection إدخال دفتر هاتف من جلسة PowerShell مرفوعة، ويوصله rasdial أو يفصله من سكربت. يمكن للتطبيق أيضاً قيادة عميل مورّد عبر سطر الأوامر الموثق لديه. إنشاء ملف التعريف لا يزال إجراءً إدارياً، لأن الإدخال يعيش في مخزن على مستوى الجهاز.
يتصرف macOS بالطريقة نفسها بأدوات مختلفة. يعمل scutil --nc start على خدمات موجودة بالفعل، ويعمل stop عليها أيضاً، بينما إنشاء خدمة جديدة مهمة إدارة نظام. امتلاك العملية على حاسوب محمول لا يجعلك مالك الإعداد.
ما الذي تبنيه بدلاً من ذلك
إن كان الشرط الحقيقي هو أن يصل تطبيقك إلى واجهة برمجة داخلية، فالنفق الذي يشغله المستخدم بالفعل كافٍ. أرسل طلبات HTTPS عادية ودع النظام يختار المسار، وتوقف عن محاولة التحكم في النفق. وإن كان الشرط هو أن تستخدم حركة تطبيقك وحدها النفق، فاطلب VPN لكل تطبيق عبر قناة الإدارة، أو أنهِ TLS داخل التطبيق أمام خدمة يمكن الوصول إليها بدونه.
واجهات البرمجة المخفية ليست اختصاراً يستحق العناء. تتغير بين الإصدارات، وتفشل في مراجعة التطبيقات، والأعطال التي تنتجها تبدو لمن يرث الكود كأعطال شبكة عادية. المقارنة ليست بين Android وiOS؛ بل بين امتلاك نفق وطلب من المنصة توجيه حركة المرور، والأولى فقط هي التي تبنيه.