Amnezia на Mikrotik: полгода спустя

В начале года я разработал AmneziaWG контейнер для Mikrotik. С тех пор произошло довольно много изменений, и судя по тому, как на хабре часто выходят разного рода статьи, где то один, то другой придумывает и вайбкодит свой новый впн-клиент, то складывается впечатление, что об Amnezia уже все забыли, хотя она очень даже помогает решать рабочие задачи.
TLDR
В данный момент это самый быстрый контейнер (весом 94.7 КБ) для Mikrotik, который реализует протокол AmneziaWG: максимально эффективно утилизируя ресурсы роутера. Протокол AmneziaWG на 80% состоит из обычного Wireguard, и на 20% состоит из своих дополнений, поэтому берем родной микротиковский Wireguard (который очень эффективно работает с криптографией на отдельном чипе), а остальное реализовываем программно. Так получился очень маленький и быстрый контейнер, обеспечивающий почти 300мбит на устройствах типа Mikrotik Hap Ax3, и 200мбит на Mikrotik Hap Ax2. Как вишенка на торте - он сам умеет быть сервером, помогая соединять микротики друг с другом.
Что это?
Маленький контейнер, который позволяет роутерам mikrotik подключаться к amneziawg серверам, с полной поддержкой протокола AmneziaWG 1.0, 1.5, 2.0, 3.0, 3.1. Работает прямо на роутере, а значит вы можете сами настроить/донастроить нужную вам маршрутизацию.
Как работает?
Протокол AmneziaWG на 80% состоит из Wireguard. Это проверенный и надежный протокол, который ещё в 2020 году сам Линус включил в ядро Linux - он простой(значит быстрый), понятный, безопасный. Да, про безопасность в последнее время все забили подзабыли - ставят всякие непонятные клиенты из appstor'а, все вокруг кодят свои реализации vpn по принципу "лишь бы работало", хотя раньше вопрос надежности и доверия было одним из главных требований...
Так вот, остальные 20% - это накрутка AmneziaWG, которая добавляет рандомных мусорных пакетов, для усложнения обнаружения трафика. И так как в роутерах Mikrotik уже есть полноценная и хорошая реализация Wireguard, но нет AmneziaWG - значит можно взять wireguard, а остальное докодить самому. Так и появился awg-proxy - контейнер, реализовывающий amneziawg.
Бан от github'а
Когда я написал статью и опубликовал её - буквально в тот же день проект набрал 150 звезд(!). Я обрадовался, чуть дотюнил проект, покрыл её тестами, лёг спать, а на утро увидел, что github его забанил! В личку уже полетели вопросики, а я, толком не спав, вскочил и начал разбираться. Сначала подумал, что так github душит всё что ему кажется подозрительным, отправил аппеляцию, но она рассматривается очень долго. Сильно не тупил - создал второй аккаунт, переопубликовал проект в нём (не делая изменений), но и его github очень быстро удалил. В это время уже наступило утро, просмотры статьи росли, а ссылки вели на нерабочие контейнеры.
Поняв, что я делаю что-то не так пошел думать, где же я резко ошибся. Спустя пару часов я понял, что причина в моих новых свежих тестах: по ошибке, я добавил и по глупости закоммитил в проект тесты, которые запускаются при сборке и коннектятся к сетевым адресам. А сборку я сделал сразу на github actions. А это сразу красный флаг: кто-то просит "github actions" подключаться к каким-то внешним серверам, что очень похоже на вредоносную активность, за которую гит быстро банит. В общем, я вырезал такие тесты, и уже текущий репозиторий (на момент публикации статьи имеет 371 звезду) больше не банился (тьфу-тьфу!). С самого начала я ориентировался на простоту, поэтому создал конфигуратор, с которым довольно легко можно получить список команд для микротика, когда лень разбираться (мне самому было бы лень всё это изучать).
Amnezia 2.0?
Буквально первый же комментарий от @sirota сообщил, что amneziawg буквально недавно выпустила версию awg 2.0. Пока я был занят разработкой - я проспал это нововведение, и оперативно добавлял поддержку этой версии. Из ключевого, в awg 2.0 значительно усилили протокол, добавив рандомный размер паддинга пакетов, что по сути смещает весь траффик от wireguard на рандомное количество байт, а значит, станосится сложнее определить что это за трафик.
А можно быстрее?
Контейнер был написан на Go, но всё равно мне казалось что можно быстрее. В итоге, с помощью claude code контейнер был переписан на Си, что дало выигрыш как по скорости и памяти, так и по размеру: контейнер уменьшился с 5МБ до 80-90КБ, а пропускаемые через себя трафик - сразу на 50%. Причина очень проста: Go тянет довольно тяжелый "лишний" обвес в виде дополнительных инструкций безопасности, в то время как Си реализует всё нативно. Меньше инструкций = меньше тактов ЦПУ на пакет = выше пропускная способность трафика.
Чтобы их не смешивать и всё-же оставить дань наследия решению на Go, создал отдельный репозиторий, он и стал основным.
Отдельно хочу поблагодарить Виктора @wiktorbgu, за оказанную поддержку и независимые тесты. У него есть свой неплохой канал по сетевым технологиям - советую подписаться (ссылок не будет, найдете если надо. А у меня своего нет и наверное не будет, сорян, даже не ищите хе-хе).
Есть потребность связывать микротики друг с другом
Представим ситуацию: вам нужно связать два офиса друг с другом, но обычные протоколы не работают по той или иной причине. А у нас вроде есть рабочий awg, но он не умеет связывать микротики друг с другом. Не беда - доработка напильником, реализация реверс-алгоритма, и мы получаем серверный режим awg. Под капотом - всё тот же родной микротиковский Wireguard, обеспечивающий безопасность и быстрое шифрование на отдельном чипе, снаружи - протокол awg в контейнере.
Чтобы было легче добавлять будущих клиентов - в разделе mikrotik: container -> envs сохраняется ссылка на конфигуратор с параметрами вашего конфига амнезии. Просто открываете его, и ваш конфигуратор просто спрашивает какого по счету №№ клиента генерируем.
Конфигуратор, кстати, полностью оффлайновый. Ни один запрос, ни одна кнопка или метрика не отправляет запрос наружу. Всё в стиле шифропанка, как мы любим.
Что было потом
Потом добавил поддержку dns-имён (даже в самой amneziavpn она появилась много позже) в виде адреса сервера. Полезно, когда адрес динамический. Было несколько ошибок, когда ip от dns резолвился лишь один раз при старте контейнера, а при его смене, контейнер не утруждался узнать новый. Добавил поддержку RU-диапазона адресов, для динамической(выборочной) маршрутизации трафика.
Начали поступать issues в проект, один из них пожаловался, что контейнер плохо работает на PPPOE. Оказалось, проблем несколько: неоптимальный и несогласованный MTU, из-за которого пакет выходил из Wireguard размером с 1420 байт, а выходной интерфейс его мог порезать до 1400 байт. Изучив TCP, выяснил что стоит обнулять специальный флаг DF (don't fragment), указывающий на запрет фрагментации пакета, и так у нас пакеты дробились, что повышало качество протокола.
С MTU вообще отдельная история: по-умолчанию в wireguard размер пакета (mtu) составляет 1420 байт (20 байт IPv4 + 8 байт UDP + 32 байт WireGuard + 20 байт внутренняя инкапсуляция IPv4 = 80 байт от обычных 1500 в ethernet). Далее, идет обвязка awg которая с версии AWG 2.0+ съедает от 0 до 128 байт от полезного трафика. Всё зависит от значения S4: чем его диапазон больше, тем больше отъедается полезной нагрузки, а значит и пропускной способности.
Далее добавил поддержку ipv6 - в оригинальном amneziawg она есть из коробки (как и в wireguard), а вот AmneziaVPN всё ещё не поддерживает его, хотя казалось бы - в наше время легко реализуется с помощью кодинговых агентов. Собственно, хоть и ipv6 внедряется очень медленно, но не реализовать его поддержку там, где он есть - было бы большой ошибкой, так-что теперь ipv6 тоже полностью поддерживается в контейнере, в том числе с работой через dns, в том числе в dual-режиме: если по dns-имени получаем сразу 2 адреса: ipv6+ipv4, то сначала пытаемся подключиться по ipv6, а в случае неудачи переключается на ipv4.
Отсюда получаем таблицу полезной нагрузки (MTU) - если значение S4 больше 16, то полезная нагрузка значительно снижается. Поэтому, его рекомендуется держать в пределах 12-16.
Размер S4 (в байтах) | MTU IPv4 | MTU IPv6 |
|---|---|---|
0 | 1420 | 1420 |
12 | 1420 | 1408 |
16 | 1420 | 1404 |
24 | 1416 | 1396 |
65 | 1375 | 1355 |
148 | 1292 | 1272 |
Конечно, это всё добавил в конфигуратор, чтобы он видя ваш S4 сам подбирал MTU в изначальном wireguard-интерфейсе.
Awg 3.0
В августе вышел AmneziaWG 3.0: он довольно интересно реализовывает имитацию поведения и маскировку под довольно популярные протоколы, такие как Quic, и вообще позволяет задавать свои паттерны пакетов (подробнее здесь, неплохой разбор от @meshroom). К тому времени я взял уже полюбившийся Claude Code и адаптировал контейнер под него. Не то, чтобы я этим горжусь, но и время своё я ценю. Обложил контейнер тестами, погонял вдоль и поперёк у себя, выложил.
Недооценённая фича: Рандомизация портов
После очередной волны банов (в т.ч. моего сервера. за что? я ведь только до нужных мне сервисов дотягивался...), я задумался как же сделать так, чтобы сервер вызывал меньше подозрений. В общем я пришел к выводам:
Протокол AWG 3.0 в целом неплох
Однако всё-равно серверы попадают в бан
Чтобы снизить подозрительность сервера, надо всем клиентам менять порт. Лучше почаще, так как это делает тот же torrent или webrtc. Рандомные порты в обе стороны = сервер проживет дольше.
Очень много внимания в интернете уделяется маскировке трафика, и так мало внимания уделяется простой периодической смене портов. Это как гигиена смены ipv4 адреса, только для портов. Чем больше трафика идет с разных адресов на один и тот же порт целевого сервере - тем больше накапливается "статистики" и подозрений. Так-что чтобы его избежать был придуман (я не заявляю первенство, но и не подсматривал ни у кого такое) довольно простой механизм: на сервере настраивается проброс диапазона портов через iptables в amneziawg server, и на клиенте в контейнере прописывается порт в виде диапазона: 10000;20000;55000-57000 - контейнер при подключении берет рандомный из них и пробует подключиться.
В версии 1.4.5 (актуальная) научил контейнер самостоятельно прыгать-периодически менять порт (главное прописать в ENV контейнера диапазон портов). Как мне кажется, это очень недооцененная фича, и я был бы рад, если бы её завезли в оригинальный awg/amneziavpn клиенты.
Генерация ключей по ssh?
Многие из вас пользовались AmneziaVPN - она позволяет сгенерировать конфиг подключения. Однако у неё есть очень важный изъян: она подключается к серверу по ssh и генерирует ключик, выполняя команды на сервере. Парочку моих серверов забанили буквально через несколько часов после того, как я с домашнего/мобильного интернета подключился к серверу. Кроме того, Appstore перестал принимать обновления клиентов amneziavpn в котором они реализовали протокол awg 3.0, так-что, например, моя знакомая не могла им пользоваться. Единственный обновляемый был AmneziaWG , который является форком от Wireguard: всё тот же неприятный туповатый интерфейс, который при создании туннеля просит назвать его на латинице. Так вот, чтобы решить сразу обе эти проблемы я завайбкодил телеграмм-бота, который помогает генерировать конфиги для амнезии. Он поддерживает как докерезированный amneziavpn, так и базовый нативный amneziawg server, и выдаёт строку подключения сразу как в виде QR-кода для AmneziaWG, так и ключик текстом для amneziavpn.
Ipv6
Про ipv6 стоит сказать отдельно: он не просто поддерживается, а очень даже работает. Поддерживается как клиентский режим, так и серверный режим(когда ваш микротик принимает подключения). И если вы ещё не используете ipv6 - советую всё-же перейти на него.
Оффлайн версия
Несколько issues в проекте висели довольно долго - там просили добавить режим оффлайн-установки. Это когда роутер не имеет доступа в интернет(до github), но их по какой-то причине им надо установить контейнер. Такой вариант оффлайн-установки тоже был добавлен в конфигуратор.
Увеличение скорости
До сих пор контейнер упирался примерно в ~180мбит на download и ~80мбит на upload на протоколе awg 3.0 (на awg 1.0 выжимаются все 250мбит в обе стороны). При этом, во время спидтеста скорость скачет от 250мбит до 100мбит. Столь значительный разбег просто не давал мне спать. Я не мог понять из-за чего он случается! Я перепробовал 100500 разных техник и механик - но все они были безрезультатные - всё упирается в фиксированный размер буфера интерфейса в 416 КБ , и ничем его нельзя было увеличить (вендор лок от микротика). Во время спидтеста (или прочей высокой нагрузки на сеть) происходит вот что:
Компьютер в сети отправляет запрос на скачивание
Удаленный сервер начинает отдавать вам кучу контента
Этот контент поступает на интерфейс микротика, который ограничен 416 КБ
CPU, выделенное на контейнер не успевает обрабатывать пакеты (я всё пробовал чтобы успевал - никак)
В итоге микротик отбрасывает пакеты, которые не успели обработаться
Растет количество потерь
Протокол TCP видя потери снижает размер окна вдвое (скорость режется вдвое)
В итоге скорость резко проседает и довольно медленно растёт, пока снова не вызовутся потери
И вот, выходит версия RouterOS 7.24 в котором они втихушку выпускают параметр privileged=yes, который позволяет разблокировать в контейнере буквально всё, и самое главное - позволяет управлять размером буфера! После кучки проб я остановился на размере буфера в 32МБ - это достаточно много, чтобы успеть всё обработать, и по идее не должно сильно отъедать микротики. Даже на самых простых микротиках вроде hap ac2 оперативная память составляет 128МБ, большая часть из которых свободно.
Такое увеличение буфера позволило наконец-то убрать ограничения буфера интерфейса, и наконец упереться в ЦПУ. Наконец-то мы достигли максимально возможной производительности!
AWG 3.0 Hap ax2: 200-250mbit
AWG 3.0 Hap ax3, RB5009B: 250-350mbit
На данный момент этот контейнер является самым быстрым решением, реализующим amneziawg протокол. Если у вас этот протокол получится ещё лучше - пишите, я с радостью поставлю на вас ссылки!
Поддержка WARP-клиентов
Среди всех issues несколько раз всплывало то, что часть людей использовали этот контейнер в виде warp-клиента. Я такого поведения не ожидал, я затачивал и тестировал только на amneziawg-клиентах. Во всех этих issues просили добавить парочку функций по автовосстановлению соединения, что собственно и было добавлено.
Итого
С момента публикации контейнер значительно преобразился - повышена производительность и устойчивость к ошибкам, реализовано довольно много фич. Самое главное - такие контейнеры работают стабильно, проект набрал 370+ звезд и продолжает развиваться.
Собственно всё, надеюсь вы узнали из данного материала что-то новенькое!
Disclaimer. Материал носит образовательный и ознакомительный характер и посвящён вопросам работы протокола AmneziaWG. Примеры приводятся для сценариев защищённого удалённого доступа к собственным системам и инфраструктуре (администрирование, корпоративные сети, тестовые стенды). Не используйте информацию из статьи для нарушения законодательства РФ и иных применимых норм, а также правил площадок и провайдеров. Автор не несёт ответственности за последствия использования описанных подходов.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.