Атрыбуцыя VPN у Android: getConnectionOwnerUid супраць /proc/net
Android 10 хавае /proc/net ад праграм. VPN па-ранейшаму атрымлівае пакеты, але атрыбуцыя пераходзіць да getConnectionOwnerUid з уласнымі правіламі картэжа і дазволаў.
На Android 10 і пазней праграма, якая адкрывае /proc/net/tcp, атрымлівае адмову ў доступе замест табліцы сокетаў. VPN-інтэрфейсу гэта не перашкаджае: пакеты прыходзяць, бо тунель — гэта рашэнне пра маршрут, а не дазвол на файл. Змяніўся адказ на больш вузкае пытанне: «якая праграма валодае гэтым злучэннем?» Замена — ConnectivityManager.getConnectionOwnerUid, падтрыманы запыт па кожным злучэнні, і выбар паміж старой звычкай і новым API залежыць ад таго, якая з трох задач у вас насамрэч ёсць: прымаць пакеты, атрыбутаваць паток або лічыць спажыванне.
Два спосабы адказаць на пытанне пра ўладальніка
Да API 29 прывілеяваны або root-працэс мог чытаць табліцы сокетаў ядра, супастаўляць чацвёрку і пазнаваць UID уладальніка. Гэта давала інвентар на ўсю прыладу ў адным файле. З API 29 прадстаўленне файлавай сістэмы абмежаванае, а падтрыманы шлях — запыт па кожным злучэнні з пратаколам, лакальным адрасам і аддаленым адрасам.
| Question | Legacy socket tables | getConnectionOwnerUid |
|---|---|---|
| Who owns this flow? | Scan and match the tuple | Ask once per tuple |
| Device-wide inventory? | Yes, when readable | No, one flow at a time |
| Historical usage? | No | No; that is NetworkStatsManager |
| Works without root? | No on modern releases | Yes, for authorized callers |
Параўнанне не «старое супраць новага». Гэта тры розныя задачы, якія раней задавальняў адзін чытэльны файл, а цяпер задавальняюць тры розныя інтэрфейсы з трыма рознымі кантрактамі.
Лакальны адрас — гэта канец праграмы
Сігнатура метаду прымае пратакол, лакальны адрас сокета і аддалены адрас сокета. «Лакальны» азначае бок злучэння з боку праграмы, а не той канец, які ў момант праверкі выпадкова аказаўся крыніцай пакета. У пакета, што ідзе ад аддаленага сервера назад да праграмы, крыніца і прызначэнне перастаўлены адносна гэтага азначэння.
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
int uid = cm.getConnectionOwnerUid(
OsConstants.IPPROTO_TCP, appLocalAddress, appRemoteAddress);Гэтая памылка кірунку — самая частая прычына таго, што працаздольны запыт вяртае няправільны адказ або ніякі. Запішыце абодва канцы перад выклікам і зверце пару са злучэннем, якое праграма сапраўды адкрыла. Сямейства адрасоў таксама важнае: адрас, адлюстраваны праз IPv4, і ўласны адрас IPv6 — гэта розныя картэжы, як і патокі TCP і UDP на адных партах.
Аўтарызацыя і невядомыя вынікі — частка кантракта
Запыт абмежаваны аўтарызаванымі выклікальнікамі, уключаючы актыўную VPN-праграму для адпаведнага карыстальніка. Стоянняя праграма маніторынгу не можа меркаваць, што пералічыць злучэнні іншага профілю, а неаўтарызаваны выклік можа выкінуць выключэнне бяспекі замест ветлівай памылкі. Апрацоўвайце абодва вынікі яўна.
Адсутны ўладальнік — гэта не імя пакета. Паток мог зачыніцца паміж пакетам і запытам, картэж можа быць няпоўным, або выклікальніку проста не дазволена ведаць. Кэшаванне першага паспяховага адказу і яго пазнейшае выкарыстанне прыпісвае новае злучэнне старой праграме, што горш за паведамленне пра невядомасць.
Калі атрыбуцыя не ўдаецца, трафік усё роўна патрабуе рашэння: заблакаваць яго там, дзе гэта патрабуе палітыка, дазволіць толькі зразумелы вам абсяг або паказаць прагал. Гэты запасны шлях належыць дызайну, а не каментару.
Улік — гэта іншае пытанне
NetworkStatsManager паведамляе пра спажыванне паводле ўласных правілаў доступу. Ён можа сказаць, колькі UID перадаў за перыяд; ён не можа сказаць уладальніка адвольнага жывога пакета. Магчымасці сеткі апісваюць уласцівасці сеткі, а не каталог сокетаў. Змешванне трох дае панэлі, якія выглядаюць узгоднена і не ёсць такімі.
Для простага выпадку — вызначыць, якія праграмы карыстаюцца тунелем, — падтрыманыя спісы дазволеных і забароненых праграм Android ужо выяўляюць палітыку без усякага выскрэбвання. Пакіньце атрыбуцыю па патоку для выпадкаў, якім яна сапраўды патрэбна, і закладзіце бюджэт на падтрымоўваную рэалізацыю, якая разам апрацоўвае кірунак, дазволы і невядомыя станы.
Выбірайце інструмент па задачы, потым правярайце мяжы
Калі патрабаванне — выбар тунеля, выкарыстоўвайце спіс платформы. Калі гэта жывая атрыбуцыя, выкарыстоўвайце запыт уладальніка і лічыце кожны бязответны картэж невядомым. Калі гэта гістарычныя сумы, выкарыстоўвайце stats API. Затым праверце тры мяжы, якія ламаюць наіўныя рэалізацыі: пакет у адваротным кірунку, паток, што зачыняецца да запыту, і выклікальнік, які не з'яўляецца актыўным VPN.
Нішто з гэтага не патрабуе адключэння SELinux, root-права на тэлефоне або глабальна чытэльных табліц ядра. Абмежаванне — гэта і ёсць сутнасць: платформа перанесла атрыбуцыю за кантракт, каб адказ быў даступны кампаненту, якому ён законна патрэбны, і нікому іншаму.