VPN

اندروید APIهایی را برای توسعه دهندگان فراهم می کند تا راه حل های شبکه خصوصی مجازی (VPN) ایجاد کنند. پس از خواندن این راهنما، می‌دانید که چگونه مشتری VPN خود را برای دستگاه‌های مجهز به Android توسعه داده و آزمایش کنید.

نمای کلی

VPN ها به دستگاه هایی که به طور فیزیکی در شبکه نیستند اجازه می دهند به طور ایمن به شبکه دسترسی داشته باشند.

Android شامل یک سرویس گیرنده VPN داخلی (PPTP و L2TP/IPSec) است که گاهی اوقات VPN قدیمی نامیده می شود. Android 4.0 (API Level 14) API هایی را معرفی کرد تا توسعه دهندگان برنامه بتوانند راه حل های VPN خود را ارائه دهند. شما راه حل VPN خود را در برنامه ای بسته بندی می کنید که مردم روی دستگاه نصب می کنند. توسعه دهندگان معمولاً به یکی از دلایل زیر یک برنامه VPN می سازند:

  • برای ارائه پروتکل های VPN که کلاینت داخلی از آنها پشتیبانی نمی کند.
  • برای کمک به افراد برای اتصال به یک سرویس VPN بدون پیکربندی پیچیده.

بقیه این راهنما نحوه توسعه برنامه‌های VPN (از جمله VPN همیشه روشن و هر برنامه ) را توضیح می‌دهد و کلاینت VPN داخلی را پوشش نمی‌دهد.

تجربه کاربری

Android یک رابط کاربری (UI) برای کمک به پیکربندی، راه‌اندازی و توقف راه‌حل VPN شما ارائه می‌کند. رابط کاربری سیستم همچنین باعث می‌شود فردی که از دستگاه استفاده می‌کند از اتصال VPN فعال آگاه شود. Android اجزای UI زیر را برای اتصالات VPN نشان می دهد:

  • قبل از اینکه یک برنامه VPN برای اولین بار فعال شود، سیستم یک گفتگوی درخواست اتصال را نمایش می دهد. گفتگو از شخصی که از دستگاه استفاده می کند می خواهد تأیید کند که به VPN اعتماد دارد و درخواست را می پذیرد.
  • صفحه تنظیمات VPN (تنظیمات > شبکه و اینترنت > VPN) برنامه های VPN را نشان می دهد که در آن شخص درخواست اتصال را پذیرفته است. دکمه ای برای پیکربندی گزینه های سیستم یا فراموش کردن VPN وجود دارد.
  • سینی تنظیمات سریع یک پانل اطلاعات را هنگامی که اتصال فعال است نشان می دهد. با ضربه زدن روی برچسب یک گفتگو با اطلاعات بیشتر و پیوندی به تنظیمات نمایش داده می شود.
  • نوار وضعیت شامل یک نماد VPN (کلید) برای نشان دادن یک اتصال فعال است.

برنامه شما همچنین باید یک رابط کاربری ارائه دهد تا شخصی که از دستگاه استفاده می کند بتواند گزینه های سرویس شما را پیکربندی کند. برای مثال، راه حل شما ممکن است نیاز به گرفتن تنظیمات احراز هویت حساب داشته باشد. برنامه ها باید رابط کاربری زیر را نشان دهند:

  • کنترل برای شروع و توقف دستی اتصال. VPN همیشه روشن می‌تواند در صورت نیاز متصل شود، اما به افراد اجازه می‌دهد در اولین باری که از VPN شما استفاده می‌کنند، اتصال را پیکربندی کنند.
  • اعلان غیرقابل رد کردن زمانی که سرویس فعال است. اعلان می‌تواند وضعیت اتصال را نشان دهد یا اطلاعات بیشتری مانند آمار شبکه ارائه دهد. با ضربه زدن روی اعلان، برنامه شما در پیش زمینه قرار می گیرد. پس از غیرفعال شدن سرویس، اعلان را حذف کنید.

سرویس VPN

برنامه شما شبکه سیستم یک کاربر (یا نمایه کاری ) را به دروازه VPN متصل می کند. هر کاربر (یا نمایه کاری) می تواند یک برنامه VPN متفاوت را اجرا کند. شما یک سرویس VPN ایجاد می کنید که سیستم از آن برای راه اندازی و توقف VPN شما استفاده می کند و وضعیت اتصال را ردیابی می کند. سرویس VPN شما از VpnService به ارث می رسد.

این سرویس همچنین به عنوان ظرف شما برای اتصالات دروازه VPN و رابط های دستگاه محلی آنها عمل می کند. نمونه سرویس شما متدهای VpnService.Builder را برای ایجاد یک رابط محلی جدید فراخوانی می کند.

شکل 1. نحوه اتصال VpnService شبکه اندروید به دروازه VPN
نمودار معماری بلوکی که نشان می دهد چگونه VpnService یک رابط محلی TUN در شبکه های سیستم ایجاد می کند.

برنامه شما داده های زیر را برای اتصال دستگاه به دروازه VPN منتقل می کند:

  • بسته های IP خروجی را از توصیفگر فایل رابط محلی می خواند، آنها را رمزگذاری می کند و به دروازه VPN ارسال می کند.
  • بسته های ورودی (دریافت و رمزگشایی شده از دروازه VPN) را در توصیفگر فایل رابط محلی می نویسد.

تنها یک سرویس فعال برای هر کاربر یا نمایه وجود دارد. شروع یک سرویس جدید، به طور خودکار یک سرویس موجود را متوقف می کند.

یک سرویس اضافه کنید

برای افزودن یک سرویس VPN به برنامه خود، یک سرویس Android ایجاد کنید که از VpnService به ارث می رسد. سرویس VPN را در فایل مانیفست برنامه خود با اضافات زیر اعلام کنید:

  • با مجوز BIND_VPN_SERVICE از سرویس محافظت کنید تا فقط سیستم بتواند به سرویس شما متصل شود.
  • سرویس را با فیلتر هدف "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() تماس بگیرید. اگر شخصی که از دستگاه استفاده می‌کند قبلاً مجوز برنامه شما را نداده باشد، این روش یک هدف فعالیت را برمی‌گرداند. شما از این هدف برای شروع یک فعالیت سیستمی استفاده می کنید که مجوز می خواهد. این سیستم گفتگویی را نشان می دهد که شبیه به سایر گفتگوهای مجوزها، مانند دسترسی به دوربین یا مخاطبین است. اگر برنامه شما از قبل آماده شده باشد، روش null را برمی‌گرداند.

فقط یک برنامه می تواند سرویس VPN آماده فعلی باشد. همیشه VpnService.prepare() را فراخوانی کنید زیرا ممکن است از آخرین باری که برنامه شما این متد را فراخوانی کرده است، برنامه دیگری را به عنوان سرویس VPN تنظیم کرده باشد. برای کسب اطلاعات بیشتر، بخش چرخه عمر سرویس را ببینید.

یک سرویس را وصل کنید

پس از اجرای سرویس، می توانید یک رابط محلی جدید ایجاد کنید که به دروازه VPN متصل است. برای درخواست مجوز و اتصال به سرویس خود به دروازه VPN، باید مراحل را به ترتیب زیر انجام دهید:

  1. با VpnService.prepare() تماس بگیرید تا مجوز بخواهید (در صورت نیاز).
  2. با VpnService.protect() تماس بگیرید تا سوکت تونل برنامه خود را خارج از VPN سیستم نگه دارید و از اتصال دایره ای جلوگیری کنید.
  3. برای اتصال سوکت تونل برنامه خود به دروازه VPN، با DatagramSocket.connect() تماس بگیرید.
  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()

جاوا

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

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

مثال در بخش VPN Per-app یک پیکربندی IPv6 شامل گزینه‌های بیشتر را نشان می‌دهد. قبل از اینکه بتوانید یک رابط جدید ایجاد کنید، باید مقادیر VpnService.Builder زیر را اضافه کنید:

addAddress()
حداقل یک آدرس IPv4 یا IPv6 را به همراه یک ماسک زیر شبکه که سیستم به عنوان آدرس رابط محلی TUN اختصاص می دهد، اضافه کنید. برنامه شما معمولاً آدرس‌های IP و ماسک‌های زیرشبکه را از یک دروازه VPN در حین دست دادن دریافت می‌کند.
addRoute()
اگر می‌خواهید سیستم ترافیک را از طریق رابط VPN ارسال کند، حداقل یک مسیر اضافه کنید. مسیرها بر اساس آدرس مقصد فیلتر می شوند. برای پذیرش تمام ترافیک، یک مسیر باز مانند 0.0.0.0/0 یا ::/0 تنظیم کنید.

متد establish() یک نمونه ParcelFileDescriptor را برمی‌گرداند که برنامه شما از آن برای خواندن و نوشتن بسته‌ها به و از بافر رابط استفاده می‌کند. اگر برنامه شما آماده نشده باشد یا کسی مجوز را لغو کند، متد establish() null برمی‌گرداند.

چرخه عمر سرویس

برنامه شما باید وضعیت VPN انتخابی سیستم و هرگونه اتصال فعال را ردیابی کند. رابط کاربری (UI) برنامه خود را به‌روزرسانی کنید تا فردی که از دستگاه استفاده می‌کند از هرگونه تغییر آگاه شود.

راه اندازی یک سرویس

سرویس VPN شما می تواند به روش های زیر راه اندازی شود:

  • برنامه شما سرویس را راه اندازی می کند—معمولاً به این دلیل که شخصی روی دکمه اتصال ضربه زده است.
  • سیستم سرویس را راه اندازی می کند زیرا VPN همیشه روشن روشن است.

برنامه شما با ارسال یک intent به startService() سرویس VPN را راه اندازی می کند. برای کسب اطلاعات بیشتر، شروع یک سرویس را بخوانید.

سیستم با فراخوانی onStartCommand() سرویس شما را در پس زمینه شروع می کند. با این حال، اندروید برای برنامه‌های پس‌زمینه در نسخه 8.0 (سطح API 26) یا بالاتر محدودیت‌هایی ایجاد می‌کند. اگر از این سطوح API پشتیبانی می کنید، باید با فراخوانی Service.startForeground() سرویس خود را به پیش زمینه انتقال دهید. برای کسب اطلاعات بیشتر، اجرای سرویس در پیش زمینه را بخوانید.

توقف یک سرویس

شخصی که از دستگاه استفاده می کند می تواند با استفاده از رابط کاربری برنامه شما سرویس شما را متوقف کند. به جای بستن اتصال، سرویس را متوقف کنید. هنگامی که شخصی که از دستگاه استفاده می‌کند در صفحه VPN برنامه تنظیمات موارد زیر را انجام دهد، سیستم اتصال فعال را متوقف می‌کند:

  • برنامه VPN را قطع یا فراموش می کند
  • VPN همیشه روشن را برای اتصال فعال خاموش می کند

سیستم متد onRevoke() سرویس شما را فراخوانی می‌کند اما ممکن است این فراخوانی در رشته اصلی اتفاق نیفتد. هنگامی که سیستم این روش را فراخوانی می کند، یک رابط شبکه جایگزین از قبل ترافیک را مسیریابی می کند. می توانید با خیال راحت منابع زیر را از بین ببرید:

  • سوکت تونل محافظت شده را به دروازه VPN با فراخوانی DatagramSocket.close() ببندید.
  • با فراخوانی ParcelFileDescriptor.close() توصیفگر فایل بسته را ببندید (نیازی به تخلیه آن ندارید).

VPN همیشه روشن

Android می‌تواند یک سرویس VPN را هنگامی که دستگاه بوت می‌شود راه‌اندازی کند و زمانی که دستگاه روشن است، آن را در حال اجرا نگه دارد. این ویژگی VPN همیشه روشن نامیده می‌شود و در Android 7.0 (سطح API 24) یا بالاتر موجود است. در حالی که Android چرخه عمر سرویس را حفظ می کند، این سرویس VPN شما است که مسئول اتصال VPN-gateway است. VPN همیشه روشن همچنین می تواند اتصالاتی را که از VPN استفاده نمی کنند مسدود کند.

تجربه کاربری

در اندروید 8.0 یا بالاتر، سیستم کادرهای گفتگوی زیر را نشان می‌دهد تا فردی که از دستگاه استفاده می‌کند از VPN همیشه روشن آگاه شود:

  • وقتی اتصالات VPN همیشه روشن قطع می‌شوند یا نمی‌توانند وصل شوند، افراد یک اعلان غیرقابل رد کردن را مشاهده می‌کنند. با ضربه زدن روی اعلان، گفتگویی نشان داده می شود که بیشتر توضیح می دهد. وقتی VPN دوباره وصل شود یا کسی گزینه VPN همیشه روشن را خاموش کند، اعلان ناپدید می شود.
  • VPN همیشه روشن به شخصی که از دستگاهی استفاده می‌کند اجازه می‌دهد اتصالات شبکه‌ای را که از VPN استفاده نمی‌کنند مسدود کند. هنگام روشن کردن این گزینه، برنامه تنظیمات به افراد هشدار می دهد که قبل از اتصال VPN به اینترنت متصل نیستند. برنامه تنظیمات از شخصی که از دستگاه استفاده می کند می خواهد ادامه یا لغو کند.

از آنجا که سیستم (و نه یک شخص) یک اتصال همیشه روشن را شروع و متوقف می کند، باید رفتار و رابط کاربری برنامه خود را تطبیق دهید:

  1. هر رابط کاربری را که اتصال را قطع می کند غیرفعال کنید زیرا سیستم و برنامه تنظیمات اتصال را کنترل می کنند.
  2. هر پیکربندی را بین شروع هر برنامه ذخیره کنید و یک اتصال را با آخرین تنظیمات پیکربندی کنید. از آنجایی که سیستم برنامه شما را در صورت تقاضا راه اندازی می کند، شخصی که از دستگاه استفاده می کند ممکن است همیشه نخواهد یک اتصال را پیکربندی کند.

همچنین می توانید از تنظیمات مدیریت شده برای پیکربندی اتصال استفاده کنید. پیکربندی های مدیریت شده به مدیر فناوری اطلاعات کمک می کند VPN شما را از راه دور پیکربندی کند.

تشخیص همیشه روشن

Android برای تأیید اینکه آیا سیستم سرویس VPN شما را راه‌اندازی کرده است یا خیر شامل API نمی‌شود. اما، وقتی برنامه شما هر نمونه سرویسی را که شروع می‌کند پرچم‌گذاری می‌کند، می‌توانید فرض کنید که سیستم سرویس‌های بدون پرچم را برای VPN همیشه روشن شروع کرده است. در اینجا یک مثال است:

  1. یک نمونه Intent برای راه اندازی سرویس VPN ایجاد کنید.
  2. با قرار دادن یک مورد اضافی در intent، سرویس VPN را پرچم گذاری کنید.
  3. در متد onStartCommand() سرویس، به دنبال پرچم در موارد اضافی آرگومان intent بگردید.

اتصالات مسدود شده

شخصی که از دستگاه استفاده می کند (یا مدیر فناوری اطلاعات) می تواند تمام ترافیک را مجبور به استفاده از VPN کند. سیستم هر ترافیک شبکه ای را که از VPN استفاده نمی کند مسدود می کند. افرادی که از دستگاه استفاده می‌کنند می‌توانند اتصالات مسدود کردن بدون سوئیچ VPN را در پانل گزینه‌های VPN در تنظیمات پیدا کنند.

انصراف از همیشه روشن

اگر برنامه شما در حال حاضر نمی‌تواند VPN همیشه روشن را پشتیبانی کند، می‌توانید با تنظیم SERVICE_META_DATA_SUPPORTS_ALWAYS_ON فوق‌داده سرویس روی false (در Android نسخه 8.1 یا بالاتر) انصراف دهید. مثال مانیفست برنامه زیر نحوه افزودن عنصر فراداده را نشان می دهد:

<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 همیشه روشن انصراف می دهد، سیستم گزینه هایی را که UI کنترل می کند در تنظیمات غیرفعال می کند.

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()

جاوا

// The apps that will have access to the VPN.
String[] appPackages = {
    "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.
VpnService.Builder builder = new VpnService.Builder();
PackageManager packageManager = getPackageManager();
for (String appPackage: appPackages) {
  try {
    packageManager.getPackageInfo(appPackage, 0);
    builder.addAllowedApplication(appPackage);
  } catch (PackageManager.NameNotFoundException e) {
    // The app isn't installed.
  }
}

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

برنامه های مجاز

برای افزودن یک برنامه به لیست مجاز، VpnService.Builder.addAllowedApplication() را فراخوانی کنید. اگر لیست شامل یک یا چند برنامه باشد، فقط برنامه های موجود در لیست از VPN استفاده می کنند. همه برنامه‌های دیگر (که در لیست نیستند) از شبکه‌های سیستمی استفاده می‌کنند که گویی VPN در حال اجرا نیست. وقتی لیست مجاز خالی است، همه برنامه ها از VPN استفاده می کنند.

برنامه های غیر مجاز

برای افزودن یک برنامه به لیست غیر مجاز، VpnService.Builder.addDisallowedApplication() را فراخوانی کنید. برنامه‌های غیرمجاز از شبکه‌سازی سیستم استفاده می‌کنند که انگار VPN در حال اجرا نیست—همه برنامه‌های دیگر از VPN استفاده می‌کنند.

دور زدن VPN

VPN شما می تواند به برنامه ها اجازه دهد VPN را دور بزنند و شبکه خود را انتخاب کنند. برای دور زدن VPN، هنگام ایجاد یک رابط VPN، VpnService.Builder.allowBypass() را فراخوانی کنید. پس از راه اندازی سرویس VPN خود نمی توانید این مقدار را تغییر دهید. اگر یک برنامه فرآیند یا سوکت خود را به یک شبکه خاص متصل نکند، ترافیک شبکه برنامه از طریق VPN ادامه می‌یابد.

برنامه‌هایی که به یک شبکه خاص متصل می‌شوند، وقتی کسی ترافیکی را که از طریق VPN نمی‌گذرد مسدود می‌کند، اتصال ندارند. برای ارسال ترافیک از طریق یک شبکه خاص، برنامه‌ها روش‌هایی مانند ConnectivityManager.bindProcessToNetwork() یا Network.bindSocket() قبل از اتصال سوکت فراخوانی می‌کنند.

کد نمونه

پروژه متن باز Android شامل یک برنامه نمونه به نام ToyVPN است. این برنامه نحوه راه اندازی و اتصال یک سرویس VPN را نشان می دهد.