Новое правило package_name_regex в sing-box (борьба с детектом VPN)

Сегодня вышла версия sing-box 1.14.0-alpha.10. Автор принял мой PR по добавлению нового правила package_name_regex (работает только на Android). Это позволяет на Android искать пакеты регуляркой (раньше только перечислять отдельные имена), но что еще важнее - позволяет ловить правилами приложения, которые принудительно лезут в tun, хотя их туда никто лезть не просил.

Для чего это нужно? Мы включаем VPN для всех приложений (можно не включать полную блокировку прямых на уровне ОС). Уже в конфиге через route rules делаем, чтобы все российские приложения всегда шли в direct, чтобы они не могли использовать прокси. И вроде бы всё хорошо, но есть проблема. Если приложение принудительно выбирает интерфейс, то правила package_name (по имени пакета) не работают, потому что из-за бага или недоработки в Android VPN-приложение именно в этом случае (принудительный выбор интерфейса) не может определить UID владельца соединения. И вот это новое правило package_name_regex теперь может поймать такие случаи и, например, либо заблокировать доступ (block), либо пустить в direct.
Правило должно быть такое:

{"package_name_regex": [".+"], "invert": true, "outbound": "block"}

А просто новое правило можно использовать вот так (пускаем все российские приложения напрямую без прокси):

{"package_name_regex": ["^ru\\."], "outbound": "direct"}

Issue: Feature request: ability to match connections with unidentified package name in routing rules · Issue #4009 · SagerNet/sing-box · GitHub
PR: Add package_name_regex rule item by hdrover · Pull Request #4013 · SagerNet/sing-box · GitHub

Они не все ru, там дофига .com к сожалению.

Например всякие vk, vkvideo, яндекс браузеры с алисами

Согласен, но в любом случае станет проще. Например, весь яндекс одной строкой, а не все пакеты перечислять. Список сильно сократится.

Добавлю, что уже выложенные пакеты в Play Market свой package ID менять не могут.

TL;DR: им не обязательно самим на TUN идти. Они могут “попросить” другие приложения.

Добавьте в правила любые приложения (их package ID), которые шпион способен вызвать из себя (например дефолтный браузер с указанной ссылкой) – он их может открыть либо через CustomTabsIntent, либо по кнопке “Open in browser” через Intent.ACTION_VIEW.

А вообще вроде как через ЛЮБОЙ браузер, который поддерживает это (а большинство поддерживают) можно вызвать URI через setPackage, даже через тот через который у вас VPN.

String url = "https://rkn-spy.ru";
String packageName = "com.android.chrome"; // браузер который идет через VPN

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setPackage(packageName); 

startActivity(intent);

Ещё любые приложения с “Deep-links” (scheme://), способные выполнить GET запрос по URI.

Ну или совсем экзотический способ, приложение может под каким-либо предлогом создать ярлык на рабочем столе (через requestPinShortcut), ведущий на какой-либо безобидный сайт (причем диалоговое окно появится всего раз), а потом обновить ссылку на шпионский сайт с помощью updateShortcuts, при этом диалоговое окно снова не появится. И тоже вроде как указать конкретный браузер можно (через который у вас VPN), с помощью того же setPackage в Intent.

Иконку тоже можно обновить (через setIcon), например на Telegram, чтобы вы по неосторожности туда кликнули.

Поправьте, если что-то где-то не так.

Как пофиксить?

(Снизить поверхность атаки)

А не работает. Как минимум Android сборка - ругается на локаль.

stacktrace: java.lang.RuntimeException: Unable to create application io.nekohasekai.sfa.Application at android.app.ActivityThread.handleBindApplication(ActivityThread.java:8150) at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2691) at android.os.Handler.dispatchMessage(Handler.java:132) at android.os.Looper.dispatchMessage(Looper.java:333) at android.os.Looper.loopOnce(Looper.java:263) at android.os.Looper.loop(Looper.java:367) at android.app.ActivityThread.main(ActivityThread.java:9298) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:569) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929) Caused by: go.Universe$proxyerror: unsupported locale: ru_RU at io.nekohasekai.libbox.Libbox.setLocale(Native Method) at io.nekohasekai.sfa.Application.onCreate(r8-map-id-d1982c6ab011c2d43878c168eb4f18af290f6e093779354c8bc449ff97508bc0:28) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1400) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:8145) ... 10 more

Перепробовал en_US, en_GB и обе китайских - пофиг

Похоже, это баг новой альфы на Android. Приложение падает еще на старте из-за проверки locale в libbox, а не из-за конфига.

Судя по коду, сейчас нормально принимается только точная локаль zh_CN. ru_RU, en_US, en_GB валят запуск с unsupported locale. Даже ручная смена языка может не помочь, если Android передает locale в другом формате.

При несовпадении locale приложение должно было откатываться на английский, а вместо этого падает.

Спасибо за вклад.

Есть идея, как немного упростить задачу.

У nekobox можно прямо по клику выбирать приложения в правилах роутинга без ручного вписывания пакетов. Создаем правило для проксирования нужных приложений, а ниже правило с ip_dest 0.0.0.0/0 на директ для всего остального.

Приложения проксируются, а termux выдает провайдерский айпи для tun и для socks

curl ifconfig.co --interface tun0
curl ifconfig.co --proxy socks5://127.0.0.1:2080

Единственный минус - нужно дублировать приложения в общих настройках и в правилах роутинга. Я пробовал разрешить сразу все в общих настройках, но тогда правило для роутинга не срабатывает

Да, рабочий вариант. В целом проблема из первого поста не стоит, если по умолчанию всё блокируется или идет в direct. Проблема с определением таких хитрых приложений есть только тогда, когда по умолчанию мы хотим направлять на прокси за исключением российских приложенией и некоторых других (обычно это плохой вариант, но для кого-то единственный возможный).

Исправили в 1.14.0-alpha.11

Это же по сути будет выглядеть, как прохождение трафика через некий бранжмауэр, фаервол, антивирус с фильтром сайтов и т.п. (ПО которое создаёт tun интерфейс и пропускает через него трафик) . Всё получается как бы лигитимно, чинно, скрепно. А с помощью настройки других маршрутов, например блокировка geosite:category-ads-all -будет и по факту выполнять эту роль.

Да, единственное неудобство - это двойной список приложений. В целом не критично, можно один раз выставить и забыть

Заметил, что если всё кроме выбранного направлять в direct (обход) то socks прокси начинает работать нестабильно, происходят отвалы. А если всё остальное в блок - то всё нормально.

Я использую только для tun, за неделю отвалов не было. Мне кажется бесполезным юзать именно сокс в такой схеме, его задача тут просто пропустить через себя в директ и не спалить сервер. У меня для сокс прокси отдельно висит обычный sing-box в без tun.

А могут быть какие-то минусы именно в блокировке всего остального трафика (кроме того, что для разрешённых приложений) вместо отправки в direct? Как будто бы на первый взгляд не просматривается.

Я тоже не вижу проблем, если именно сокс не используется для каких-то отдельных правил, для браузера например

Спасибо за ответ. В NekoBox приложения работающие через прокси (Ff, Tg) кстати работают без внесения в оба белых списка (“Режим VPN для приложений” и в маршруте “прокси”). Видимо эти настройки влияют только на tun. (и это хорошо) А авторизация в прокси по ip+порт+логин+пароль.

Не должно быть так. У меня оба уходят в директ с этим правилом, должно быть в самом низу по очередности