Раздзеленае тунэляванне ў праграме на VpnService ламаецца ў некалькіх канкрэтных месцах, і кожнаму патрэбна сваё выпраўленне. Кліент паказвае толькі адзін пункт прызначэння, які не адпрацоўвае, таму спачатку трэба вызначыць, якую мяжу вы ўпрэцеся: маршруты, якія атрымаў builder, проксі, да якога не ўдаецца аўтэнтыфікавацца, рэзалвер, які вы толькі абвясцілі, або спіс праграм, які платформа прымяняе для кожнага карыстальніка. Менавіта таму я трымаю чатырохкрокавую праверку. Яна ідзе ў парадку ніжэй і спыняецца на першай памылцы.

Пацвердзіце, што builder перадаў платформе

VpnService.Builder — гэта спіс дэкларацый. establish() вяртае ParcelFileDescriptor для віртуальнага інтэрфейсу, і платформа накіроўвае ў гэты дэскрыптар толькі трафік, які адпавядае прэфіксам з addAddress і addRoute; addDnsServer паведамляе кліентам, які рэзалвер запытваць. Пункт прызначэння па-за ўсімі дададзенымі прэфіксамі ніколі не дасягне вашай праграмы, што б ні абяцаў інтэрфейс. Гэты адзіны факт тлумачыць большасць скаргаў на зламанае раздзеленае тунэляванне яшчэ да змяненняў у кодзе.

Залогіруйце спісы адрасоў, маршрутаў і DNS, якія сабраў ваш builder, і параўнайце іх з пунктамі прызначэння, што не адпрацоўваюць. Адсутны /32 для кіраўніцкага хоста сустракаецца значна часцей, чым зламаны тунэль. Калі спіс слушны, ідзіце далей; калі не, рэшта чэк-ліста — марная праца.

excludeRoute існуе толькі з API 33

Выключэнне па пункце прызначэння — гэта запыт, які часцей за ўсё ператвараецца ў тэму падтрымкі. builder.excludeRoute(IpPrefix) з'явіўся ў API 33; да гэтага адзінае падтрыманае набліжэнне — дадаць маршруты для ўсяго, акрамя выключанага прэфікса, а значыць, давядзецца самастойна вылічыць дапаўняльныя прэфіксы. Гэтыя два выклікі не люстэркавыя: адзін адымае з набору маршрутаў інтэрфейсу, другі будуе набор з нуля, а старэйшая прылада кіне выключэнне або моўчкі праігнаруе невядомы выклік. Разгаліноўвайце па Build.VERSION.SDK_INT і правярайце абодва шляхі на рэальным абсталяванні.

Калі вылічваеце дапаўненні, сачыце за выраўноўваннем прэфіксаў. Выключэнне /24 унутры маршруту /16 патрабуе пакрывальных прэфіксаў, якія сапраўды апісваюць астатнюю частку, а не адзін няправільны /8. Залогіруйце канчатковы спіс і праверце, што выключаны хост па-ранейшаму рэзалвіцца праз шлях па-за тунэлем.

Уліковыя даныя проксі немагчыма выразиць

setHttpProxy(ProxyInfo) накіроўвае інтэрфейс на HTTP- або SOCKS-проксі, а PAC-URL можа выбіраць паміж імі для кожнага пункта прызначэння. Ні ProxyInfo, ні фармат PAC не перадаюць імя карыстальніка і пароль, таму проксі з аўтэнтыфікацыяй застаецца па-за API платформы. Праграмы, якім ён патрэбны, звычайна ўбудоўваюць карыстальніцкі стек, tun2socks або падобны рухавік, і самі адказваюць на выклік 407.

Гэты выбар мае наступствы. Карыстальніцкі стек бачыць толькі той трафік, які вы накіравалі ў яго, таму ён мусіць жыць у тых жа межах дэскрыптара і наследуе тую ж патрабу protect() для ўласнага сокета ўзыходзячага канала. Прапусціце protect() — і тунэль зловіць уласны транспарт, што выглядае як рукапацісканне, якое застойваецца пасля першага пакета. Правярайце на проксі, які патрабуе ўліковыя даныя, бо адкрыты проксі праходзіць па шляху платформы і нічога не даказвае.

Спісы праграм і DNS — таксама дэкларацыі

У Builder ёсць і addAllowedApplication, і addDisallowedApplication, а яны ўзаемавыключальныя: выклік абодвух кідае выключэнне. У любым выпадку спіс прымяняецца для кожнага карыстальніка пры наладзе інтэрфейсу, таму праграма працоўнага профілю кіруецца тунэлем працоўнага профілю, а не вашым. Чаго не дае ніводнае API — гэта кантролю над рашэннем іншай праграмы: вы не можаце знешне загнаць пакет у свой тунэль і не можаце закрыць або пераканфігураваць ужо актыўны VPN.

DNS ідзе па той жа схеме. addDnsServer абвяшчае адрас рэзалвера; ён не дазваляе вашай праграме адказваць на запыты. Калі хочаце рэзалвіць імёны самі, запусціце рэзалвер і накіруйце яго адрас у тунэль. Праграмы, што выкарыстоўваюць DoT або DoH да фіксаванага імя хоста, могуць абысці абвешчаны вамі адрас, таму лічыце распазнаванне імёнаў лепшым намаганнем, пакуль вы не перахопліваеце і шлях запыту.

Маніфест, always-on і чатырохкрокавы тэст

Права на працу абвяшчаюцца ў маніфесце: BIND_VPN_SERVICE, intent-фільтр android.net.VpnService і foreground-сэрвіс, калі тунэль мусіць выжыць у фоне. Always-on VPN дадаткова патрабуе чыстага onRevoke, бо сістэма выклікае яго, калі карыстальнік змяняе налады або іншая праграма перахапляе кіраванне.

Выконвайце праверкі па парадку і спыняйцеся на першай памылцы. Першае: выведзіце спісы адрасоў, маршрутаў і DNS, перададзеныя builder. Другое: пацвердзіце, што establish() вярнуў дэскрыптар і што транспартны сокет абаронены. Трэцяе: запытайце адзін пункт прызначэння ўнутры ўключанага прэфікса і адзін — унутры выключанага. Чацвёртае: высветліце, які рэзалвер фактычна адказаў, запытаўшы імя хоста, якое можа рэзалвіць толькі ваш рэзалвер.

Завершанае рукапацісканне не даказвае ніводнага з наступных крокаў. Кожная праверка ізалюе адзін пласт, таму поспех на другім кроку нічога не кажа пра чацвёрты.

Даведка па VpnService.Builder пералічвае ўсю паверхню builder. Усё, што патрэбна звыш яе, — гэта іншае API, іншая праграма або ваш уласны карыстальніцкі стек, і гэта рашэнне праектавання, а не памылка раздзеленага тунэлявання.

Падобныя артыкулы