VPN

Android, geliştiricilerin sanal özel ağ (VPN) çözümleri oluşturmaları için API'ler sağlar. Bu kılavuzu okuduktan sonra, Android destekli cihazlar için kendi VPN istemcinizi nasıl geliştireceğinizi ve test edeceğinizi öğreneceksiniz.

Genel bakış

VPN'ler, fiziksel olarak bir ağda bulunmayan cihazların o ağa güvenli bir şekilde erişmesine olanak tanır.

Android, bazen eski VPN olarak adlandırılan yerleşik (PPTP ve L2TP/IPSec) bir VPN istemcisi içerir. Android 4.0 (API Düzeyi 14), uygulama geliştiricilerin kendi VPN çözümlerini sağlayabilmeleri için API'leri kullanıma sunmuştur. VPN çözümünüzü, kullanıcıların cihaza yüklediği bir uygulama olarak paketlersiniz. Geliştiriciler normalde aşağıdakilerden biri nedeniyle bir VPN uygulaması oluştururlar:

  • Yerleşik istemcinin desteklemediği VPN protokollerini sunmak için.
  • Kullanıcıların karmaşık yapılandırma olmadan bir VPN hizmetine bağlanmalarına yardımcı olmak için.

Bu kılavuzun geri kalanında, VPN uygulamalarının (her zaman açık ve uygulama başına VPN dahil) nasıl geliştirileceği açıklanmakta olup yerleşik VPN istemcisini kapsamamaktadır.

Kullanıcı deneyimi

Android, VPN çözümünüzü yapılandırmaya, başlatmaya ve durdurmaya yardımcı olacak bir kullanıcı arayüzü sağlar. Sistem kullanıcı arayüzü, cihazı kullanan kişinin etkin bir VPN bağlantısının farkında olmasını da sağlar. Android, VPN bağlantıları için aşağıdaki kullanıcı arayüzü bileşenlerini gösterir:

  • Bir VPN uygulaması ilk kez etkin hale gelmeden önce sistem bir bağlantı isteği iletişim kutusu görüntüler. İletişim kutusunda, cihazı kullanan kişiden VPN'e güvendiğini ve isteği kabul ettiğini doğrulaması istenir.
  • VPN ayarları ekranı (Ayarlar > Ağ ve İnternet > VPN), kişinin bağlantı isteklerini kabul ettiği VPN uygulamalarını gösterir. Sistem seçeneklerini yapılandırmak veya VPN'yi unutmak için bir düğme var.
  • Bağlantı etkin olduğunda Hızlı Ayarlar tepsisinde bir bilgi paneli gösterilir. Etikete dokunduğunuzda daha fazla bilgi ve Ayarlar bağlantısının bulunduğu bir iletişim kutusu görüntülenir.
  • Durum çubuğunda, etkin bir bağlantıyı belirten bir VPN (anahtar) simgesi bulunur.

Uygulamanızın, cihazı kullanan kişinin hizmetinizin seçeneklerini yapılandırabilmesi için bir kullanıcı arayüzü de sağlaması gerekir. Örneğin, çözümünüzün hesap kimlik doğrulama ayarlarını yakalaması gerekebilir. Uygulamalar aşağıdaki kullanıcı arayüzünü göstermelidir:

  • Bağlantıyı manuel olarak başlatmayı ve durdurmayı kontrol eder. Her zaman açık VPN gerektiğinde bağlanabilir, ancak kullanıcıların VPN'inizi ilk kez kullandıklarında bağlantıyı yapılandırmasına olanak tanır.
  • Hizmet etkin olduğunda gönderilen kapatılamayan bir bildirim. Bildirim, bağlantı durumunu gösterebilir veya ağ istatistikleri gibi daha fazla bilgi sağlayabilir. Bildirime dokunduğunuzda uygulamanız ön plana taşınır. Hizmet devre dışı bırakıldıktan sonra bildirimi kaldırın.

VPN hizmeti

Uygulamanız bir kullanıcının (veya iş profilinin) sistem ağını bir VPN ağ geçidine bağlar. Her kullanıcı (veya iş profili) farklı bir VPN uygulaması çalıştırabilir. Sistemin VPN'nizi başlatmak, durdurmak ve bağlantı durumunu izlemek için kullandığı bir VPN hizmeti oluşturursunuz. VPN hizmetiniz VpnService öğesinden devralır.

Hizmet, ayrıca VPN ağ geçidi bağlantıları ve bunların yerel cihaz arayüzleri için kapsayıcınız görevi görür. Hizmet örneğiniz yeni bir yerel arayüz oluşturmak için VpnService.Builder yöntemlerini çağırır.

Şekil 1. VpnService, Android ağını VPN ağ geçidine nasıl bağlar?
VpnService'in sistem ağında nasıl yerel TUN arayüzü oluşturduğunu gösteren blok mimari şeması.

Uygulamanız, cihazı VPN ağ geçidine bağlamak için aşağıdaki verileri aktarır:

  • Giden IP paketlerini yerel arayüzün dosya açıklayıcısından okur, şifreler ve VPN ağ geçidine gönderir.
  • Gelen paketleri (VPN ağ geçidinden alınan ve şifresi çözülmüş) yerel arayüzün dosya tanımlayıcısına yazar.

Kullanıcı veya profil başına yalnızca bir etkin hizmet vardır. Yeni bir hizmet başlatılırken mevcut bir hizmet otomatik olarak durdurulur.

Hizmet ekle

Uygulamanıza VPN hizmeti eklemek için VpnService'ten devralan bir Android hizmeti oluşturun. Aşağıdaki eklemelerle uygulama manifest dosyanızda VPN hizmetini beyan edin:

  • Hizmetinize yalnızca sistemin bağlanabilmesi için BIND_VPN_SERVICE izniyle hizmeti koruyun.
  • Sistemin hizmetinizi bulabilmesi için "android.net.VpnService" intent filtresiyle hizmeti tanıtın.

Bu örnekte, uygulama manifest dosyanızda hizmeti nasıl beyan edebileceğiniz gösterilmektedir:

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

Uygulamanız artık hizmeti beyan ettiğine göre sistem gerektiğinde uygulamanızın VPN hizmetini otomatik olarak başlatıp durdurabilir. Örneğin, her zaman açık VPN çalışırken hizmetinizi sistem kontrol eder.

Hizmet hazırlama

Uygulamayı kullanıcının mevcut VPN hizmeti olmaya hazırlamak için VpnService.prepare() numaralı telefonu arayın. Cihazı kullanan kişi uygulamanıza önceden izin vermemişse yöntem bir etkinlik amacı döndürür. Bu niyeti, izin isteyen bir sistem etkinliği başlatmak için kullanırsınız. Sistem, kamera veya kişiler erişimi gibi diğer izin iletişim kutularına benzeyen bir iletişim kutusu gösterir. Uygulamanız zaten hazırsa yöntem null değerini döndürür.

Şu anda hazırlanan VPN hizmeti olarak yalnızca bir uygulama kullanılabilir. Uygulamanızın bu yöntemi son kez çağırmasından bu yana bir kişi, VPN hizmeti olarak farklı bir uygulamayı ayarlamış olabileceği için her zaman VpnService.prepare() yöntemini çağırın. Daha fazla bilgi edinmek için Hizmet yaşam döngüsü bölümünü inceleyin.

Hizmet bağlayın

Hizmet çalıştığında, bir VPN ağ geçidine bağlı yeni bir yerel arayüz oluşturabilirsiniz. İzin istemek ve hizmetinize VPN ağ geçidine bağlanmak için adımları aşağıdaki sırayla tamamlamanız gerekir:

  1. Gerektiğinde izin istemek için VpnService.prepare() numaralı telefonu arayın.
  2. Uygulamanızın tünel bağlantısını sistem VPN'sinin dışında tutmak ve döngüsel bağlantıdan kaçınmak için VpnService.protect() numaralı telefonu arayın.
  3. Uygulamanızın tünel yuvasını VPN ağ geçidine bağlamak için DatagramSocket.connect() numaralı telefonu arayın.
  4. VPN trafiği için cihazda yeni bir yerel TUN arayüzü yapılandırmak üzere VpnService.Builder yöntemlerini çağırın.
  5. Sistemin yerel TUN arayüzünü oluşturması ve trafiği arayüz üzerinden yönlendirmeye başlaması için VpnService.Builder.establish() yöntemini çağırın.

VPN ağ geçidi normalde el sıkışma sırasında yerel TUN arayüzü için ayarlar önerir. Uygulamanız, bir hizmeti aşağıdaki örnekte gösterildiği gibi yapılandırmak için VpnService.Builder yöntemlerini çağırır:

Kotlin

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

Java

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

Uygulama bazında VPN bölümündeki örnekte, daha fazla seçenek içeren bir IPv6 yapılandırması gösterilmektedir. Yeni bir arayüz oluşturabilmek için önce aşağıdaki VpnService.Builder değerlerini eklemeniz gerekir:

addAddress()
Sistemin yerel TUN arayüz adresi olarak atadığı alt ağ maskesiyle birlikte en az bir IPv4 veya IPv6 adresi ekleyin. Uygulamanız genellikle el sıkışma sırasında IP adreslerini ve alt ağ maskelerini bir VPN ağ geçidinden alır.
addRoute()
Sistemin trafiği VPN arayüzü üzerinden göndermesini istiyorsanız en az bir rota ekleyin. Rotalar, hedef adreslerine göre filtrelenir. Tüm trafiği kabul etmek için 0.0.0.0/0 veya ::/0 gibi açık bir rota ayarlayın.

establish() yöntemi, uygulamanızın arayüzün arabelleğine/arabelleğinden paket okumak ve yazmak için kullandığı bir ParcelFileDescriptor örneğini döndürür. Uygulamanız hazır değilse veya birisi izni iptal ederse establish() yöntemi null değerini döndürür.

Hizmet yaşam döngüsü

Uygulamanız, sistem tarafından seçilen VPN'lerin ve etkin bağlantıların durumunu izlemelidir. Cihazı kullanan kişinin değişikliklerden haberdar olmasını sağlamak için uygulamanızın kullanıcı arayüzünü güncelleyin.

Hizmet başlatma

VPN hizmetiniz aşağıdaki yöntemlerle başlatılabilir:

  • Uygulamanız, genellikle bir kişi bağlan düğmesine dokunduğu için hizmeti başlatır.
  • Her zaman açık VPN ayarı açık olduğu için sistem hizmeti başlatır.

Uygulamanız, startService()'e bir amaç ileterek VPN hizmetini başlatır. Daha fazla bilgi için Hizmet başlatma bölümünü okuyun.

Sistem, onStartCommand() yöntemini çağırarak hizmetinizi arka planda başlatır. Ancak Android, 8.0 (API Düzeyi 26) veya sonraki sürümlerde arka plan uygulamalarına kısıtlamalar getirir. Bu API Düzeylerini destekliyorsanız Service.startForeground() yöntemini çağırarak hizmetinizi ön plana taşımanız gerekir. Daha fazla bilgi edinmek için Bir hizmeti ön planda çalıştırma konusunu okuyun.

Bir hizmeti durdurma

Cihazı kullanan bir kişi, uygulamanızın kullanıcı arayüzünü kullanarak hizmetinizi durdurabilir. Bağlantıyı kapatmak yerine hizmeti durdurun. Cihazı kullanan kişi, Ayarlar uygulamasının VPN ekranında aşağıdakileri yaptığında sistem etkin bir bağlantıyı da durdurur:

  • VPN uygulamasının bağlantısı kesildiğinde veya unutulduğunda
  • etkin bağlantı için her zaman açık VPN'yi kapatır

Sistem, hizmetinizin onRevoke() yöntemini çağırır ancak bu çağrı ana iş parçacığında gerçekleşmeyebilir. Sistem bu yöntemi çağırdığında alternatif bir ağ arayüzü zaten trafiği yönlendiriyor demektir. Aşağıdaki kaynakları güvenli bir şekilde imha edebilirsiniz:

Her zaman açık VPN

Android, cihaz başlatıldığında bir VPN hizmeti başlatabilir ve cihaz açıkken de çalışmaya devam edebilir. Her zaman açık VPN adlı bu özellik, Android 7.0 (API Düzeyi 24) veya sonraki sürümlerde kullanılabilir. Android, hizmet yaşam döngüsünü sürdürürken VPN ağ geçidi bağlantısından sorumlu olan VPN hizmetinizdir. Her zaman açık VPN, VPN kullanmayan bağlantıları da engelleyebilir.

Kullanıcı deneyimi

Android 8.0 veya sonraki sürümlerde, cihazı kullanan kişinin her zaman açık VPN'yi tanıması için sistem aşağıdaki iletişim kutularını gösterir:

  • Her zaman açık VPN bağlantıları kesildiğinde veya bağlanamadığında kullanıcılar kapatılamaz bir bildirim görür. Bildirime dokunduğunuzda daha fazla bilgi veren bir iletişim kutusu görüntülenir. VPN'e yeniden bağlandığınızda veya birisi her zaman açık VPN seçeneğini kapattığında bildirim kaybolur.
  • Her zaman açık VPN, cihazı kullanan kişinin VPN'i kullanmayan ağ bağlantılarını engellemesine olanak tanır. Bu seçeneği etkinleştirdiğinizde Ayarlar uygulaması, kullanıcıları VPN'e bağlanmadan internet bağlantıları olmadığı konusunda uyarır. Ayarlar uygulaması, cihazı kullanan kişiden devam etmesini veya iptal etmesini ister.

Her zaman açık bağlantıyı sistem (kişiler değil) başlatıp durdurduğundan, uygulamanızın davranışını ve kullanıcı arayüzünü uyarlamanız gerekir:

  1. Sistem ve Ayarlar uygulaması bağlantıyı kontrol ettiği için bağlantıyı kesen kullanıcı arayüzünü devre dışı bırakın.
  2. Her uygulama başlangıcı arasında tüm yapılandırmaları kaydedin ve en son ayarlarla bağlantı yapılandırın. Sistem, uygulamanızı istek üzerine başlattığından, cihazı kullanan kişi her zaman bağlantı yapılandırmak istemeyebilir.

Bir bağlantıyı yapılandırmak için yönetilen yapılandırmaları da kullanabilirsiniz. Yönetilen yapılandırmalar, BT yöneticisinin VPN'inizi uzaktan yapılandırmasına yardımcı olur.

Her zaman açık olduğunu algıla

Android, sistemin VPN hizmetinizi başlatıp başlatmadığını onaylayan API'ler içermez. Ancak, uygulamanız başladığı herhangi bir hizmet örneğini işaretlediğinde, sistemin her zaman açık VPN için işaretlenmemiş hizmetleri başlattığını varsayabilirsiniz. Örnek:

  1. VPN hizmetini başlatmak için bir Intent örneği oluşturun.
  2. Amaca fazladan bir şey ekleyerek VPN hizmetini işaretleyin.
  3. Hizmetin onStartCommand() yönteminde, intent bağımsız değişkeninin ekstralarında işareti arayın.

Engellenen bağlantılar

Cihazı kullanan bir kişi (veya bir BT yöneticisi) tüm trafiği VPN'i kullanmaya zorlayabilir. Sistem, VPN'yi kullanmayan ağ trafiğini engeller. Cihazı kullanan kişiler, Ayarlar'daki VPN seçenekleri panelinde VPN'siz bağlantıları engelle anahtarını bulabilir.

Her zaman açık özelliğini devre dışı bırak

Uygulamanız şu anda her zaman açık VPN'yi desteklemiyorsa SERVICE_META_DATA_SUPPORTS_ALWAYS_ON hizmet meta verilerini false değerine ayarlayarak (Android 8.1 veya sonraki sürümlerde) bu özelliği devre dışı bırakabilirsiniz. Aşağıdaki uygulama manifesti örneğinde, meta veri öğesinin nasıl ekleneceği gösterilmektedir:

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

Uygulamanız her zaman açık VPN'yi devre dışı bıraktığında sistem, Ayarlar'daki seçenekler kullanıcı arayüzü kontrollerini devre dışı bırakır.

Uygulama başına VPN

VPN uygulamaları, hangi yüklü uygulamaların VPN bağlantısı üzerinden trafik göndermesine izin verileceğini filtreleyebilir. İzin verilenlerin veya izin verilmeyenlerin listesini oluşturabilirsiniz, ikisini birden oluşturamazsınız. İzin verilenler veya izin verilmeyenler listeleri oluşturmazsanız sistem tüm ağ trafiğini VPN üzerinden gönderir.

Bağlantı kurulmadan önce VPN uygulamanız listeleri ayarlamalıdır. Listeleri değiştirmeniz gerekirse yeni bir VPN bağlantısı oluşturun. Bir uygulamayı bir listeye eklediğinizde cihaza yüklenmiş olması gerekir.

Kotlin

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

Java

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

İzin verilen uygulamalar

İzin verilenler listesine uygulama eklemek için VpnService.Builder.addAllowedApplication() numaralı telefonu arayın. Listede bir veya daha fazla uygulama varsa yalnızca listedeki uygulamalar VPN kullanır. Listede olmayan diğer tüm uygulamalar, VPN çalışmıyormuş gibi sistem ağlarını kullanır. İzin verilenler listesi boş olduğunda tüm uygulamalar VPN'yi kullanır.

İzin verilmeyen uygulamalar

İzin verilenler listesine uygulama eklemek için VpnService.Builder.addDisallowedApplication() numaralı telefonu arayın. İzin verilmeyen uygulamalar, VPN çalışmıyormuş gibi sistem ağını kullanır. Diğer tüm uygulamalar VPN'yi kullanır.

VPN'yi atla

VPN'iniz, uygulamaların VPN'i atlamasına ve kendi ağını seçmesine izin verebilir. VPN'yi atlamak için VPN arayüzü oluştururken VpnService.Builder.allowBypass() çağrısı yapın. VPN hizmetinizi başlattıktan sonra bu değeri değiştiremezsiniz. Bir uygulama, sürecini veya bir yuvasını belirli bir ağa bağlamazsa uygulamanın ağ trafiği VPN üzerinden devam eder.

Kullanıcılar VPN'den geçmeyen trafiği engellediğinde belirli bir ağa bağlanan uygulamaların bağlantısı olmaz. Trafiği belirli bir ağ üzerinden göndermek için uygulamalar, yuvayı bağlamadan önce ConnectivityManager.bindProcessToNetwork() veya Network.bindSocket() gibi yöntemleri çağırır.

Örnek kod

Android Açık Kaynak Projesi, ToyVPN adlı örnek bir uygulama içerir. Bu uygulama, bir VPN hizmetinin nasıl kurulacağını ve bağlanacağını göstermektedir.