გაყოფილი გვირაბი Android VpnService-ში: რისი მარშრუტიზაცია შეიძლება სინამდვილეში
VpnService გადასცემს მხოლოდ იმ პაკეტებს, რომლებსაც builder თავის დესკრიპტორში აგზავნის. შეამოწმეთ პრეფიქსები, პროქსის ავთენტიფიკაცია, DNS და აპების სიები, სანამ გაყოფილ გვირაბს დაადანაშაულებთ.
გაყოფილი გვირაბი VpnService-ის აპში რამდენიმე კონკრეტულ ადგილას იშლება და თითოეულის შეკეთება განსხვავებულია. კლიენტი მხოლოდ ერთ წარუმატებელ დანიშნულებას აჩვენებს, ამიტომ პირველი ამოცანაა გაარკვიოთ, რომელ საზღვარს ეჯახებით: builder-ის მიღებული მარშრუტები, პროქსი, რომელსაც ვერ ამოწმებთ, resolver, რომელიც მხოლოდ გამოაცხადეთ, თუ აპების ის სია, რომელსაც პლატფორმა თითოეულ მომხმარებელზე იყენებს. სწორედ ამიტომ მაქვს ოთხსაფეხურიანი შემოწმება. ის ქვემოთ მოცემული თანმიმდევრობით მიდის და პირველივე წარუმატებლობაზე ჩერდება.
დაადასტურეთ, რა გადასცა builder-მა პლატფორმას
VpnService.Builder დეკლარაციების სიაა. establish() აბრუნებს ParcelFileDescriptor-ს ვირტუალური ინტერფეისისთვის და პლატფორმა ამ დესკრიპტორში მხოლოდ იმ ტრაფიკს აგზავნის, რომელიც addAddress-ისა და addRoute-ის პრეფიქსებს ემთხვევა; addDnsServer კლიენტებს ეუბნება, რომელ resolver-ს ჰკითხონ. დანიშნულება, რომელიც თქვენ მიერ დამატებული ყველა პრეფიქსის გარეთაა, არასდროს აღწევს თქვენს აპამდე, რაც არ უნდა დაჰპირდეს ინტერფეისი. ეს ერთი ფაქტი ხსნის გატეხილი გაყოფილი გვირაბის შესახებ შეტყობინებების უმეტესობას კოდის შეცვლამდე.
დაალოგეთ builder-ის მიერ შედგენილი მისამართების, მარშრუტებისა და DNS-ის სიები და შეადარეთ ისინი წარუმატებელ დანიშნულებებს. მართვის ჰოსტისთვის დაკარგული /32 გაცილებით ხშირია, ვიდრე გატეხილი გვირაბი. თუ სია სწორია, განაგრძეთ; თუ არა, დანარჩენი სია ფუჭი შრომაა.
excludeRoute მხოლოდ API 33-დან არსებობს
დანიშნულებაზე დაფუძნებული გამორიცხვა ყველაზე ხშირად მხარდაჭერის თემად ქცეული მოთხოვნაა. builder.excludeRoute(IpPrefix) API 33-ში გამოჩნდა; მანამდე ერთადერთი მხარდაჭერილი მიახლოებაა მარშრუტების დამატება ყველაფრისთვის გამორიცხული პრეფიქსის გარდა, რაც ნიშნავს, რომ დამატებითი პრეფიქსები თავად უნდა გამოთვალოთ. ორი გამოძახება სარკული ანარეკლი არ არის: ერთი აკლებს ინტერფეისის მარშრუტების სიმრავლეს, მეორე სიმრავლეს ნულიდან აშენებს, და ძველი მოწყობილობა უცნობ გამოძახებას გადააგდებს ან ჩუმად უგულებელყოფს. განტოტეთ Build.VERSION.SDK_INT-ის მიხედვით და ორივე გზა რეალურ ტექნიკაზე გამოსცადეთ.
დამატებითი პრეფიქსების გამოთვლისას დააკვირდით პრეფიქსების გასწორებას. /16 მარშრუტის შიგნით /24-ის გამორიცხვას სჭირდება დამფარავი პრეფიქსები, რომლებიც ნამდვილად აღწერს დარჩენილ ნაწილს და არა ერთი დამახინჯებული /8. დაალოგეთ საბოლოო სია და დაადასტურეთ, რომ გამორიცხული ჰოსტი მაინც იხსნება გვირაბის გარეთ არსებული გზით.
პროქსის სამართავები ვერ გამოისახება
setHttpProxy(ProxyInfo) ინტერფეისს HTTP ან SOCKS პროქსისკენ მიმართავს და PAC URL-ს შეუძლია თითოეული დანიშნულებისთვის მათ შორის არჩევა. არც ProxyInfo და არც PAC ფორმატი არ ატარებს მომხმარებლის სახელსა და პაროლს, ამიტომ ავთენტიფიკაციის მომთხოვნი პროქსი პლატფორმის API-ს გარეთ რჩება. აპები, რომლებსაც ეს სჭირდებათ, ჩვეულებრივ ჩაშენებენ მომხმარებლის სივრცის სტეკს, tun2socks-ს ან მსგავს ძრავას და 407 გამოწვევას თავად პასუხობენ.
ამ არჩევანს შედეგები აქვს. მომხმარებლის სივრცის სტეკი მხოლოდ იმ ტრაფიკს ხედავს, რომელსაც მისკენ მიმართავთ, ამიტომ მან იმავე დესკრიპტორის საზღვრებში უნდა იცხოვროს და მემკვიდრეობით იღებს იმავე protect() მოთხოვნას საკუთარი uplink სოკეტისთვის. გამოტოვეთ protect() და გვირაბი საკუთარ ტრანსპორტს დაიჭერს, რაც ჩანს როგორც ხელის ჩამორთმევა, რომელიც პირველი პაკეტის შემდეგ ჩერდება. გამოსცადეთ პროქსის წინააღმდეგ, რომელიც სამართავებს მოითხოვს, რადგან ღია პროქსი პლატფორმის გზაზე გადის და არაფერს ამტკიცებს.
აპების სიები და DNS ასევე დეკლარაციებია
Builder-ს აქვს როგორც addAllowedApplication, ისე addDisallowedApplication და ისინი ერთმანეთს გამორიცხავენ: ორივეს გამოძახება გამონაკლისს ისვრის. ნებისმიერ შემთხვევაში სია ინტერფეისის დაყენებისას თითოეულ მომხმარებელზე გამოიყენება, ამიტომ სამუშაო პროფილის აპს სამუშაო პროფილის გვირაბი მართავს და არა თქვენი. რასაც ვერც ერთი API არ გაძლევთ, სხვა აპის გადაწყვეტილების კონტროლია: პაკეტს გარედან თქვენს გვირაბში ვერ შეაგდებთ და უკვე აქტიურ VPN-ს ვერ დახურავთ ან ხელახლა ვერ დააკონფიგურებთ.
DNS იმავე ნიმუშს მიჰყვება. addDnsServer აცხადებს resolver-ის მისამართს; ის თქვენს აპს არ აძლევს უფლებას უპასუხოს მოთხოვნებს. თუ სახელების დამოუკიდებლად გადაწყვეტა გსურთ, გაუშვით resolver და მისი მისამართი გვირაბში გაატარეთ. აპები, რომლებიც DoT-ს ან DoH-ს იყენებენ ფიქსირებულ ჰოსტის სახელზე, შეიძლება გვერდი აუარონ თქვენს გამოცხადებულ მისამართს, ამიტომ სახელების გადაწყვეტა საუკეთესო მცდელობად მიიჩნიეთ, თუ მოთხოვნის გზასაც არ ჩაჭრით.
Manifest, always-on და ოთხსაფეხურიანი ტესტი
უფლებამოსილება manifest-ში დეკლარირდება: BIND_VPN_SERVICE, android.net.VpnService intent ფილტრი და foreground სერვისი, თუ გვირაბმა ფონზე უნდა გაძლოს. Always-on VPN დამატებით სუფთა onRevoke-ს მოითხოვს, რადგან სისტემა მას მაშინ იძახებს, როცა მომხმარებელი პარამეტრებს ცვლის ან სხვა აპი იღებს კონტროლს.
შეასრულეთ შემოწმებები თანმიმდევრობით და გაჩერდით პირველ წარუმატებლობაზე. პირველი: გამოიტანეთ builder-ისთვის გადაცემული მისამართების, მარშრუტებისა და DNS-ის სიები. მეორე: დაადასტურეთ, რომ establish()-მა დესკრიპტორი დააბრუნა და ტრანსპორტის სოკეტი დაცულია. მესამე: მოითხოვეთ ერთი დანიშნულება ჩართული პრეფიქსის შიგნით და ერთი გამორიცხულის შიგნით. მეოთხე: შეამოწმეთ, რომელმა resolver-მა რეალურად უპასუხა, ჰკითხეთ ჰოსტის სახელი, რომლის გადაწყვეტაც მხოლოდ თქვენს resolver-ს შეუძლია.
დასრულებული ხელის ჩამორთმევა არც ერთ შემდგომ საფეხურს არ ამტკიცებს. თითოეული შემოწმება ერთ ფენას გამოყოფს, ამიტომ მეორე საფეხურზე წარმატება მეოთხეზე არაფერს ამბობს.
VpnService.Builder-ის საცნობარი builder-ის მთელ ზედაპირს ჩამოთვლის. ყველაფერი, რაც ამის მიღმა გჭირდებათ, სხვა API, სხვა აპი ან თქვენივე მომხმარებლის სივრცის სტეკია და ეს დიზაინის გადაწყვეტილებაა და არა გაყოფილი გვირაბის ხარვეზი.