
Как-то в мою практику системного администратора поступил простой и понятный, но, как выяснилось, не самый тривиальный запрос: как организовать работу так, чтобы клиентам WiFi не требовалось включать VPN или прокси. Подключился к сети — [вставьте ваш любимый сервис] заработал. Красота. При этом нужно, чтобы было надёжно, без сбоев. И администрировать удобно.
Основная задумка — сделать всё по минимуму. Без скриптов, без тяжёлого mangle. Чем ближе к чистой маршрутизации — тем лучше.
В общем, тем, что у меня получилось, я и хочу поделиться. В этой статье не будет ничего про аренду и настройку VPS, обзоры технологий прокси/VPN и тому подобное. Предполагается, что всё уже готово и настроено.
Сразу предупреждаю: это не руководство для новичков. Я постараюсь, но это скорее поток мыслей для коллег, которые бороздят просторы большого интернет-театра в поисках вдохновения для собственных проектов.
MikroTik
Почему именно MikroTik? Ответ простой — у меня он стоит буквально везде. И, по моему скромному мнению, это всё ещё безусловный лидер малого и среднего бизнеса по соотношению цена/возможности. Ну и часть устройств досталась по наследству — не менять же.
За основу берём RouterOS 7.22 (на момент написания статьи). Начиная с версии 7.19, седьмая версия стала вполне пригодной для использования, без серьёзных багов и отвалов. Для сомневающихся есть long-term прошивка на базе 7.21.
Главная её проблема в связке с xray — это неумение выступать инициатором SOCKS/HTTP. Сервером — пожалуйста, а клиентом, увы. Здесь нас выручает Wireguard из ROS7 — это единственный протокол, который есть и там, и там. Плюс: можно тянуть туннель куда угодно — хоть в локальную сеть, хоть на внешний сервер, хоть в контейнер, разницы никакой.
BGP
BGP нам нужен только для одной цели — забирать маршруты/префиксы из внешнего источника. Какого именно — выбирать по ситуации. Можно и свой собрать, но это уже дело вкуса.
Недоброжелатели утверждают, что бюджетный MikroTik не вытянет 100 тысяч+ префиксов, полученных по BGP, но это актуально только для старых прошивок. Видимо, в новых что-то подкрутили, и даже на скромном hEX 120 тысяч маршрутов держатся вполне стабильно, хоть и со скрипом.
Это закроет большую часть наших потребностей в маршрутизации, практически ничего не требуя взамен. Остальное мы добьём в следующем разделе.
DNS Forward
Если по каким-то причинам нужные нам домены и, соответственно, IP-адреса не попали в основной список маршрутов, то вручную мы будем заводить их здесь.
Логика простая. Клиент захотел на сайт. Роутер, как обычно, рекурсивно резолвит домен, отдаёт клиенту адрес, но заодно копирует его себе в табличку, и все запросы на эти адреса маршрутизирует на нужный шлюз.
Главная фишка — возможность одной галочкой закрыть сразу все поддомены указанной зоны, чего не может, например, чистый address-list. Более того, оно ещё и обновляется в зависимости от того, какой именно адрес тебе отдаёт сейчас домен. Минимум ручного труда, никакого мусора — лучше не придумаешь. Списки доменов конкретного сервиса часто можно нагуглить как «настройка за корпоративным прокси».
Xray
Почти золотой стандарт в наши дни, ничего не скажешь. На Хабре только ленивый не прошёлся по его настройке (и я в том числе). Как правило, это простенькие конфиги из-под панелей типа 3X или взятые примеры из документации. HTTP/SOCKS in > VLESS out. Хорошо, но уже не вдохновляет.
Мы пойдём немного дальше, и я покажу свой боевой конфиг с несколькими серверами, проверкой доступности, балансировкой нагрузки и ручным приоритетом по весам в зависимости от наших потребностей. Выжимаем максимум из ядра, без особого колхоза.
В нашем случае именно он является VPN/прокси-клиентом для всей нашей локальной сети.
Подготовка маршрутизатора
Начнём с настройки MikroTik. Буду использовать команды терминала, но их легко конвертировать во вкладки Winbox — порядок тот же.
Из IP-адресов нас интересуют только 3: адрес сервера с xray на борту и 2 адреса, которые мы назначим на концах туннеля. Первый мы укажем как точку подключения по WG, а туннельный адрес xray послужит шлюзом для всех кастомных маршрутов на MikroTik. В примере даны 192.168.0.0/24 для локалки и 172.16.0.0/30 для туннельных адресов.
Сюда мы будем складывать наши маршруты. Можно и в main, но это создаст неудобства в быту.
Логика такая: хост ищет в кастомной таблице маршрут до узла. Если не находит — fallback в main, где есть шлюз последней надежды, он же default gateway. На случай, если xray в локалке, нужно добавить правило, которое запретит ему все таблицы, кроме main, чтобы избежать петли.
Полученные по BGP маршруты имеют кривой шлюз. Меняем его на правильный. Опционально можем задать проверку на доступность. Если шлюз отвалится, проверка отключит маршруты и пустит трафик по умолчанию. Может быть полезно, если используются несколько шлюзов.
Для примера привожу подключение к сферическому BGP-хосту в вакууме. Если источников несколько, предпочтение отдаётся более точному маршруту, поэтому в большинстве случаев конфликтов быть не должно. Не забудьте поменять router-id на корректный. После создания наша кастомная таблица начнёт наполняться маршрутами.
Создаём интерфейс и peer. Не забываем подставить публичный ключ из xray.
Осталось дело за малым: настроить кастомные домены для маршрутизации. DNS даны для примера, лучше, конечно, использовать DOH/DOT. Параметр address-list-extra-time определяет, сколько времени адрес будет висеть в листе. Я бы не делал слишком много, чтобы не засорять, но и не слишком мало.
Теперь нужно как-то превратить address-list в настоящие маршруты. В mangle action появилось новое действие route. Согласно документации, оно игнорирует все настройки роутинга и принудительно задаёт пакету шлюз. Что нам более чем подходит. Убиваем сразу двух зайцев — не нужно создавать отдельную таблицу и правило для неё.
На этом подготовка роутера завершена. Таблицы ломятся от маршрутов, все кастомные домены выписаны — осталось только открыть врата нашего VPN-туннеля.
Подготовка xray
Теперь настало время заняться самим прокси. Базовая схема выглядит так: WG in > VLESS1/VLESS2/…/VLESSN out. Outbound, в принципе, может быть любым, не обязательно именно VLESS.
Для примера приведу обезличенный конфиг со своего сервера и его краткое описание. Развёрнутое описание, в целом, неплохо стала давать нейронка — можно смело туда загнать, она расскажет подробнее.
«observatory» — отправляет healthcheck-запрос на outbounds. Если хост недоступен — исключает его.
«observatory» — отправляет healthcheck-запрос на outbounds. Если хост недоступен — исключает его.
«inbounds» — задаём настройки для WG. В данном случае он работает только как сервер, сам он подключения, если верить документации, устанавливать не может. Пару ключей генерируем сами и меняемся публичными с MikroTik.
«inbounds» — задаём настройки для WG. В данном случае он работает только как сервер, сам он подключения, если верить документации, устанавливать не может. Пару ключей генерируем сами и меняемся публичными с MikroTik.
«outbounds» — как обычно, перечисляем список всех наших конечных узлов.
«outbounds» — как обычно, перечисляем список всех наших конечных узлов.
«routing» — из экзотики правила теперь ссылаются на тег балансировщика, а не на чистый outbound. Также добавил на всякий случай исключение для ру-доменов, если какой-то случайно затесался на MikroTik.
«routing» — из экзотики правила теперь ссылаются на тег балансировщика, а не на чистый outbound. Также добавил на всякий случай исключение для ру-доменов, если какой-то случайно затесался на MikroTik.
«balancers» — fallbackTag можете не смотреть, это заглушка. По задумке тут должен быть какой-то 4-й сервак, который не участвует в основной движухе, либо freedom/blackhole.
«balancers» — fallbackTag можете не смотреть, это заглушка. По задумке тут должен быть какой-то 4-й сервак, который не участвует в основной движухе, либо freedom/blackhole.
Блок «costs» работает только с типом leastLoad. Настройки дефолтные. Чем ниже вес — тем выше приоритет.
Блок «costs» работает только с типом leastLoad. Настройки дефолтные. Чем ниже вес — тем выше приоритет.
ПНР
Собственно, запускаем xray, смотрим логи на предмет ошибок в конфиге или проблем с серверами. Поглядываем на MikroTik — счётчик last handshake должен начать тикать и обнуляться каждые 1.5–2 минуты. Значит, VPN-соединение установлено. Обязательно пингануть адрес шлюза, чтобы убедиться, что пакеты уходят и приходят.
Если всё в порядке, система должна начать работать. В логах xray будут видны записи соединений и их теги — можно убедиться, что балансировка и проверка доступности отрабатывают корректно.
Итоги
Получилась не серебряная пуля, но для моей задачи вариант оказался удачным: всё работает из коробки, локальная сеть живёт как раньше, маршрутизатор занимается маршрутизацией, xray занимается проксированием, а я вспоминаю об этой конструкции только когда нужно добавить очередной домен или поменять сервер.