Выборочный обход блокировок на iOS
Давно пытался найти какое-то решение, чтобы трафик на айфоне для запрещенных ресурсов шел через VPN, а остальные приложения открывались как обычно. Но таких решений в iOS не предусмотрено в отличии от Андроида. В чате Tools & Productivity случайно наткнулся на статью о программе Shadowrocket.
При детальном изучении оказалось, что она не поддерживается протокол Wireguard, где у меня поднят свой сервер. После часа поисков в Гугле с помощью операторов URL rewrite wireguard site:apps.apple.com оказалось, что почти все программы в похожей тематике принадлежат разработчику Shadow Launch Technology Limited, который выпускает Shadowrocket и прогу Blatu с похожим функционалом, но только под WireGuard. Об установке рассказал в посте Wireguard с интерфейсом.
Настройка
Качаем приложение, заходим в Configuration и находим строку Rule File. Далее выбираем Export и сохраняем себе куда-нибудь файлик с настройками по-умолчанию. Теперь настроим подключение таким образом, где мы будем ходить на роскомнадзор сайты только под vpn, а остальной трафик будет идти напрямую.
Конфигурация
В файле по-умолчанию все настройки сделаны под Китай. Поэтому важно отредачить конфигурация под российскую действительность:
Пример настроек автора CONF, 1КБ
Правила
Файл состоит из основных настроек (глобальные), отдельные правила для доменов или подсетей и даже в рамках целой страны. Хост, а также правила для переопределения адресов. Рассмотрим ключевые правила:
DOMAIN-KEYWORD | указываем название домена и будет работать с любыми поддоменами; |
DIRECT | трафик без VPN |
PROXY | трафик через VPN |
REJECT | трафик блокируется |
Обратите внимание, что раздел Rule заканчивается FINAL,PROXY — это значит, что если сайта нет в списке или айпи адрес определился, как не российский (даже если домен ru), то трафик пойдет через VPN.
Укрощаем одноглазого змея. Разбираемся с WireGuard и делаем свой умный VPN
Забавная ситуация: сайтов и сервисов, доступных только через VPN, все больше, но при этом многие российские компании закрывают доступ из-за границы. В результате приходится целыми днями теребить ползунки «вкл-выкл», что утомительно. Я расскажу, как с помощью магии маршрутов и WireGuard, решить эту проблему и сделать «умный» VPN, который не надо отключать.
Если ты пользуешься VPN, то и сам наверняка сталкиваешься с блокировками зарубежного трафика. К примеру, могут не открываться pochta.ru, leroymerlin.ru, rt.ru, avito.ru.
Каждый с этим борется как может. Например, на устройствах Apple родными средствами можно настроить автоматизацию, которая будет запускать VPN, когда открываешь определенные приложения (например, Twitter), а когда выходишь из них — выключать обратно. Но это костыль, а хочется все сделать красиво, да еще и прокачать навык работы с сетью.
Поэтому мы сейчас попробуем «включать VPN чуть-чуть».
Заодно чуть улучшим качество связи с локальными ресурсами: необходимость таскать трафик сначала до VPN вне страны, а потом обратно до сервера внутри нее драматично сказывается если не на скорости, то на задержке точно. Даже на проводном интернете пинг в 4 мс до Яндекса легко превращается в 190 мс, а на мобильном интернете — из 80 мс в 240 мс. Дополнительный хоп внутри страны чуть ухудшит дело, но далеко не так драматично.
Делать все мы будем на основе WireGuard — это относительно новая (разрабатывается с 2016 года, в отличие от OpenVPN и IPsec: первый — это двухтысячные, а второй еще раньше) технология VPN. Создал ее, по сути, один человек — zx2c4, которого в миру зовут Джейсоном Доненфельдом. Плюсы WG — скорость (особенно для Linux, где он может работать как модуль ядра, начиная с Kernel 5.6, и Windows, где модуль для ядра выпустили около недели назад), низкие задержки, современная криптография и простая настройка и использование конечным юзером.
Ах да, еще UDP. UDP для туннелей — это хорошо, потому что у TCP уже есть механизмы, которые позволяют ему работать на неидеальных соединениях, а UDP представляет собой именно такое соединение. А когда ты засовываешь TCP в TCP, то отказываешься от большей части этих механизмов (инкапсулированный TCP-пакет будет гарантированно доставлен другой стороне, хотя протокол допускает недоставку), но все еще несешь весь оверхед вида «хендшейк соединения для отправки хендшейка».
Не говоря уж о том, что инкапсулировать UDP в TCP — ничуть не лучшая идея, потому что сразу рушит все предположения всяких скайпов о том, что лучше пропустить пару пакетов, чем уменьшить задержку: каждый UDP-пакет в этом случае будет принудительно послан заново и доставлен корректно, не считаясь с затратами времени.
Особенно для одинокого пользователя-хакера приятна работа с шифрованием: нет необходимости ни в сертификатах и удостоверяющих центрах, ни в логинах-паролях, все, что нужно, — обменяться публичными ключами с пиром, с которым ты хочешь установить соединение. Для больших компаний это, конечно, будет скорее минусом, как и то, что WG — это только базовая часть полноценной большой инфраструктуры VPN. Но именно WireGuard использовали, к примеру, в Cloudflare для своего WARP, правда, написав его собственную реализацию — boringtun.
Еще один минус WG — то, что трафик не обфусцирован и глубокая инспекция пакетов (DPI) выявит и позволит заблокировать такое соединение (не говоря уж о блокировке UDP совсем, что почти не мешает работать с вебом, но гарантированно ломает WireGuard). Для скрытия трафика рекомендуется использовать специализированное ПО — Cloak, Obfsproxy, Shadowsocks, Stunnel, SoftEther, SSTP или, в конце концов, простой SSH. Часть из этих инструментов может работать совместно с WG, а часть способна его заменять в качестве инструмента стеганографии: WG изначально создавался под скорость и криптографическую защищенность.
Если очень упрощать, ключи работают следующим образом: у нас есть закрытый (приватный) ключ, из которого можно сгенерировать открытый, или публичный. Наоборот, из открытого ключа получить закрытый мы никак не можем. Затем мы шифруем с помощью закрытого ключа какую-то строку, а при помощи открытого расшифруем ее и тем самым убедимся, что у собеседника точно есть закрытый ключ, а значит, он тот, за кого себя выдает. Таким образом, мы можем без проблем передавать открытый ключ — он всего лишь позволяет проверить подлинность автора, но не притвориться им.
Это как в SSH — публичный ключ лежит на сервере, где его потеря — небольшая беда: все, что сможет сделать с ним злоумышленник, — это положить его на свой сервер, чтобы ты мог подключиться к нему с помощью закрытого ключа.
Так вот, в WG на первом этапе подключения каждая сторона с помощью зашифрованного приватным ключом сообщения доказывает собеседнику, что она именно она: это проверяется публичным ключом.
Второй этап — создание с помощью этих ключей и матана симметричных ключей для шифрования самого трафика. Благодаря тому что расшифровать зашифрованное публичным ключом нельзя без приватного, мы сможем создать ключ для симметричного шифрования и отправить его по защищенному каналу. Этот шаг необходим потому, что симметричное шифрование гораздо менее ресурсоемкая операция, а минус у нее только один: необходимо синхронизировать ключ между сторонами, при том что перехват ключа третьей стороной ведет к возможности расшифровки трафика.
Но эта проблема решается с помощью асимметричной схемы. Это называется протокол Диффи-Хеллмана — способ защищенного получения общего секретного ключа. В WG используется ECDH — вариация Диффи — Хеллмана на эллиптических кривых. Первые два этапа в терминах WG называются рукопожатием или хендшейком.
Затем симметричные ключи используются для шифрования трафика. Раз в две минуты происходит новое рукопожатие и сессионные симметричные ключи меняются.
Разумеется, в реальности все немного сложнее: например, отправляются не сами ключи, а сгенерированные на их основе эфемерные ключи, которые удаляются сразу после операции. Заинтересовавшихся подробностями отправляю к краткому описанию на сайте WireGuard.
Мы же перейдем к более практическим действиям.
Шаг 1. Создаем и настраиваем два сервера
Один сервер будет внутри страны — через него трафик пойдет на локальные ресурсы, а второй — за границей. Дальше я их буду называть local и external.
Идеально, если local будет в твоей домашней сети, потому что при этом трафик на внешние ресурсы не отличается от твоего домашнего трафика. Но для этого нужен какой-то хост дома, белый IP и возможность пробросить порт. У меня это виртуалка на домашнем сервере, но, наверное, подойдет и Raspberry Pi или аналогичный одноплатник.
Если дома хоста нет, можно взять любой сервер у хостера VDS. Увы, некоторые сайты блокируют подсети хостеров, опасаясь ботов, так что это менее предпочтительный вариант.
А вот внешнюю машину можно арендовать и у хостера. К примеру, у RUVDS и VDSina есть зарубежные площадки. А можно выбрать иностранного хостера, если найдешь способ оплачивать его услуги. Например, я использую исландский 1984.hosting, созданный специально для параноиков.
Считаем, что на обоих серверах у нас Debian 11.
Ставим нужные нам пакеты:
Включаем перенаправление трафика: в этом случае сервер, получив пакет, который не предназначается ни одному из его IP-адресов, не отбросит его, а попытается перенаправить в соответствии со своими маршрутами.
Опционально (но очень удобно) сразу поменять hostname обоих серверов, чтобы не запутаться, где какая консоль:
Шаг 2. Настраиваем WireGuard для связи двух серверов
Для начала генерируем ключи. Запускаем два раза wg genkey и получаем два приватных ключа:
Утилита wg genkey не делает ничего волшебного, это просто аналог чего-то в таком духе (генерируем 32 байта случайных значений и представляем их в виде Base64):
Только более случайное. Не стоит использовать $RANDOM в серьезных применениях.
Создаем два конфига. Один на internal:
/etc/wireguard/wg-internal.conf
Второй на external:
/etc/wireguard/wg-external.conf
Секция [Interface] — это настройки конкретного сетевого интерфейса WireGuard, того, что будет виден в ip a . Название интерфейса берется из названия текущего файла конфигурации. У одного интерфейса всегда одна ключевая пара: у пиров этого интерфейса одинаковый публичный ключ.
Но никто не мешает, если хочется, сделать для каждого пира отдельный конфиг и отдельный интерфейс (правда, на сотнях клиентов это будет неудобно).
Управляются интерфейсы обычно при помощи утилиты wg-quick:
wg-quick down wg-external и wg-quick up wg-external
Утилита wg-quick — это на самом деле 400 строк на баше, которые автоматизируют часто используемые вещи, например установку маршрутов. Наличие туннеля само по себе не дает ничего, кроме защищенной «трубы», за которой находится другой пир. Чтобы твой запрос в браузере попал в интерфейс, системе надо явно сказать: «Маршрутизируй, пожалуйста, пакеты с таким-то адресом назначения вот в этот сетевой интерфейс».
Именно этим занимается wg-quick . Ну еще и настройкой адресов DNS, указанных в конфиге, установкой MTU и еще парой вещей. Но ничего сложного в этом нет, достаточно сделать cat /usr/bin/wg-quick , чтобы посмотреть на эту логику, и, если надо, повторить то же самое руками.
- Interface-Address — это IP текущего пира. Вся адресация в WG статическая. С одной стороны, это упрощает настройку и бутстрап, с другой стороны, усложняет работу, если у тебя очень много клиентов.
- ListenPort — это UDP-порт для подключения извне. Если не указать, будет прослушивать 51820.
- Interface-PostUp и Interface-PostDown — скрипты, которые выполняются после поднятия и после остановки интерфейса. Есть еще PreUP и PreDown.
Кроме публичных и приватных ключей, есть еще опция PresharedKey, которая обеспечивает дополнительное шифрование симметричным шифром. Ключ можно сгенерировать, например, командой wg genpsk и добавить в опцию PresharedKey в секциях Peer на обоих пирах. Если не использовать эту опцию, нагрузка по шифрованию и расшифровке не вырастет: когда ключ не указан, используется нулевое значение ключа.
А чтобы по-настоящему обеспечить постквантовую безопасность (невозможность расшифровки данных квантовыми компьютерами), разработчики рекомендуют дополнительный внешний квантово-устойчивый механизм хендшейка, например SIDH, который Microsoft пиарит именно в таком контексте. Созданный им общий ключ можно использовать в качестве PresharedKey.
Заклинания в PostUp достаточно просты. Вот команда для подстановки имени сетевого интерфейса, куда по умолчанию выполняется маршрутизация:
Как правило, это интерфейс, обращенный к провайдеру или роутеру.
Таким образом, страшная команда превращается в такую:
Здесь происходит включение NAT в режиме маскарада: сервер будет отправлять пришедшие ему пакеты во внешнюю сеть, подменяя адрес отправителя своим, чтобы ответы на эти пакеты тоже приходили ему, а не исходному отправителю.
Вторая команда уже немного сложнее, но она подставляет IP-адрес дефолтного маршрута.
Сначала мы получаем, как и выше, сетевой интерфейс маршрута по умолчанию:
Потом данные о состоянии этого интерфейса:
И дальше вытаскиваем оттуда адрес, в данном случае 192.168.88.70.
Команда становится такой:
Это необходимо для сервера internal, потому что иначе при активации маршрута 0.0.0.0/0 он начинает пересылать ответы на пакеты, приходящие ему на внешние адреса через туннель WG. Сервер на том конце, конечно, пересылает их по назначению, но тут уже не готов отправитель пакета: он присылает что-то на внешний адрес сервера internal, а ответ ему приходит с external.
Естественно, при включенном rp_filter пакет отбрасывается. В этом случае сервер перестает быть доступным, например по SSH снаружи. К нему придется коннектиться только по внутреннему IP WireGuard. Отключать rp_filter — это стрелять из пушки по воробьям, а вот дополнительное правило исправляет ситуацию.
Теперь в оба конфига надо добавить секцию Peer, чтобы связать серверы друг с другом.
Генерируем из приватного ключа публичный (вот в wg pubkey как раз и происходит криптомагия):
Там же, в Endpoint указываем адрес сервера internal и порт, который мы задали в ListenPort.
С AllowedIPs при использовании wg-quick возникает небольшая путаница. Это именно список IP-адресов, с которых мы разрешаем принимать пакеты из туннеля. Если прилетит что-то с другим src, оно будет отброшено.
Утилита wg-quick разумно считает, что, если есть какие-то устройства, которые могут послать пакет, пакеты к этим устройствам надо маршрутизировать туда же, и создает маршруты на эти адреса, указывающие на туннель пира.
В этих примерах AllowedIPs можно читать как «адреса, трафик на которые будет маршрутизироваться в туннель этого пира и с которых пир сможет отправить что-то в туннель». То есть пункт AllowedIPs = 10.20.30.3/32 означает буквально «только запросы на 10.20.30.3 (адрес пира WG) отправлять в туннель» — дать доступ только до машины этого клиента.
Пункт AllowedIPs = 192.168.88.0/24 означает, что при запросе адреса из этой подсети запрос уйдет в туннель клиента, и если у него включен форвардинг и ему доступна эта подсеть, то к ней можно будет получить доступ.
AllowedIPs = 0.0.0.0/0 означает, что в туннель надо маршрутизировать вообще весь трафик. Правда, это не относится к трафику, например, локальной сети: приоритет у маршрута, который создается из маски подсети и адреса шлюза, выше, чем у 0.0.0.0/0. Также маршрут 0.0.0.0/0 перебьют маршруты других пиров, если они будут в конфиге.
В данном случае AllowedIPs=10.20.30.0/24 означает, что трафик с external в подсеть 10.20.30.0–10.20.30.255 будет уходить в туннель к internal. В принципе, особой нужды в этом нет, external у нас исключительно выходная нода. Но вдруг мы как-нибудь захотим зайти оттуда по SSH на какую-нибудь другую машину.
Повторяем генерацию публичного ключа с external:
AllowedIPs тут 10.20.30.2/32, 0.0.0.0/0 . Этим мы указываем, что за туннелем находится конкретный IP 10.20.30.2, и, помимо этого, пробрасываем весь трафик, не связанный другими маршрутами, в этот туннель: external у нас основная выходная нода VPN.
Поэтому по умолчанию весь трафик будет направляться через нее, так как зарубежных маршрутов больше, чем российских, и логичнее фильтровать именно российские, а зарубежный трафик пустить по умолчанию через ноду в другой стране.
Итак, два конфига.
/etc/wireguard/wg-internal.conf
/etc/wireguard/wg-external.conf
Теперь можно поднять туннели на обоих серверах:
Проверяем, что туннели активны, командой wg :
Если видим «latest handshake:… seconds ago» и байты и в received и в sent, значит, все хорошо. Если байты только в send, без хендшейка и полученных данных, значит, где-то в конфиге ошибка или серверы недоступны друг для друга.
Если что-то пошло не так и отвалился SSH, то достаточно перезагрузить сервер — активные туннели сбросятся.
Если же все хорошо и доступ к серверам сохранился, ставим туннели в автозапуск:
Попробуем посмотреть маршрут (рекомендую замечательную утилиту mytraceroute , mtr ) без туннеля:
Все хорошо, трафик идет через внешний сервер — сначала на 10.20.30.2, который у нас назначен выходной нодой, а потом через его маршрутизаторы.
У нас получилась примерно следующая схема.
Шаг 3. Добавляем конфиг клиента
Создаем конфиг клиента, конечного устройства — пользователя VPN. За основу берем wg-external.conf , потому что это точно такой же клиент, который подключается к internal: разница только в том, что external будет получать пакеты, а наш клиент — отправлять.
Генерируем ему сразу пару публичный и приватный ключ:
Конфиг почти такой же (этот файл уже должен быть на твоем устройстве, не на сервере):
/etc/wireguard/wg-notebook-client.conf
Тут у нас добавилась опция PersistentKeepalive . Дело в том, что роутеры в цепочке между двумя пирами ничего не знают о сессии WG, а знают только о потоке UDP-пакетов. Для маршрутизации UDP-пакетов за NAT они создают у себя табличку, в которую записывают, кто, куда и на какой порт отправил пакет. И если с destination-адреса/порта приходит UPD-пакет, то они определяют, куда его отправить, по этой таблице, делая вывод, что если сервер B недавно отправил пакет серверу А, то ответ от сервера А на этот же адрес и порт, скорее всего, надо переслать серверу B.
В UDP, в отличие от TCP, нет никаких договоренностей о поддержании сессии, так как нет и самого понятия сессии. WG же построен таким образом, что при отсутствии трафика, попадающего в туннель, не будет и трафика между пирами, только хендшейки раз в две минуты. Опция PersistentKeepalive заставляет его посылать пустые пакеты каждые 25 с, что предотвращает потерю маршрута на промежуточных роутерах. Без этого мы бы могли раз за разом отправлять пакеты, до второго пира они не доходили бы, а он бы об этом не знал.
Дальше мы добавляем еще одну секцию Peer в конфиг на internal — для клиента:
Перезапускаем туннель на internal ( wg-quick down/up ), подключаемся… Есть хендшейк! Данные пошли.
Теперь смотрим свой IP (например, на reg.ru): видим IP external ноды и другую страну.
Таким же образом создавай конфиги для других клиентов. Если это мобильные устройства, то удобнее показать им QR-код. Он делается так: создай в текущей папке конфиг как обычно (конечно, с новыми ключами и другим IP), назыви, например, wg-moblie-client.conf . А дальше прямо командой в консоли создай QR-код, который сканируем с телефона:
Это удобнее, чем копирование файлов, но тебе никто не мешает скинуть wg-moblie-client.conf на телефон или вообще ввести значения семи полей вручную.
Теперь наша схема выглядит следующим образом.
В целом все готово: мы только что сделали очень странный двуххоповый VPN. Надо это дело отметить! Открываем Сбермаркет, чтобы заказать пивка.
Ах да, мы же с этой проблемой и собирались бороться. Неловко!
Давай доделаем.
Шаг 4. Добавляем регионально зависимую маршрутизацию
Как ты помнишь, мы отправляем все данные с клиента на internal, он все данные шлет на external, а тот уже своему провайдеру. Также мы помним, что у нас на internal «слабый» маршрут 0.0.0.0/0, который перебивается любыми другими маршрутами, а сам internal находится в российском сегменте. Значит, все, что нам надо, — это как-то перехватить запросы на российские IP на уровне internal и перенаправить их не в туннель WG до external, а напрямую в сетевой порт самого сервера, в тот, через который он получает доступ в православный, российский интернет со скрепами и девицами в кокошниках.
Давай проверим предположение. На клиенте получим IP того же Сбермаркета ( nslookup sbermarket.ru ) и посмотрим, как туда идет трафик ( traceroute 212.193.158.175 ):
Ага, как и ожидалось, через external.
Теперь создадим маршрут до этого адреса через дефолтный шлюз и устройство. Их можно узнать в ip r :
195.2.79.1 и ens3 — это и есть нужные нам данные. Используем уже знакомые подстановочные команды и создадим новый маршрут:
Да, на последнем месте у нас нужный маршрут.
Теперь повторяем команду traceroute -r 212.193.158.175 на клиенте и видим, что трейс другой:
Ура, Сбермаркет открывается! Правда, такое сработает не со всеми сервисами: например, JavaScript на сайте может дернуть другой сервер, а не тот, в адрес которого резолвится имя домена и до которого нет маршрута.
Можно сходить на asnlookup.com, вбить туда адрес и получить принадлежность адреса к AS и заодно список подсетей этой autonomous system (у Сбермаркета это AS34879, OOO Sovremennye setevye tekhnologii). С большой вероятностью для более-менее крупных компаний это и будет их сетевая инфраструктура (ну или, по крайней мере, инфраструктура, относящаяся к конкретному сайту), прописав для которой маршруты ты обеспечишь доступ на нужный сайт или сервис. Для мелких сайтов ты, скорее всего, получишь AS хостера или дата-центра, но, во-первых, это тоже сработает, а во-вторых, мелкие сайты обычно и не закрывают иностранные диапазоны, потому что не испытывают проблем с DDoS из-за границы.
Но можно сделать проще и автоматически: засунуть в маршруты вообще все адреса российского сегмента (спасибо статье на Хабре) и не париться о ручном добавлении.
RIPE отдает их все в виде JSON вот по этому адресу:
Утилита jq преобразует из JSON в список подсетей:
Правда, почему-то некоторые адреса там в формате 195.85.234.0-195.85.236.255 , а не в виде подсети. Их там с десяток, можно было бы так и оставить, но, если уж начали делать, давай сделаем до конца и красиво. Нам понадобится утилита ipcalc.
Выделить эти адреса из базового списка легко: grep ‘-‘ или grep -v ‘/’ .
Скрипт для загрузки роутов выглядит как-то так (я не удержался и добавил туда еще и прогресс-бар):
Добавим строчки в крон ( EDITOR=nano crontab -e ), чтобы он запускался после перезагрузки и каждую неделю — обновить список адресов, если они поменялись:
Если тебе нужно принудительно маршрутизировать какую-то сеть через internal, то можно рядом со скриптом создать файлик subnets_user_list.txt , в который поместить список подсетей, тогда они каждый раз будут добавляться к общему списку при обновлении (в скрипте выше эта возможность уже реализована).
Мой, например, выглядит так:
Первая подсеть — для мобильного приложения «Авито»: ее почему-то не было в списке RIPE. Дальше подсети для «Телеграма», чтобы хоть немного ускорить загрузку фото и видео.
Как видишь, разные сервисы показывают нам разные адреса, потому что один сервис хостится где-то внутри России, а другой — снаружи. Работает!
Кстати, если у тебя internal находится в домашней сети, бонусом ты получишь доступ к домашней сети из любого места, где находится устройство со включенным VPN: маршрут 0.0.0.0/0 на устройстве отправляет в VPN весь трафик, а internal, замечая трафик в ту подсеть, в которой он находится, отправляет ее в локальный порт, а не в туннель до external. Очень удобно: у меня в домашней сети работает сервер с докер-контейнерами web2rss, navidrome, freshrss, rss-bridge, homeassistant, и мне для получения доступа к ним совершенно не надо заморачиваться с пробросом портов, авторизацией каждого сервера и HTTPS, не говоря уж о том, что некоторые сервисы, типа IoT-устройств, не имеют ни авторизации, ни шифрования в принципе.
Шаг 5. Настраиваем файрвол
Полезной привычкой и хорошим тоном будет закрыть все ненужное на серверах.
Для начала на обоих серверах редактируем файл /etc/default/ufw , изменяя значение DEFAULT_FORWARD_POLICY на ACCEPT .
Теперь выполняем следующие команды на internal:
Что происходит, думаю, понятно — запретить все, разрешить исходящие, входящие SSH и подключения к WG, а что приходит из туннеля — разрешить.
На external то же самое, но открывать порт для WG не надо — он подключается сам.
Шаг 6, бонусный и необязательный. Кеширующий защищенный DNS over HTTPS
Теперь нам нужна еще одна вещь — DNS.
Можно, конечно, жить с DNS 1.1.1.1 , но надо учитывать, что трафик на него пойдет через external, а это автоматически означает задержку порядка 100 мс при каждом запросе. Можно, конечно, добавить 1.1.1.1/32 в subnets_user_list.txt , и тогда трафик пойдет через локальную ноду и локальный сервер 1.1.1.1 . Это уменьшит задержку до 10–20 мс, но твои DNS-запросы будет доступны провайдеру, что в случае локальной ноды для кого-то может быть неприемлемо.
Несколькими командами можно легко сделать кеширующий DNS, который еще и будет работать с DNS over HTTPS, а значит, провайдер узнает только, что использовался DoH, но не сами запросы. Это, конечно, не обязательно: у меня internal находится в домашней сети, и я просто использую DNS «Микротика», который находится в той же сети и поддерживает DoH. Но если у тебя internal-сервер — это VPS, то можно сделать там и DNS-сервер. В первой версии статьи использовалась ручная настройка cloudflared и dnsmasq, но потом я решил, что использовать Adguard Home лучше, удобнее и проще. Кроме того, блокировка рекламы не будет лишней.
Для начала, тебе надо поставить на internal докер (хотя я надеюсь, что он у тебя уже стоит, нельзя же быть таким отсталым, с домашним сервером и без докера):
После установки скачивай запускай контейнер с Adguard Home
Заходи в веб-панель, которая доступна на порту 3000 сервера internal, указывай Admin Web Interface Port — 3000, придумывай логин и пароль, нажимай кнопочки, которые покажутся тебе подходящими, пока не закончишь инсталляцию. Готово.
Adguard Home уже использует DoH, но можешь добавить свой любимый сервер в веб-панели, в разделе Settings — DNS settings.
Если тебе нужен просто защищенный DNS-сервер, то можешь нажать кнопочку «Disable protection» и закончить на этом. Если хочешь побольше блокировок рекламы, до добавь в Filters-DNS blocklists свои любимые списки блокировок. Я, например, выбрал эти:
https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt
https://someonewhocares.org/hosts/zero/hosts
Запрос должен вернуть что-нибудь с «ANSWER SECTION:» и IP-адресом в ней, а не упасть с «no servers could be reached».
Проверим заблокированный домен:
Оп, возвращает другой адрес:
Красота.
Проверяем доступ к DNS с другого WG-клиента:
Если ответ на запрос есть, значит, DNS работает.
Теперь можно добавить в конфиги клиентов в секцию Interface:
Шаг 7, еще более бонусный и необязательный. Доступ до чужих подсетей и защита своей подсети
Как я уже говорил, в такой инсталляции мы получаем бонусом доступ в свою локальную сеть, где бы мы не находились, из любого места. Иметь защищенный доступ до своих серверов очень удобно. Но что, если тебе нужен доступ не только в эту подсеть?
Например, у твоей девушки есть сервер inferno, который не торчит в интернет, и имеет только IP в локалке, скажем, 10.30.3.203 . Если ты хочешь получить к нему доступ, то просто ставишь на этот сервер wireguard с обычным конфигом клиента, например:
А в конфиге на internal в AllowedIPs пиши не только IP клиента внутри WG, но и 10.30.3.203/32
Ситуация несколько меняется, когда ты хочешь сделать доступ не только до конкретного клиента, но до всей подсети этого клиента: скажем, у этой девушки, кроме сервера, есть свой умный дом, и доступ нужен не только до сервера, на котором стоит клиент, но и до кучи других устройств, на часть из которых клиента поставить вовсе невозможно, потому что они ESP.
Казалось бы, достаточно изменить конфиг на internal таким образом, чтобы в AllowedIPs попал не конкретный сервер ( 10.30.3.203/32 ), а вся подсеть ( 10.30.3.0/24 ), и включить на inferno ip_forward , но нет.
Рассмотрим путь пакета от тебя до inferno ( 10.30.3.203 ) и обратно: наш маршрут 0.0.0.0/0 на клиенте заставит весь трафик, для которого нет локальных маршрутов, попасть в туннель WG, сервер internal получит пакет, увидит у себя активный маршрут 10.30.3.0/24 , отправит этот пакет снова в туннель WG, пакет примет inferno ( 10.30.3.203 ), ответит на него, взяв адрес получателя из поля «from» (где оно 10.20.30.3 , например), это пакет попадет в туннель WG, и будет доставлен обратно тебе.
А если ты захочешь отправить пакет уже до соседнего сервера 10.30.3.204 , то пакет точно так же попадет на inferno, где пакет будет смаршрутизован в локальную сеть (ведь получатель не inferno), пройдет через роутер и доберется до получателя. Но вот обратный пакет получатель отправит хоть и тебе ( 10.20.30.3 ), но в свой главный маршрут (например, в свой роутер), а надо было в сторону 10.30.3.203 . Т.е. нам нужен маршрут, который говорит «для пакетов, следующих в подсеть 10.30.3.0/24 , шлюз — 10.30.3.203 «.
Это можно сделать:
1)Вручную прописав маршрут на самом устройстве 10.30.3.204 (и на каждом таком устройстве в подсети 10.30.3.0/24 )
2)Раздать такой маршрут всем устройствам вместе с настройками сети через DHCP Option 121 (не знаю, зачем тебе эта информация)
3)Создать маршрут на маршрутизаторе, который анонсируется по DHCP как основной шлюз.
Рабочий вариант, пожалуй, только третий. Второй — это костыль для тех ситуаций, когда у тебя есть контроль над DHCP-сервером, но нет над маршрутизатором.
Но можно сделать гораздо более просто: воспользоваться маскадингом — точно так же, как делает ваш роутер, когда делает для провайдера вид, что все-все пакеты, которые генерируются устройствами за ним, отправляются именно им, и никем больше (ну, конечно, если провайдер не выдает тебе подсеть или ты уже не живете в 23 веке с IPv6). Тем более, ты уже это делал — на internal и на external.
Просто добавь в конфиг на inferno в секцию Interface такие строки:
Работать это будет, правда, только на Linux (и, наверное, макоси), на мобильных клиентах (c некотором вероятностью заработает на рутованном андроиде) и в Windows — нет, т.к. это просто команда операционной системы. На iOS нет шансов вообще, а на винде тебе придется разобраться самому, как включить маскарадинг (ну или забить и поставить Tailscale).
Однако, тут возникает некоторая проблема безопасности. Догадываешься какая?
Правильно: легкой правкой конфига на inferno можно добавить в AllowedIPs всю твою подсеть 192.168.88.0/32 , и получить доступ до всех твоих устройств в ней (а там вполне может быть что-то без авторизации, это же локальная сеть). Более того, если ты решишь поделиться этим VPN с другом, то при наличии маршрута 0.0.0.0/0 , он по умолчанию будет иметь доступ к твоей локальной подсети.
Можешь извернуться и перечислить в конфиге для друга AllowedIPs без локальных подсетей, например так:
Но это все равно не обеспечит тебе безопасности, потому что конфиг на чужом устройстве тобой не контролируется. И если завтра друг станет не-другом, ему ничего не помешает вписать в AllowedIPs абсолютно что угодно и потренировать на твоей сети свои навыки пентестера.
Поэтому, было бы неплохо разделять доверенные устройства — те, с которых ключ утечь не может никак, потому что оно лежит у тебя в кармане и заблокированное и недоверенные — все остальные. И второй группе, если уж им нужен доступ до каких-то сервисов, давать его только через разрешительные правила для конкретных IP и портов.
Можно, конечно, писать вручную правила для каждого устройства, но есть решение проще: разделить IP-адреса WG на две половины и сказать, что первая половина ( 10.20.30.1/25 , т.е. 10.20.30.1-10.20.30.126) это доверенные устройства с доступом к локалке, а вторая ( 10.20.30.128/25 , т.е. 10.20.30.129-10.20.30.255) — это недоверенные. И просто при создании конфига и выдаче устройству IP-адреса выбирать, какой это IP — до 126 или после. С таким подходом правил фаервола понадобится… ну, не одно, но всего три:
Первое правило запрещает доступ с недоверенных устройств до локальной подсети, второе — до подсети внутренних адресов WG (иначе можно с любого клиента достать до других устройств с WG-клиентами), а третье — до internal, потому что пакеты напрямую до нее попадают в цепочку INPUT, а не FORWARD, т.к. предназначаются самой машине.
Эти правила, как и выше, надо не просто вводить в консоль, а описать в конфиге internal через директивы PostUp и PostDown (в PostDown с «-D» вместо «-A» для удаления правила):
Если у тебя есть еще какие-то подсети, которые доступны через WG (как 10.30.3.0/24 твоей девушки в примере выше), то их тоже стоит вписать в еще одно такое же правило. Ну или просто запретить для недоверенных устройств все 10.0.0.0/8 и 192.168.0.0/16 .
Обрати внимание, что устройство одновременно может предоставлять доступ в свою сеть и быть недоверенным — комбинация IP из недоверенного диапазона с указанием подсети в AllowedIPs и маскарадингом в конфиге. Такое устройство позволит пакетам доходить до его подсети, но не получит доступ к подсетям других устройств.
Однако, помнишь DNS-сервер, который мы развернули? Если запретить доступ к 10.20.30.0/24 вообще, то недоверенные клиенты (например, твой друг) не сможет получить доступ до него и будет вынужден пользоваться каким-нибудь 8.8.8.8 . Благо, решить эту проблему можно еще парой правил фаервола:
Что они делают, думаю, тебе понятно: разрешает из недоверенной сети доступ к DNS серверу, но только к портам DNS. Эти два правила обязательно должны находиться выше того, что запрещает локальную сеть:
Так, а если я хочу не исключать российские IP из туннеля, а наоборот, отправлять только определенные IP в туннель?
Это сделать тоже довольно просто. Но есть пара тонкостей.
Первое желание — убрать 0.0.0.0/0 в wg-internal.conf у ноды external, чтобы весь трафик пошел через internal, а дальше завернуть часть трафика через external, правилом типа такого: ip route add 8.8.8.8 via 10.20.30.2 dev wg-internal — для IP 8.8.8.8 использовать в качестве шлюза 10.20.30.2 (external) и интерфейс wg-internal (на ноде internal интерфейс называется wg-internal ).
Но это первое желание неправильное: WG слишком умный, у него есть собственная маршрутизация, которую он берет из AllowedIPs, и так как у ноды external нет ничего, кроме ее адреса, то и трафик он пустит к ней только тот, что направлен на ее адрес. Чтобы разрешить любой трафик, надо все-таки указывать 0.0.0.0/0 . Но это одновременно создаст default маршрут, а мы хотим направить туда только определенный трафик. Самый простой способ — это оставить 0.0.0.0/0 , а ненужный нам маршрут в таблице системы просто удалить директивой PostUp уже после создания туннеля:
Обрати внимание на «table 51820» — это указание на отдельную таблицу маршрутизации WireGuard.
Теперь можешь добавлять маршруты, причем указывать шлюз не обязательно — WG со внутренней таблицей сам разберется, что куда отправлять.
Осталось немного модицифировать скрипт создания маршрутов, чтобы он просто брал подсети из subnets_user_list.txt и хосты из hosts_user_list.txt, и готово. Скрипт можно найти там же, в include_mode_cfg_gen/update_include_routes.sh .
Выводы
Хоть пары ключей в статье — действующие, но для боевого применения ключи надо обязательно сгенерировать заново.
Благо это просто, даже если тебе лень разбираться с ручной генерацией ключей, — я создал небольшой bash-скрипт для этого. Достаточно сделать так на internal-сервере:
Ну или использовать «include_mode_cfg_gen», если хочется направлять в VPN только отдельные подсети.
После чего в папке trickster-vpn/exclude_mode_cfg_gen/configs появятся конфиги с только что сгенерированными ключами, и останется только скопировать wg-internal.conf в /etc/wireguard/wg-internal.conf , wg-external.conf унести на другой сервер, а wg-mobile-client.conf использовать для ноутбука или телефона. Ну и не забыть о пробросе порта, если internal у тебя за NAT.
Если захочешь это все красиво обернуть в докер и прикрутить какой-нибудь веб-интерфейс для WireGuard (потому что конфиги клиентов все же удобнее создавать в нем) — напиши мне или добро пожаловать на гитхаб: Trickster VPN.
WireGuard Setup Guide for iOS
WireGuard is the new kid on the block when it comes to VPNs. I could try and explain what it is and why you should switch from OpenVPN or IPsec to it, but they do a great job of that themselves:
WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPSec, while avoiding the massive headache.
“Great” I hear you say, “but IPSec and OpenVPN are a pain to setup, let alone setup securely”. Fear not, WireGuard has you covered:
WireGuard aims to be as easy to configure and deploy as SSH. A VPN connection is made simply by exchanging very simple public keys – exactly like exchanging SSH keys – and all the rest is transparently handled by WireGuard.
This guide will walk you through how to setup WireGuard in a way that all your client outgoing traffic will be routed via another machine (server). This is ideal for situations where you don’t trust the local network (public or coffee shop wifi) and wish to encrypt all your traffic to a server you trust, before routing it to the Internet.
Server Setup Guide
I am making some assumptions here, such as using Ubuntu for your server OS, so please tweak to fit your situation.
First up, we need to make sure you’ve got the kernel headers installed, as well as enabling IPv4 forwarding:
Because WireGuard isn’t currently part of the distributed Ubuntu packages, we’ll add the helpful PPA repository to keep things easy and up to date.
With WireGuard now installed, we need to generate a public and a private key – fortunately, this is a simple one-liner:
Next up, we need to create a WireGuard config file:
We also need a DNS server running, to forward our requests from the clients rather than letting the clients go out to their local/ISP provided DNS. I have pinched most of this from ck’s Wireguard setup guide.
At this point, WireGuard on the server is complete and we could start it up if we wanted, but first, lets configure our client.
iOS Client Setup Guide
Our first client is an Apple iOS device. Currently WireGuard isn’t built into the operating system, unlike IPSec or IKEv2. However, we can easily overcome this hurdle thanks to the WireGuard iOS App which is currently in alpha and can be installed easily via TestFlight. Alternatively you can check out another 3rd party client produced by TunSafe which again can be installed via TestFlight.
With one of the two apps installed, lets generate a config server-side and we can transfer it later with a simple QR code. We could manually enter a config within the app, however moving around those keys can be a nightmare…
First up, client private and public keys:
We need to configure our mobile client as a peer within wg0.conf , so that we can connect to our server.
That is everything we need to do for our server config — simple right?
To split things up a little, lets start WireGuard. We can bring the interface up with wg-quick up wg0 , as well as down with wg-quick down wg0 .
However if you would rather run it as a service so that it is always brought up on start-up or after reboots, we can utilise systemctl :
You can get the current status of WireGuard by simply running wg :
With WireGuard successfully running, lets create a conf file for our iOS device which we will transfer via QR code. We can do all of this whilst we are still on our server.
Mobile config done. Lets get it transferred to our device. We will do this using a QR code, which we can create on our server using qrencode . You can install this via apt install qrencode .
To generate the QR code and display it on the screen:
A QR code generated from `mobile.conf`
Simply scan this code using one of the iOS apps mentioned previously, and you will be all setup! Enjoy routing all of your traffic via your server. You can verify this by visiting https://canihazip.com or similar on your device.
$ whoami I am Graham Stevens, a Cyber Security Specialist based in the South West of the UK. If you’re lucky, I very occasionally post updates on Twitter/Mastodon.
Включать Wireguard только для конкретных сайтов и\или приложений на Windows
На телефоне в Android приложении Wireguard можно зайти в редактирование используемого профиля, и изменить его режим работы с «для всех приложений» на работу только для выбранных. Выбирать сайты там нельзя, но это не проблема, так как можно включить его (например) только для второго браузера.
В приложении для Windows таких настроек не нашёл. Мб знает кто способы или альтернативное приложение для Wireguard VPN на Windows?
Глянул — выглядит чуток сложноватым для моих сетевых познаний. Ну и в идеале бы хотел прямо в браузере этим управлять. Товарищ выше (или ниже) напомнил, что расширение "все_и_так_поняли_какое" можно гибко настраивать и оно же умеет использовать внешние proxy, но там нет wireguard в настройках.
жаль что это расширение при смерти
А что для Wireguard выбирать то?
а тут так нельзя
Я тебе показал прокси и расширение, которое можно настроить для работы прокси на разных сайтах
если используешь vds, то можешь на него поставить прокси
Прокси еще можешь купить
Мб ты не до конца вопрос понял, друг, VDS у меня и так есть. Сейчас заценю wiresocks по совету чела ниже
Можно через клиент wiresocks настроить работу впн на определённые приложения
и добавить строчку к конфиг ( на сайте есть инструкция )
AllowedApps = chrome
да я умею гуглить, пошёл проверять уже
Эта страница первая в выдаче Яндекса, но ответа тут нет, поэтому пришлось разобраться самому.
Если коротко, то нужно использовать параметр AllowedIPs с перечислением подсетей, которые использует сервис.
Теперь чуть длиннее.
1. Открываем консоль. Пишем команду nslookup <имя домена>.
Например, nslookup kinopoisk.ru
Копируем любой адрес из поля Addresses.
2. Заходим на https://asnlookup.com/ и вставляем адрес. Проверяем, что страница плюс-минус совпадает с тем, что мы ожидаем.
3. Копируем и IPv4, и IPv6 адреса со страницы и создаём из них строчку с адресами через запятую.
4. Открываем файл конфигурации Wireguard и в разделе [Peer] пишем параметр
AllowedIPs = <строка с адресами>
5. Пробуем, тестируем.
Этот способ сработал у меня как на Windows, так и на iPhone (на 90%).
Выбранные мной сайты и приложения стали открываться с российской локацией.
К примеру, я одновременно могу смотреть кинопоиск и инсту, а с перенаправлением всего трафика (0.0.0.0/0) у меня инста была заблокирована, т.к. Россия её блокирует.
В файле конфигурации можно писать комментарии с помощью решётки #, а строк AllowedIPs может быть сколько угодно.
Вы можете подумать, что можно накидать много сервисов и включать, и выключать их в приложении, когда захочется.
Но, к сожалению, приложения трут комментарии, а строки разрешенных адресов склеивают в одну. Но всё равно полезно при редактировании.
Часто достаточно прописать только те адреса сервиса, где проверяется метаположение. Тогда сайт будет думать, что вы в России, но остальной траффик будет идти через основного провайдера, что увеличивает скорость.
Написал для айфона, что работает на 90%, потому что почему-то кинопоиск с такими настройками не определяет Россию, хотя на десктопе всё работает на 100%. Думаю, что надо покопаться ещё, куда там подключается приложение.
Отдельно приложениям разрешить подключаться не получится, если неизвестны адреса, куда они стучатся. Узнать можно, если просниффать траффик, но это уже не в рамках этого вопроса.