VPN

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

Genel Bakış

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

Android'de, bazen eski VPN olarak adlandırılan yerleşik bir (PPTP ve L2TP/IPSec) VPN istemcisi bulunur. Android 4.0 (API düzeyi 14), uygulama geliştiricilerin kendi VPN çözümlerini sunabilmeleri için API'ler 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ğıdaki nedenlerden biri için VPN uygulaması oluşturur:

  • 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 istemcisi ele alınmamaktadır.

Kullanıcı deneyimi

Android, kullanıcıların VPN çözümünüzü yapılandırmasına, başlatmasına ve durdurmasına yardımcı olan bir kullanıcı arayüzü (UI) sunar. Sistem kullanıcı arayüzü, cihazı kullanan kişiyi etkin bir VPN bağlantısı olduğu konusunda da bilgilendirir. Android, VPN bağlantıları için aşağıdaki kullanıcı arayüzü bileşenlerini gösterir:

  • Bir VPN uygulaması ilk kez etkinleştirilmeden önce sistem, bağlantı isteği iletişim kutusu gösterir. İletişim kutusu, cihazı kullanan kişiden VPN'ye güvendiğini onaylamasını ve isteği kabul etmesini ister.
  • VPN ayarları ekranında (Ayarlar > Ağ ve internet > VPN), kullanıcının bağlantı isteklerini kabul ettiği VPN uygulamaları gösterilir. Sistem seçeneklerini yapılandırmak veya VPN'yi unutmak için bir düğme var.
  • Hızlı Ayarlar tepsisinde, bağlantı etkin olduğunda bir bilgi paneli gösterilir. Etikete dokunduğunuzda daha fazla bilgi ve Ayarlar'a yönlendiren bir bağlantı içeren bir iletişim kutusu gösterilir.
  • Durum çubuğunda, etkin bir bağlantı olduğunu gösteren 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. Uygulamalarda aşağıdaki kullanıcı arayüzü gösterilmelidir:

  • Bağlantıyı manuel olarak başlatma ve durdurma kontrolleri. 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ırmalarına izin verir.
  • Hizmet etkin olduğunda kapatılamayan bir bildirim. Bildirim, bağlantı durumunu gösterebilir veya ağ istatistikleri gibi daha fazla bilgi sağlayabilir. Bildirime dokunulduğunda uygulamanız ön plana getirilir. 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'inizi başlatmak ve durdurmak, ayrıca bağlantı durumunu izlemek için kullandığı bir VPN hizmeti oluşturursunuz. VPN hizmetiniz VpnService sınıfından devralıyor.

Hizmet, VPN ağ geçidi bağlantıları ve bunların yerel cihaz arayüzleri için kapsayıcı görevi de 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 yerel bir TUN arayüzü oluşturma şeklini gösteren blok mimari diyagramı.

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

  • Yerel arayüzün dosya tanımlayıcısından giden IP paketlerini okur, şifreler ve VPN ağ geçidine gönderir.
  • Gelen paketleri (VPN ağ geçidinden alınan ve şifresi çözülen) 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ıldığında mevcut hizmet otomatik olarak durdurulur.

Hizmet ekleme

Uygulamanıza VPN hizmeti eklemek için VpnService sınıfından devralınan bir Android hizmeti oluşturun. Uygulamanızın manifest dosyasında VPN hizmetini aşağıdaki eklemelerle bildirin:

  • Hizmetinizi BIND_VPN_SERVICE izniyle koruyun. Böylece hizmetinize yalnızca sistem bağlanabilir.
  • Sistemin hizmetinizi bulabilmesi için hizmeti "android.net.VpnService" intent filtresiyle duyurun.

Aşağıdaki örnekte, hizmeti uygulama manifest dosyanızda 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 hizmeti beyan ettiğine göre sistem, uygulamanızın VPN hizmetini gerektiğinde otomatik olarak başlatıp durdurabilir. Örneğin, her zaman açık VPN çalışırken sistem, hizmetinizi kontrol eder.

Hizmet hazırlama

Uygulamayı kullanıcının mevcut VPN hizmeti olmaya hazırlamak için VpnService.prepare() yöntemini çağırın. Cihazı kullanan kişi uygulamanıza henüz izin vermediyse yöntem bir etkinlik intent'i döndürür. Bu intent'i, izin isteyen bir sistem etkinliği başlatmak için kullanırsınız. Sistem, kamera veya kişi erişimi gibi diğer izin iletişim kutularına benzer bir iletişim kutusu gösterir. Uygulamanız zaten hazırsa yöntem null değerini döndürür.

Aynı anda yalnızca bir uygulama hazırlanmış VPN hizmeti olabilir. Uygulamanız yöntemi son çağırdığından beri bir kullanıcı VPN hizmeti olarak farklı bir uygulama ayarlamış olabileceğinden 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ğlama

Hizmet çalışmaya başladıktan sonra VPN ağ geçidine bağlı yeni bir yerel arayüz oluşturabilirsiniz. İzin istemek ve hizmetinizi VPN ağ geçidine bağlamak için aşağıdaki adımları sırayla tamamlamanız gerekir:

  1. İzin istemek için VpnService.prepare() numarasını arayın (gerekirse).
  2. Uygulamanızın tünel soketini sistem VPN'sinin dışında tutmak ve dairesel bağlantıyı önlemek için VpnService.protect() yöntemini çağırın.
  3. Uygulamanızın tünel soketini VPN ağ geçidine bağlamak için DatagramSocket.connect() yöntemini çağırın.
  4. Cihazda VPN trafiği için 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, el sıkışma sırasında normalde yerel TUN arayüzü için ayarlar önerir. Uygulamanız, bir hizmeti yapılandırmak için aşağıdaki örnekte gösterildiği gibi VpnService.Builder yöntemlerini çağırır:

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

Uygulama başına 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 aşağıdaki VpnService.Builder değerlerini eklemeniz gerekir:

addAddress()
Sistemin yerel TUN arayüz adresi olarak atadığı bir alt ağ maskesiyle birlikte en az bir IPv4 veya IPv6 adresi ekleyin. Uygulamanız genellikle el sıkışma sırasında bir VPN ağ geçidinden IP adreslerini ve alt ağ maskelerini alır.
addRoute()
Sistemin trafiği VPN arayüzü üzerinden göndermesini istiyorsanız en az bir rota ekleyin. Rotalar, hedef adreslere 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 paket yazmak ve arabellekten paket okumak için kullandığı bir ParcelFileDescriptor örneği 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, sistemin seçili VPN'sinin ve etkin bağlantıların durumunu izlemelidir. Cihazı kullanan kişinin değişikliklerden haberdar olması için uygulamanızın kullanıcı arayüzünü (UI) güncelleyin.

Hizmet başlatma

VPN hizmetiniz aşağıdaki şekillerde başlatılabilir:

  • Uygulamanız hizmeti başlatır. Bu genellikle bir kullanıcının bağlan düğmesine dokunmasıyla gerçekleşir.
  • Her zaman açık VPN etkin olduğu için sistem hizmeti başlatır.

Uygulamanız, startService() sınıfına bir intent ileterek VPN hizmetini başlatır. Daha fazla bilgi edinmek için Hizmet başlatma başlıklı makaleyi 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 geçirmeniz gerekir. Daha fazla bilgi edinmek için Ön planda hizmet çalıştırma başlıklı makaleyi okuyun.

Hizmeti durdurma

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

  • VPN uygulamasının bağlantısını keser veya uygulamayı unutur.
  • Etkin bir 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ü trafiği yönlendiriyordur. Aşağıdaki kaynakları güvenle silebilirsiniz:

Her zaman açık VPN

Android, cihaz başlatıldığında bir VPN hizmeti başlatabilir ve cihaz açıkken bunun çalışmaya devam etmesini sağlayabilir. Her zaman açık VPN olarak adlandırılan 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 VPN hizmetiniz sorumludur. Her zaman açık VPN, VPN kullanmayan bağlantıları da engelleyebilir.

Kullanıcı deneyimi

Android 8.0 veya sonraki sürümlerde sistem, cihazı kullanan kişinin her zaman açık VPN'den haberdar olması için 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ılamayan bir bildirim görür. Bildirime dokunduğunuzda daha fazla bilgi veren bir iletişim kutusu gösterilir. VPN yeniden bağlandığında veya birisi her zaman açık VPN seçeneğini kapattığında bildirim kaybolur.
  • Her zaman açık VPN, cihazı kullanan kişinin VPN kullanmayan tüm ağ bağlantılarını engellemesine olanak tanır. Bu seçenek etkinleştirildiğinde Ayarlar uygulaması, VPN bağlanmadan önce kullanıcılara internet bağlantılarının olmadığını bildirir. Ayarlar uygulaması, cihazı kullanan kişiden devam etmesini veya işlemi iptal etmesini ister.

Her zaman açık bağlantıyı bir kişi değil, sistem başlattığı ve durdurduğu için uygulamanızın davranışını ve kullanıcı arayüzünü uyarlamanız gerekir:

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

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

Her zaman açık algılama

Android, sistemin VPN hizmetinizi başlatıp başlatmadığını onaylayan API'ler içermez. Ancak uygulamanız başlattığı hizmet örneklerini işaretlediğinde, sistemin her zaman açık VPN için işaretsiz hizmetler başlattığını varsayabilirsiniz. Örnek:

  1. VPN hizmetini başlatmak için bir Intent örneği oluşturun.
  2. Intent'e ek bir öğe koyarak 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 BT yöneticisi), tüm trafiği VPN kullanmaya zorlayabilir. Sistem, VPN kullanmayan tüm 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ırakma

Uygulamanız şu anda her zaman açık VPN'i destekleyemiyorsa SERVICE_META_DATA_SUPPORTS_ALWAYS_ON hizmet meta verilerini false olarak ayarlayarak (Android 8.1 veya sonraki sürümlerde) 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'i 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ı, yüklü uygulamalardan hangilerinin VPN bağlantısı üzerinden trafik göndermesine izin verileceğini filtreleyebilir. İzin verilenler listesi veya izin verilmeyenler listesi oluşturabilirsiniz ancak ikisini birden oluşturamazsınız. İzin verilenler veya izin verilmeyenler listesi oluşturmazsanız sistem tüm ağ trafiğini VPN üzerinden gönderir.

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

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

İzin verilen uygulamalar

İzin verilenler listesine uygulama eklemek için VpnService.Builder.addAllowedApplication() yöntemini çağırı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 kullanır.

İzin verilmeyen uygulamalar

İzin verilmeyenler listesine uygulama eklemek için VpnService.Builder.addDisallowedApplication() yöntemini çağırın. İzin verilmeyen uygulamalar, VPN çalışmıyormuş gibi sistem ağını kullanır. Diğer tüm uygulamalar VPN'i kullanır.

VPN'yi atla

VPN'iniz, uygulamaların VPN'yi atlayıp kendi ağlarını seçmesine izin verebilir. VPN'i atlamak için VPN arayüzü oluştururken VpnService.Builder.allowBypass() yöntemini çağırın. VPN hizmetinizi başlattıktan sonra bu değeri değiştiremezsiniz. Bir uygulama, işlemini veya soketini belirli bir ağa bağlamazsa uygulamanın ağ trafiği VPN üzerinden devam eder.

Belirli bir ağa bağlanan uygulamalar, VPN üzerinden geçmeyen trafiğin engellenmesi durumunda bağlantı kuramaz. Uygulamalar, trafiği belirli bir ağ üzerinden göndermek için soketi 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, VPN hizmetinin nasıl kurulacağını ve bağlanacağını gösterir.