
TLDR: Для Mikrotik'ов на базе Arm, Arm64 и Amd64 создана рабочая реализация AmneziaWG для подключения к AmneziaWG и AmneziaVPN серверам. Для воспроизводимой настройки создан небольшой оффлайн(!) конфигуратор, который по входному amneziawg.conf формирует набор команд для RouterOS Terminal (и скрипт очистки): https://amneziawg-mikrotik.github.io/awg-proxy/configurator.html. Итоговый контейнер весит очень мало, почти не потребляет ЦПУ (1-2%), использует 7-10 МБ Ram на ARM64.
Github: https://github.com/amneziawg-mikrotik/awg-proxy
upd: Добавлена поддержка протокола AmneziaWG v2.
upd2: провели тестовые замеры скорости. На линке 300мбит выжали +-250мбит (теоретический предел - 280 мбит). Видеозамер в гитхабе.

Disclaimer. Материал носит образовательный и ознакомительный характер и посвящён вопросам совместимости реализаций WireGuard/AmneziaWG и разбору сетевой упаковки пакетов/handshake. Примеры приводятся для сценариев защищённого удалённого доступа к собственным системам и инфраструктуре (администрирование, корпоративные сети, тестовые стенды). Не используйте информацию из статьи для нарушения законодательства РФ и иных применимых норм, а также правил площадок и провайдеров. Автор не несёт ответственности за последствия использования описанных подходов.
Одним очередным томным вечером приходя домой вдруг снова обнаруживаешь, что соединение с сервером нестабильно и периодически обрывается. Вполне безобидный сервер, для доступа к рабочим инструментам вдруг стал недоступен. В очередной раз решил поискать в интернете, может кто-то уже реализовал нормальный клиент AmneziaWG в Mikrotik:
На форуме mikrotik так ничего и не появилось, а из самых доступных реализаций находится только этот контейнер на 36 МБ (от @wiktorbgu, он много лет поддерживал его, поставьте + в карму за поддержку) с полной реализацией AmneziaWG-клиента, но на моем стареньком Mikrotik доступно всего 7 МБ :'( , поэтому этот вариант отпадает. (UPD: по пользовательским замерам, у моего решения нагрузка на CPU примерно в 1,5-2 раза ниже, чем у контейнера на 36 МБ по ссылке выше).
Выхода нет. Почему бы не написать реализацию самому с нуля?
Варианты реализации
Первым делом полез искать вариант реализации своего Package, который можно было бы установить максимально нативно, чтобы он работал на уровне ядра Mikrotik. К сожалению, этот вариант отпадает первым - Routerboard закрытая проприетарная система, а пакеты требуют цифровую подпись.
Второй вариант - создать свой минималистичный container по реализации AmneziaWG клиента. Туда нужно прокинуть все настройки подключения, научить его не только подключаться и работать с криптографией, но и корректно маршрутизировать сквозь себя трафик. При реализации очень легко ошибиться с криптографией, а сам бинарник, по моим подсчетам, будет в районе ~15 МБ. Не говоря уже о дублировании почти всего, что в mikrotik уже есть.
Третий вариант пришел неожиданно. У нас есть Mikrotik, в котором из коробки уже есть прекрасная реализация Wireguard. А AmneziaWG это тот же Wireguard, с небольшими отличиями. Вся разница - во фрейминге пакетов: перед тем как безопасное соединение установится, две точки должны обменяться рандомными мусорными пакетами. А дальше идет обычное соединение Wireguard!
То есть всё, что нам нужно - это реализовать первую фазу установления соединения: "поздороваться" с сервером на языке AmneziaWG, а дальше просто передать всё управление интерфейсу Wireguard'а! Всего лишь обменяемся мусорными пакетами (так думал я поначалу и глубоко ошибался), а всю дальнейшую маршрутизацию, криптографию и пиринг передадим в руки нативного Wireguard в mikrotik. Такая реализация простой udp-прокси по моим подсчетам могла уложиться в виде go-бинарника размером 3-4 МБ, что соответствует моим ожиданиям.
В итоге мы по-максимуму переиспользуем уже готовый и реализованный Wireguard в самом Mikrotik, а допишем и реализуем только то, что ему не хватает - Handshake с AmneziaWG!
Mikrotik Wireguard -> UDP -> [awg-proxy container] -> UDP -> AmneziaWG Server
Приступаем к реализации
Был выбран язык Go, который будет компилироваться из scratch-контейнера в самый минималистичный вариант. Go довольно быстрый, позволяет легко собрать бинарник, не требующий скрытых зависимостей, да и вообще я его сейчас практикую, почему бы и нет?
Вполне возможно, реализация на С была бы ещё меньшего размера, но у меня в нём мало практики, а я не собирался стрелять себе в ноги, я лишь хочу решить свою маленькую проблему. Так что выбор был остановлен на Go.
Протокол WireGuard изнутри
Чтобы понять, что именно трансформирует прокси, нужно заглянуть в формат пакетов WireGuard. Не в криптографию (она нас не касается), а именно в структуру датаграмм:
WireGuard использует ровно четыре типа UDP-сообщений. Тип кодируется как uint32 в little-endian порядке в первых 4 байтах каждого пакета:
Тип |
Значение |
Размер |
Назначение |
|---|---|---|---|
1 |
Handshake Init |
148 байт (фикс.) |
Инициация handshake, первое сообщение Noise IK |
2 |
Handshake Response |
92 байта (фикс.) |
Ответ на handshake, второе сообщение Noise IK |
3 |
Cookie Reply |
64 байта (фикс.) |
Cookie для защиты от DoS (rate limiting) |
4 |
Transport Data |
32+ байт (перем.) |
Зашифрованные данные пользователя |
Три handshake-пакета имеют фиксированный размер. Transport data - переменный (зависит от размера payload). Это важно: прокси идентифицирует вид пакета по комбинации (тип в первых 4 байтах, общий размер датаграммы). Это надёжнее, чем полагаться только на тип - например, случайные данные с type=1 но размером 200 байт явно не handshake init.
В коде это выглядит так:
// Standard WireGuard message types (little-endian uint32 in first 4 bytes). const ( wgHandshakeInit uint32 = 1 wgHandshakeResponse uint32 = 2 wgCookieReply uint32 = 3 wgTransportData uint32 = 4 ) // Standard WireGuard packet sizes. const ( WgHandshakeInitSize = 148 WgHandshakeResponseSize = 92 WgCookieReplySize = 64 WgTransportMinSize = 32 )
Побайтовая раскладка Handshake Init
Самый важный для нас пакет - Handshake Init. Именно на нём сломается всё, что может сломаться. Вот его структура:
Handshake Init (148 байт): +--------+--------+--------+----------+---------+---------+------+ | type | sender | epheme | static | timesta | mac1 | mac2 | | uint32 | uint32 | ral | (encryp) | mp(enc) | 16 B | 16 B | | 4 B | 4 B | 32 B | 48 B | 28 B | | | +--------+--------+--------+----------+---------+---------+------+ 0 4 8 40 88 116 132 148 |<------------- MAC1 покрывает [0:116] ------------->|
Поля: type (4 байта) - тип сообщения; sender (4 байта) - индекс отправителя; ephemeral (32 байта) - эфемерный публичный ключ Curve25519; static (48 байт) - зашифрованный статический публичный ключ + Poly1305 тег; timestamp (28 байт) - зашифрованный TAI64N-таймстамп + тег; mac1 (16 байт) - хэш по алгоритму BLAKE2s-128 MAC; mac2 (16 байт) - опциональный cookie MAC (нули, если cookie не требуется).
Обратите внимание на MAC1: 16 байт по смещению [116:132]. Хэш MAC1 вычисляется алгоритмом BLAKE2s-128 (с ключом) от первых 116 байт пакета. Ключ для MAC1 - это BLAKE2s-256("mac1----" || server_public_key).
Поле type входит в расчёт MAC1. Запомните этот факт. Мы к нему вернёмся. И когда вернёмся - станет очень больно.
Что делает AmneziaWG
TLDR: Амнезия генерирует мусорные пакеты в том порядке и размере, в котором это ждет сервер.
AmneziaWG модифицирует данные тремя способами:
1. Замена типов (H1-H4). Стандартные значения 1, 2, 3, 4 заменяются на произвольные uint32. Например, вместо type=1 для Handshake Init может использоваться type=1013049720. Это рандомизированные значения, уникальные для каждой конфигурации - клиент и сервер договариваются о них заранее.
2. Паддинг (S1/S2). Перед handshake init вставляются S1 случайных байт. Перед handshake response - S2 байт. Пакет из 148 байт превращается в (S1 + 148) байт. Это меняет характерный размер пакета.
3. Junk-пакеты (Jc/Jmin/Jmax). Перед отправкой Handshake Init клиент отправляет Jc пакетов случайного размера (от Jmin до Jmax байт) со случайным содержимым. Сервер их получает и отбрасывает. Это маскирует характерный паттерн "один пакет 148 байт - начало сессии".
Криптография при этом не меняется. Noise IK handshake, Curve25519, ChaCha20-Poly1305 - всё идентично стандартному WireGuard. Вся нагрузка (генерация ключей, шифрование, расшифровка) остаётся на MikroTik. Прокси трогает только внешнюю обёртку.
Трансформация пакетов
Теперь к коду. Трансформация - сердце прокси. Два направления: outbound (WG -> AWG) и inbound (AWG -> WG).
Outbound: от WireGuard к AmneziaWG
Алгоритм outbound-трансформации:
Прочитать тип пакета из первых 4 байт (uint32 LE)
Определить вид пакета по паре (тип, размер)
Заменить тип на H1/H2/H3/H4
Для handshake init: пересчитать MAC1 (подробнее в секции 4)
Для handshake init/response: добавить S1/S2 случайных байт перед пакетом
Для handshake init: вернуть флаг "перед отправкой послать junk-пакеты"
TransformOutbound
func TransformOutbound(buf []byte, n int, cfg *Config) (out []byte, sendJunk bool) { if n < 4 { return buf[:n], false } msgType := binary.LittleEndian.Uint32(buf[:4]) switch { case msgType == wgHandshakeInit && n == WgHandshakeInitSize: // Replace type and recompute MAC1. binary.LittleEndian.PutUint32(buf[:4], cfg.H1) if cfg.ServerPub != ([32]byte{}) { recomputeMAC1(buf[:n], cfg.mac1keyServer) } if cfg.S1 > 0 { out = make([]byte, cfg.S1+n) randFill(out[:cfg.S1]) copy(out[cfg.S1:], buf[:n]) } else { out = buf[:n] } return out, cfg.Jc > 0 case msgType == wgHandshakeResponse && n == WgHandshakeResponseSize: binary.LittleEndian.PutUint32(buf[:4], cfg.H2) if cfg.S2 > 0 { out = make([]byte, cfg.S2+n) randFill(out[:cfg.S2]) copy(out[cfg.S2:], buf[:n]) } else { out = buf[:n] } return out, false case msgType == wgCookieReply && n == WgCookieReplySize: binary.LittleEndian.PutUint32(buf[:4], cfg.H3) return buf[:n], false case msgType == wgTransportData && n >= WgTransportMinSize: // Hot path: replace type in-place, no allocation. binary.LittleEndian.PutUint32(buf[:4], cfg.H4) return buf[:n], false default: return buf[:n], false } }
Hot path: transport data
После завершения handshake 99%+ трафика - это transport data. Для этих пакетов трансформация максимально дешёвая:
case msgType == wgTransportData && n >= WgTransportMinSize: // Hot path: replace type in-place, no allocation. binary.LittleEndian.PutUint32(buf[:4], cfg.H4) return buf[:n], false
Одна запись 4 байт прямо в исходный буфер. Zero allocation - новый слайс не создаётся, возвращается подслайс входного буфера. Никакого паддинга, никаких junk-пакетов, никакого пересчёта MAC1. Этот путь - самый частый и самый быстрый. Для гигабита трафика это тысячи пакетов в секунду, и каждый обрабатывается одной записью PutUint32.
Inbound: от AmneziaWG к WireGuard
Обратная трансформация сложнее, потому что нужно учитывать паддинг и определить тип по заменённому значению.
Входящий пакет может быть:
Handshake init с S1 байтами паддинга: размер = S1 + 148, тип H1 по смещению S1
Handshake response с S2 байтами паддинга: размер = S2 + 92, тип H2 по смещению S2
Cookie reply без паддинга: размер = 64, тип H3
Transport data без паддинга: размер >= 32, тип H4
Junk-пакет: не подходит ни под одно правило - отбрасывается
Для каждого варианта: проверяем общий размер датаграммы, читаем тип с учётом смещения паддинга, если тип совпадает с Hx - заменяем обратно на стандартный WireGuard-тип и отрезаем паддинг. Для handshake response - дополнительно пересчитываем MAC1, потому что MikroTik тоже проверяет MAC1 входящих пакетов.
Если пакет не подошёл ни под одно правило - возвращаем valid=false, и прокси его просто отбрасывает. Это нормальное поведение для junk-пакетов.
randFill: случайные байты быстро
Для заполнения паддинга и junk-пакетов случайными данными нужна быстрая функция. crypto/rand можно использовать, но для мусорных данных я взял быстрый PRNG, чтобы не упираться в syscalls/производительность. math/rand/v2 - наш выбор: быстрый PRNG, достаточный для мусорных данных (криптографическая стойкость здесь не нужна).
Но побайтовая генерация медленная: rand.IntN(256) на каждый байт - это лишние вызовы. Решение - генерировать по 8 байт за раз через rand.Uint64():
func randFill(b []byte) { for i := 0; i+8 <= len(b); i += 8 { binary.LittleEndian.PutUint64(b[i:i+8], rand.Uint64()) } // Handle remaining bytes. tail := len(b) & 7 if tail > 0 { v := rand.Uint64() off := len(b) - tail for j := 0; j < tail; j++ { b[off+j] = byte(v >> (j * 8)) } } }
Один вызов rand.Uint64() даёт 8 байт псевдослучайных данных. Основной цикл заполняет буфер блоками по 8 байт. Остаток (0-7 байт) обрабатывается побитовым сдвигом одного uint64. Для заполнения буфера в 500 байт (типичный junk-пакет) это 63 вызова rand.Uint64() вместо 500 вызовов rand.IntN(256).
Побитовая маска & 7 вместо % 8 - микрооптимизация, но для паддинга и junk'а эта функция вызывается часто. math/rand/v2 использует ChaCha8 в качестве PRNG - это быстро и даёт хорошее распределение, достаточное для заполнения мусорных данных. Для криптографических целей (генерация ключей, nonce) math/rand непригоден - но мы им и не пользуемся для этого.
Ловушка с MAC1: 3 дня в тишине
Баг, над которым просидел 3 дня.
Вроде работает но не работает
Я написал прокси, проверил код: пакет превращается из WG в AWG и обратно без потерь: тип восстанавливается, payload побайтово совпадает с оригиналом. Junk-пакеты генерируются правильного размера. Паддинг добавляется и снимается.
Запускаю прокси локально, указываю на реальный AWG-сервер. Конфигурация - 13 env-переменных, трижды проверенных по .conf-файлу. WireGuard-клиент отправляет handshake init через прокси. В логах прокси вижу: c->s: recv 148B, send 194B, junk=true. Пакет принят, трансформирован (148 + S1 = 194 байта), junk-пакеты отправлены. Всё по плану.
Жду handshake response.
Тишина.
Ни ответа, ни ошибки. WireGuard-клиент ждёт 5 секунд и ретранслирует handshake init. Прокси послушно трансформирует каждую ретрансляцию. Junk-пакеты летят. Трансформированные init'ы летят. Сервер - молчит. 5 секунд, 1, 3, минуты. WireGuard сдаётся: handshake did not complete. Ничего. Wireguard тихо молча отваливается по таймауту (и это внесло больше всего смуты).
Может, проблема в параметрах?
Первая мысль - я неправильно прочитал конфиг. Открываю .conf-файл, перепроверяю все параметры: H1-H4, S1, S2, Jc, Jmin, Jmax. Всё совпадает. Перепроверяю ещё раз, побуквенно. Совпадает.
Запускаю tcpdump на стороне сервера (благо, есть root-доступ). Пакеты приходят. Правильного размера: S1 + 148 = 194 байта. Перед ними - 4 junk-пакета в правильном диапазоне размеров (10-50 байт). Читаю hex-дамп, нахожу тип по смещению S1 - H1 на месте.
Три дня я перепроверял, методично исключая гипотезы:
Значения H1-H4 - три раза сверил с конфигом, конвертировал вручную в hex и сравнил с дампом
Размеры пакетов - ровно как ожидается, посчитал побайтово в Wireshark
Паддинг - случайные байты на месте, правильной длины, перед payload
Junk-пакеты - отправляются, правильного количества и размера
Endianness параметров - перепроверил, что H1 записывается как uint32 LE, а не BE
Сетевая связность - пинг до сервера проходит, UDP-порт открыт
Firewall - правила не блокируют, tcpdump на сервере видит пакеты
Реализовал даже упрощенную проксю для обычного AmneziaWG client, вдруг я реализовал udp-proxy криво. Но нет, родная amneziawg завелась успешно, значит проблема была в реализации.
Добавлял всё более детальное логирование. Выводил каждый байт входящего и исходящего пакета в hex. Сравнивал с тем, что показывает tcpdump. Всё совпадало (почти). Байт в байт. Пакет выходил из прокси точно таким, каким я его ожидал. Структура правильная. Но сервер его игнорировал.
Я начал подозревать баг в самом AWG-сервере. Пробовал подключиться обычным AmneziaWG-клиентом - работает. Значит, сервер исправен. Проблема в моём прокси. Но где?
Изучаем исходники
От безысходности полез копать исходники WireGuard - конкретно noise-protocol.c и cookie.c. И нашёл это:
При получении Handshake Init: 1. Проверить размер пакета <- OK 2. Прочитать тип <- OK 3. Проверить MAC1 <- !!! 4. Если MAC1 невалиден - DROP <- молча, без логирования 5. Проверить MAC2 (если нужен) 6. Расшифровать static key 7. ...остальная обработка...
MAC1 проверяется ДО любой криптографической обработки пакета. Оказывается, это DoS-защита: проверка MAC1 дешёвая (один BLAKE2s-128), а расшифровка - дорогая. Если MAC1 невалиден, пакет отбрасывается немедленно. Без ответа. Без логирования. Молча. Это by design - нет смысла тратить ресурсы и раскрывать информацию о себе для пакетов с невалидным MAC.
MAC1 в Handshake Init - это:
mac1key = BLAKE2s-256("mac1" || server_public_key) MAC1 = BLAKE2s-128(mac1key, packet[0:116]) ^^^^^^^^^^^^^^ включая type в bytes [0:4] !
Что делает прокси? Заменяет type с 1 на H1. Четыре байта. После замены, хэш MAC1, который MikroTik вычислил по type=1, становится невалидным для сервера, который теперь видит type=H1 в тех же 4 байтах.
MikroTik: MAC1 = BLAKE2s-128(key, [01,00,00,00 | rest...]) - вычислил Прокси: type = 01,00,00,00 -> 38,89,89,3D (H1) - заменил Сервер: MAC1' = BLAKE2s-128(key, [38,89,89,3D | rest...]) - ожидает MAC1 != MAC1' - DROP
Вот и проблема. 3 дня!
И это работает в обе стороны. Когда сервер отправляет Handshake Response с type=H2, прокси заменяет на type=2 и MikroTik WG-стек отбрасывает ответ по той же причине: MAC1 в ответе был вычислен по type=H2, а MikroTik ожидает MAC1 по type=2. Даже если бы сервер каким-то чудом ответил на пакет с невалидным MAC1 (что невозможно, но допустим) - MikroTik бы не принял его ответ. Двусторонний deadlock. Нужно переделывать расчёт MAC1.
В общем, пришлось реализовывать подсчет и пересчет MAC1 самостоятельно. А хотел обойтись "простой udp-прокси". С привлечением LLM'ки написал вполне сносный модуль, проверил, и.... запустилось! Пошли байтики в Tx и Rx, родненькие! Фух, на компе работает, осталось запустить это в Mikrotik'е. В подсчете MAC1 активно участвует публичный ключ туннеля, поэтому нужно прокинуть +1 env в контейнер.
В итоге, даже с учетом добавления небольшой криптографии с пересчетом MAC1, бинарники получились всего 806-866 кб! Это 0.85 МБ. Гораздо меньше ожидаемого! Отлично!
Развёртывание на MikroTik
Контейнеры MikroTik: ограничения
MikroTik RouterOS 7.4+ поддерживает Docker-контейнеры. Но это не полноценный Docker - скорее, минимальная реализация OCI runtime с существенными ограничениями:
RAM: контейнеры разделяют память роутера (обычно 256-512 МБ на всё). Каждый мбайт, съеденный контейнером - это МБ, отнятый у RouterOS.
Диск: NAND или eMMC, типично 128-256 МБ, из них свободного и того меньше. Образ контейнера хранится на flash.
Нет привычных docker-команд: ни тебе docker exec, ни docker pull, логи не посмотреть, установка вручную проприетарными командами вроде /container/add
Через winbox тоже не покликаешь - лично у меня типичный баг добавления контейнера в UI: это сделать по сути невозможно, т.к. бесконечно ругается на поле Shm size.
После нескольких часов тестов и дебагов - комплекс всё же завелся. Настройки задаю через переменные окружения, создаю дополнительный виртуальный интерфейс для контейнера, настраиваю маршрутизацию.
В процессе настройки я накопил себе целую кучу скриптов. Со временем я понял, что уже сам путаюсь что откуда брать, и представил как могут мучаться все остальные (или я сам через год, когда попытаюсь поднять новое соединение - уже всё забуду и буду не понимать, кто такое написал). Так что решил что надо это дело оформить в виде конфигуратора: вставляем туда конфиг подключения AmneziaWG.conf, он его парсит и выдает:
Команды на полную установку с проверками совместимости
Создает скрипт удаления всего этого - вдруг у вас что-то пойдёт не так и захотите откатиться
Конфигуратор помогает только установить базовое соединение. Он не настраивает вам маршрутизацию - это каждый делает сам. Если что - обращайтесь к LLM'кам
Не забывайте создавать бэкапы перед выполнением команд!
В процессе эксплуатации выявил, что ЦПУ в основном потребляет Wireguard. Контейнер awg-proxy потребляет ЦПУ незначительно (1-2%). Бинарник хоть и весит 0.9 МБ, но потребление Ram колеблется в районе 7-10 МБ. Глобально, у меня 128 МБ, я могу себе это позволить)
Итоги
Ограничения рождают креативность. Когда совсем уже припекло, родилось хорошее решение переиспользовать. По итогу был реализован минималистичный AmneziaWG-container, неплохо (уверен, можно сделать ещё лучше) решающий мою задачу, делюсь с обществом (для ознакомления, см. disclaimer).
Github (MIT)
Комментарии (103)

Belkogoth
23.02.2026 22:04Оо, благодарствую, как раз стало актуально. Последнее время ощущение, что добрались и до самого стабильного SSTP - по этому протоколу соединение не рвется, аптайм долгий, но speedtest запускаю - с домашнего микрота на забугорный VPS под RouterOS TCP DL бывает валится до пары мегабит, и даже 720р ютуб начал подвисать, хотя еще год назад легко выдавало 18-20 мегабит даже на древнем MIPS. При этом прямой коннект дает 50-60 мегабит, то есть всю ширину тарифа.
Остальные протоколы давно неработоспособны: WG валится на первых 92 байт уже года 2 как, OpenVPN на UDP тоже не работает давно, на TCP скорость редко превышает 0.5 МБит.ikev2@ipsec не коннектится, L2tp@ipsec как повезет: с работы микрот вроде цепляется, дома то цепляется, то нет, а когда цепляется - начинает падать каждую минуту-две.
Короче, мрак))) Настал черед городить свою амнезию))

ruraic
23.02.2026 22:04Остальные протоколы давно неработоспособны
Попробуйте openconnect с камуфляжем - пока все отлично работает. В отличии от sstp он проходит проверку active probing.

Maxim_Q
23.02.2026 22:04openconnect с камуфляжем вы пробовали, тестировали на Микротике?
Контейнер вроде есть https://hub.docker.com/r/rajven/mikrotik-openconnect если получится протестировать скажите стабильно ли все работает? У меня пока Микротик на архитектуре mipsbe и там нет контейнеров.
ruraic
23.02.2026 22:04Именно на Микротике не пробовал, но сейчас ситуация такая, что часто приходится выбирать железо под ПО, а не наоборот. Поэтому дома пришлось перейти на OpenWRT, хотя конечно Микротик отличные железки делает

Belkogoth
23.02.2026 22:04Микротик отличную ось делает на самом деле. Мне нравится что у нее требования на уровне 2к винды, при этом на х86 железе она открывает массу возможностей. Там неплохой SMB сервер, который они обновили в 7 версии, плюс пакет ROSE добавляет немало возможностей жля дисковой подсистемы.
Контейнеры хорошенько допиливают, container application в 7.21+ сейчас позволяет вообще в 2 клика развернуть приложение из списка. Сейчас дома на itx мамке работает ROS с шарой, плюс торрентокачалка с раздачей. И все это довольно удобно управляется в winbox. Не жалею ни копейки, потраченной на лицензию L4)

Belkogoth
23.02.2026 22:04Вчера на работе поигрался и решил сделать проще. Вместо маркировки dst-траффика в mangle и отправки его маршрутом в sstp-туннель, попробовал новую функцию Socksify, она работает в NAT. Просто весь dst-траффик на список ресурсов (формируется динамически чеез добавление домена в статическую запись на fwd-правило вместе с субдоменами, что сильно экономит число dns запросов) отправляется в action=socksify, и далее через socks5. Скорость таки выросла заметно, с 1-5 мбит/с до тарифных 50)
Плюсом создание socks-прокси позволило еще и телегу на смартфоне заставить нормально работать) Тьфу 3 раза, пока вроде работает...

cucumslice
23.02.2026 22:04Остальные протоколы давно неработоспособны: WG валится на первых 92 байт уже года 2 как
А что я не так делаю, что WG года три уже стабильно работает на одном сервере? Только один месяц были проблемы на домру, а в остальном все гладко и сейчас интервал rekey стал стабильно 2 минуты, а раньше раз в 20 секунд было.

Belkogoth
23.02.2026 22:04Вероятно, у микротика совсем уж базовая реализация. А городить другой сервер не хочу, ибо RouterOS привычнее и эргономически куда удобнее других реализаций. Тем более что у нее и системные требования на уровне 2000 винды) Пока другие способы на нем работают - нет смысла менять. Сейчас пока перешел на socks прокси, полет нормальный.

Spyman
23.02.2026 22:04А где-то amneziaWg ещё работает? О.о
В трёх локациях где я бывал - уже года полтора как работает в лучшем случае xray + vless, и тот не всегда

0ka
23.02.2026 22:04С маленькими junk пакетами везде должно работать

valera_efremov
23.02.2026 22:04Подскажите, под что маскируетесь? Пробовал AWG2 маскировать под QUIC первый попавшийся пакет в Wireshark, не работает.

0ka
23.02.2026 22:04какой хостинг?

valera_efremov
23.02.2026 22:04hostvds, советую не брать )

0ka
23.02.2026 22:04На нём "динамическая блокировка", это которая на 10 минут после триггера, но у вас возможно постоянная. У вас обычный https веб сервер на 443 порте с доменом работает? (Динамик блок блочит любые SNI на моей сети, без SNI блока нет)

valera_efremov
23.02.2026 22:04Да. У меня там есть всё ещё живой WG. Но он может не подключаться, а через какое то время работает. И будет работать пока не отключишься. Если отключился, то повторное подключение - лотерея.

MyraJKee
23.02.2026 22:04Why? У меня там крутится уже больше года наверное vps с amnezia, вроде больших проблем пока что не было

Belkogoth
23.02.2026 22:04Я сам не люблю hostvds, они раньше недорогие были, но RouterOS поставить невозможно было, ибо там дисковая подсистема на virtio. А сейчас еще и цены сильно подняли. Месяц рылся по недорогим хостингам, в итоге наткнулся на rdp one dash, минимальная конфигурация обошлась в 4000 на 2 года аренды))) Что для забугорных vps прям копейки.
На данный момент там запущена RouterOS, sstp протокол самый надежный, аптайм стабильный и не рвется. Но чет подтупливать стало последнее время, потому решил попробовать на нем socks5. Сутки полет нормальный, ютуб, телега работают отлично) SSTP оставил скорее для управления.

SchwarzFuchs
23.02.2026 22:04Есть три версии амнезии. 1.0 заблокировали, но 1.5/2.0 с дополнительными опциями по имитации разрешённых запросов успешно держатся.

0ka
23.02.2026 22:04Где заблокировали 1.0? Не считая публичных впн сервисов

SchwarzFuchs
23.02.2026 22:04Сам протокол ловить научились, там при попытке подключения теперь то же самое, что и с обычным WG - 92 байта принимается, а дальше всё, тишина.

0ka
23.02.2026 22:04J параметры решают эту проблему, есть в первой версии

SchwarzFuchs
23.02.2026 22:04Лично в моём случае они мне никак не помогли ни в какой комбинации. Как и большинству людей, судя по обсуждениям в TG-канале генератора WARP-конфигов. Помогли только <I> параметры из 1.5.

0ka
23.02.2026 22:04Warp - публичный впн сервис который блокируется (скорее всего с белым списком протоколов на айпи адресе), а не ваша впска где ограничений меньше

SchwarzFuchs
23.02.2026 22:04Есть жалобы и на неработоспособность частных VPS в том числе

0ka
23.02.2026 22:04Неизвестна реальная причина, склоняюсь что забанили хостинги, а точнее поставили под белый список доменов, а через I параметр можно притворяться QUIC протоколом с разрешенным доменом

SchwarzFuchs
23.02.2026 22:04Какой бы ни была причина, это никак не влияет на изначальный тезис ветки — 1.0 устарела и в подавляющем большинстве случаев абсолютно бесполезна. И даже если какие-то VPS до сих пор на ней работают, то это, скорее всего, "временная недоработка".

0ka
23.02.2026 22:04нет, всё таки пока что в большинстве случаев работает, жалобы единичны и поэтому даже не получится проанализировать блокировку

itoolsy
23.02.2026 22:04Как раз зашел, чтобы увидеть этот коммент.
Билайн уже недели 2 как влесс режет на ура и ничего не помогает, а тут wg, который отключили года 2 назад. Он ходит только по белым спискам на частные сервера, одобренные ркн
s5384
23.02.2026 22:04Vless надо уметь настраивать. Ну и от хостинга многое зависит, многие зарубежные диапазоны шейпят

andrewilife
23.02.2026 22:04Зачем это на микротик если можно все сделать на самом дешевом кинетике?

AleGen
23.02.2026 22:04Наверное потому что микротик был в наличии.

andrewilife
23.02.2026 22:04извините что не уясновидел что у вас только микротик и нет денег на кинетик старт. влепите мне еще 10 дизов как вы это любите.

TimsTims Автор
23.02.2026 22:04извините что не уясновидел что у вас только микротик и нет денег на кинетик старт
Да не в этом дело. Кинетик (очень) сильно проигрывает по возможностям и функционалу микротика (при этом микротик настраивать намного сложнее). Это как менять ящик инструментов на школьную деревянную линейку. Деньги на деревянную линейку есть, но зачем?
Статья как-раз описывает, как этот ящик инструментов допрокачать недостающим инструментом. Вы предлагаете не заниматься прокачкой, а перейти на деревянную линейку.

TimsTims Автор
23.02.2026 22:04Зачем это на микротик если можно все сделать на самом дешевом кинетике?
Предлагаете выкинуть mikrotik и купить самый дешёвый keenetic, чтобы поднять туннель? Кроме кинетиков, awg также давно реализован для openwrt. На микротик нормальных очень маленьких реализаций (хотя бы в районе 8 МБ) я не встречал.

Belkogoth
23.02.2026 22:04Реализация имеется в виду серверная, или клиентская? На серверной-то размер не имеет значения, ибо VPS дает минимум 5 Гб диска на самых дрищенских конфигурациях
С другой стороны, если микрот с USB - флэшка с EXT4 спасает. Под ARM можно и контейнер запустить. Но флэшку, ессно, лучше взять на нормальном контроллере, и желательно на MLC чипе.

Barsuk
23.02.2026 22:04Прям на самом дешевом? У меня старт лежит в коробке новый, на нем тоже?

LbICbIY
23.02.2026 22:04Да, на старте отлично работает уже полтора года, 2200+ маршрутов.

Belkogoth
23.02.2026 22:04А нафига маршрутов-то столько?) Из пушки по воробьям. Тот же микрот позволяет одним dns-правилом отправлять весь домен с субдоменами в динамический address-лист вместо того, чтобы городить конский статический список из CIDR, и далее заворачивать весь dst-траффик на адреслист куда угодно и как угодно) Хоть манглом на впн туннель, хоть натом на socks прокси, которым сейчас активно и пользуюсь. Причем в такой реализации и мощности много не надо, стабильно работает даже на самом дешманском микроте с древним MIPS камнем и 64/32 ram/rom) Про современные арм вообше молчу.

mayorovp
23.02.2026 22:04А статический список адресов и не нужен, есть один проект который этот самый список адресов сформирует и раздаст любому роутеру по протоколу BGP.

dartraiden
23.02.2026 22:04Самые дешёвые кинетики не имеют USB-порта, без которого не развернуть** Entware даже во внутренней памяти***. А без Entware вы не получите AmneziaWG 1.5/2.0, т.к. встроенная реализация в кинетике ограничена 1.0 (и, судя по последнему ответу поддержки и закрытию темы на форуме, всё так и останется).
** так исторически сложилось, что компонент OPKG имеет в зависимостях модули для работы с USB-накопителями, а для роутеров без USB-порта эти модули компания не предоставляет
*** теоретически можно, но придётся пересобирать прошивку и в гробу я видел такие пляски с бубном

ritorichesky_echpochmak
23.02.2026 22:04С приземлёнными зондами? Ну и хочется уже и сеть пободрее, а то любые китайцы их на повороте обходят, и что-то типа Tailscale / NetBird, а не вот это всё. А то SSTP вроде на кинетике есть... но с ведроида фиг подключишься (при этом с винды работает)... openconnect вроде есть, но с винды фиг подключишься (android клиент при этом работает) - надоел рандомно неработающий зоопарк. И, похоже, ещё и с DNS начинается лютая чехарда

sirota
23.02.2026 22:04Может проглядел, а v2 амнезия поддерживается?

TimsTims Автор
23.02.2026 22:04Действительно, сфокусировался на v1. Постараюсь на днях дотянуть v1.5, v2.

TimsTims Автор
23.02.2026 22:04Добавил поддержку v2

Maxim_Q
23.02.2026 22:04Кем вы работаете если не секрет, что за 3 часа быстро сделали поддержку новой версии?

TimsTims Автор
23.02.2026 22:04Да уже буквально сразу после публикации стало ясно, что нужна будет v2, и в целом приступил к изучению. Плюс, никто не запрещает спрашивать LLM, они значительно упрощают погружение. Так-что не за 3 часа)
Но как видите ниже, коду нужна дальнейшая оптимизация.

Nuslagar
23.02.2026 22:04Зачем столько приседаний если есть это?

dartraiden
23.02.2026 22:04И ещё https://hub.docker.com/r/wiktorbgu/amneziawg-mikrotik
А также можно использовать обычный WireGuard, встроенный в RouterOS, подмешивая мусор самостоятельно

TimsTims Автор
23.02.2026 22:04Ссылка на первый контейнер уже есть в статье, я его упоминал: он слишком большой, мне не годится.
Обычный wireguard не запустится - недостаточно просто взять и отправить мусор, нужно ещё заменить параметр type с 1 на H1 (такого параметра ожидает amneziawg). При такой замене пакета нужно ещё пересчитать MAC1 (на скриптах в mikrotik такое дебажить, скажем так, не просто). С этим как раз бился 3 дня, пока искал причину.

0ka
23.02.2026 22:04По ссылке awg-go, т.е. userspace версия, а не из ядра линукс, нагрузка на проц в несколько раз выше

gwk
23.02.2026 22:04[Interface] Address и [Interface] DNS в конфигураторе не поддерживают IPv6

TimsTims Автор
23.02.2026 22:04Для ipv6 текущий контейнер не подойдет. Дело не столько в конфигуратора, а в корректной работе с IP-протоколом. Он поддерживает только IPv4. Поэтому даже если в конфигуратор добавить поддержку и команды ipv6 - это работать не будет.

haos
23.02.2026 22:04На RB5009 отлично взлетело. По сравнению с контейнерами amnezia-go, те что в userspace, нагрузка на CPU раза в полтора меньше.

TimsTims Автор
23.02.2026 22:04Спасибо! Как-раз хотел чтобы кто-нибудь проверил и сравнил с другими реализациями. Я у себя лишь сравнивал голый wireguard и wireguard через этот контейнер, и не заметил какой-то доп.нагрузки на ЦПУ.
Выходит, тонкий контейнер делает своё дело.
Вы кстати тестили на amneziawg v1 или v2?

haos
23.02.2026 22:04amneziawg v1 уже давно "замедленна" по крайней мере у провайдеров с которыми я работаю. Тестировал именно V2.

dreamer2
23.02.2026 22:04это сделать по сути невозможно, т.к. бесконечно ругается на поле Shm size
ошибка про shm только в старой версии винбокса, новый (4) уже умеет эти поля и приходится ради правки контейнеров ходить в него
будут ли чинить старый инфы нет

Mupok
23.02.2026 22:04Мои наблюдения: Vless+reality работает минуты 2 до 90мегабит, потом падает до 5.
Поставил амнезию на свою VPS пока 90Мегабит стабильно. Если поставить Sock5 через амнезию скорость опять же режется до 5 мегабит.
Надеюсь они сделают в программе встроенный прокси как в Некобоксе и других. Чтоб не весь трафиг гоняло, а только те программы где указан прокси с приложения Амнезия
Maxim_Q
23.02.2026 22:04Что у вас используется как сервер Socks5, какая программа? Для защиты авторизации чтобы скрыть логин/пароль что-то делали? Socks5 передает логин и пароль в открытом виде и их можно перехватить снифером.

gwk
23.02.2026 22:04а если нужно два интерфейса - запускать два контейнера и на уровне list=awg-proxy-env-01 и list=awg-proxy-env-02 развести?

wiktorbgu
23.02.2026 22:04Потестил на warp'е, через контейнер на ARM64 hAP AX3 жмет чуть более сотки, напрямую через микрот нативный wg жмет в полный канал 300 мегабит
По-ходу нужна оптимизация)
Использую простой конфиг с пробивным "мусором"S1 = 0 S2 = 0 Jc = 4 Jmin = 40 Jmax = 70 H1 = 1 H2 = 2 H3 = 3 H4 = 4 I1 = <b 0xc10000000114367096bb0fb3f58f3a3fb8aaacd61d63a1c8a40e14f7374b8a62dccba6431716c3abf6f5afbcfb39bd008000047c32e268567c652e6f4db58bff759bc8c5aaca183b87cb4d22938fe7d8dca22a679a79e4d9ee62e4bbb3a380dd78d4e8e48f26b38a1d42d76b371a5a9a0444827a69d1ab5872a85749f65a4104e931740b4dc1e2dd77733fc7fac4f93011cd622f2bb47e85f71992e2d585f8dc765a7a12ddeb879746a267393ad023d267c4bd79f258703e27345155268bd3cc0506ebd72e2e3c6b5b0f005299cd94b67ddabe30389c4f9b5c2d512dcc298c14f14e9b7f931e1dc397926c31fbb7cebfc668349c218672501031ecce151d4cb03c4c660b6c6fe7754e75446cd7de09a8c81030c5f6fb377203f551864f3d83e27de7b86499736cbbb549b2f37f436db1cae0a4ea39930f0534aacdd1e3534bc87877e2afabe959ced261f228d6362e6fd277c88c312d966c8b9f67e4a92e757773db0b0862fb8108d1d8fa262a40a1b4171961f0704c8ba314da2482ac8ed9bd28d4b50f7432d89fd800c25a50c5e2f5c0710544fef5273401116aa0572366d8e49ad758fcb29e6a92912e644dbe227c247cb3417eabfab2db16796b2fba420de3b1dc94e8361f1f324a331ddaf1e626553138860757fd0bf687566108b77b70fb9f8f8962eca599c4a70ed373666961a8cb506b96756d9e28b94122b20f16b54f118c0e603ce0b831efea614ad836df6cf9affbdd09596412547496967da758cec9080295d853b0861670b71d9abde0d562b1a6de82782a5b0c14d297f27283a895abc889a5f6703f0e6eb95f67b2da45f150d0d8ab805612d570c2d5cb6997ac3a7756226c2f5c8982ffbd480c5004b0660a3c9468945efde90864019a2b519458724b55d766e16b0da25c0557c01f3c11ddeb024b62e303640e17fdd57dedb3aeb4a2c1b7c93059f9c1d7118d77caac1cd0f6556e46cbc991c1bb16970273dea833d01e5090d061a0c6d25af2415cd2878af97f6d0e7f1f936247b394ecb9bd484da6be936dee9b0b92dc90101a1b4295e97a9772f2263eb09431995aa173df4ca2abd687d87706f0f93eaa5e13cbe3b574fa3cfe94502ace25265778da6960d561381769c24e0cbd7aac73c16f95ae74ff7ec38124f7c722b9cb151d4b6841343f29be8f35145e1b27021056820fed77003df8554b4155716c8cf6049ef5e318481460a8ce3be7c7bfac695255be84dc491c19e9dedc449dd3471728cd2a3ee51324ccb3eef121e3e08f8e18f0006ea8957371d9f2f739f0b89e4db11e5c6430ada61572e589519fbad4498b460ce6e4407fc2d8f2dd4293a50a0cb8fcaaf35cd9a8cc097e3603fbfa08d9036f52b3e7fcce11b83ad28a4ac12dba0395a0cc871cefd1a2856fffb3f28d82ce35cf80579974778bab13d9b3578d8c75a2d196087a2cd439aff2bb33f2db24ac175fff4ed91d36a4cdbfaf3f83074f03894ea40f17034629890da3efdbb41141b38368ab532209b69f057ddc559c19bc8ae62bf3fd564c9a35d9a83d14a95834a92bae6d9a29ae5e8ece07910d16433e4c6230c9bd7d68b47de0de9843988af6dc88b5301820443bd4d0537778bf6b4c1dd067fcf14b81015f2a67c7f2a28f9cb7e0684d3cb4b1c24d9b343122a086611b489532f1c3a26779da1706c6759d96d8ab>Какие есть предложения:
хочется именно для таких случаев пробивания чтобы программа понимала с каким портом src приходит запрос от микротика и чтобы с ним же шел запрос к серверу, для реализации следующего алгоритма:
если пир потух, вырубаем его, ставим рандомный порт у интерфейса wg (=0), в пире адрес сервера заменяем на адрес контейнера, врубаем пир - контейнер пробивается (ВАЖНО) с этим же src портом на реальный сервер,скрипт проверки чекает что пир ожил и меняет в нем адрес сервера напрямую с передергиванием пира, это даст максимальную производительность wg с пробивными плюшками)
- либо просто дать возможность передать src порт как необязательный параметр в переменную
и что еще важно нужно кикнуть существующий коннект от контейнера, т.е. либо сервис в контейнере сам перестает соединяться с сервером когда нет клиента в лице микротика, потому что он займет тот же порт, либо принудительно скриптом автоматизации останавливать контейнер, удалять коннект если он будет и уже потом запускать встроенный пир напрямую на сервер.
Примерно то же самое я хотел сделать на своем контейнере, но пока лень меня побеждает)) делаю это все вручную раз в неделю
В коде на Go не шарю, возможно не нужно вмешиваться в изменение трафика при дефолтных полях awg как у меня, по сути поле только одно, которое используется при первоначальном коннекте к серверу, но может быть это все таки тяжесть маршрутизации udp трафика через контейнер...

wiktorbgu
23.02.2026 22:04А может быть таки дело в mtu внутри контейнера... как его изменить? как минимум до 170 мегабит должно дожимать как эти же контейнеры с полной реализацией awg в user-space, а mihomo так максимум 200 мегабит прожимает на ax3)

TimsTims Автор
23.02.2026 22:04Спасибо. Связался, дожали вместе до 130-150мбит. Посмотрим, что ещё можно будет оптимизировать.

TimsTims Автор
23.02.2026 22:04upd: выжали 280мбит https://github.com/amneziawg-mikrotik/awg-proxy/issues/11

Ateela
23.02.2026 22:04Чем больше реализаций, тем круче. Осталось 2 варианта: полностью закрыть "внешку", тогда падают банки. Либо, оставить на этом уровне, кто умеет-обойдет.

romandocent
23.02.2026 22:04Помоему проще сразу ставить xray на ethware, чем эти все бесконкчные танцы с бубном с WG

haos
23.02.2026 22:04Раньше читали только заголовок статьи и писали коменты. Теперь уже даже это не делают...
Решение для Router OS aka Mikrotik.

amiton
23.02.2026 22:04А подскажите пожалуйста, это будет работать между двумя микротиками? То есть если идёт подключение не к серверу, а к другому микротику с этим поднятым docker контейнером? Просто с amneziawg раньше не сталкивался и не до конца понял этот момент.

Maxim_Q
23.02.2026 22:04Добавьте информацию в configurator.html для параноиков, что вся конфигурация и все данные обрабатываются локально в браузере и никуда ничего не передаются. Все приватные ключи в безопасности.

Maxim_Q
23.02.2026 22:04Я верно AWG Proxy работает как полноценный сервер ретранстялор? Он принимает обычный трафик WG расшифровывает его и потом снова зашифровывает его но как AWG и отпрапвляет его на другой сервер.

TimsTims Автор
23.02.2026 22:04Что-то вроде того. Только он не расшифровывает трафик WG (криптографии в нём там 0), а заменяет в нем несколько пакетов и пересчитывает контрольную сумму, и всё это уже становится вполне себе легитимным протоколом AWG.
И точно также в обратную сторону: то, что прилетело от AWG-сервера избавляется от лишней шелухи, остается только чистый WG-зашифрованный трафик, который дальше подается в wireguard-соединение на микротике.

doctorgasparyan
23.02.2026 22:04Данный контейнер еще не пробывал настраивать. Но, точно могу сказать , что с контейнером от @wiktorbgu, на железке RB5009UPr+S+IN, параллельно два замера скорости на https://www.speedtest.net/ вариабельность загрузки процессора - во время "Download" на 20-46%, во время Upload на 14-36%.

0ka
23.02.2026 22:04скорость то какая при этом?

doctorgasparyan
23.02.2026 22:04Скорость, провайдер мне по проводу отдает заявленные - 500 Mbps, система у меня построена из трех железок RB5009UPr+S+IN и двух cap - cAP ax (Wi-Fi 6 (802.11ax)) измерения по WiFi Download - 461.27 Mbps, Upload - 330.27 Mbps, Ping -49ms, по проводу не измерял.

imintsev
23.02.2026 22:04Круто, спасибо, воспользовался, работает
Только у 7.19 пришлось поправить синтаксис скрипта (на 7.21 нет возможности обновить CHR x86)
Maxim_Q
1) Сколько времени вы тестировали свою реализацию?
2) Нет ли утечки памяти при длительной работе Mikrotik без перезагрузки?
3) Как держит высокую нагрузку, ничего не падает, не глючит? Например если запустить паралельно два замера скорости на https://www.speedtest.net/ сколько пакажет скорость?
4) Держит ли стабильно но соединение без перезагрузки и отвалов при длительной работе? Перекликается с первым вопросом и зависит от него.
5) Кроме Mikrotik где-то еще пробовали запустить свой конвертер? Например на Raspberry Pi или VPS в России, куда в теории чистый WG должен дойти без блокировки.
TimsTims Автор
1) Около 1 недели
2) Утечек не замечал. Потребление памяти держится стабильно в районе 10 Мбайт. Утекать там в целом нечему, логи если и пишутся, то сразу в консоль в Ram, flash-память микротика не изнашивается (у них с этим беда)
3) Падений канала не замечал. На сервере с каналом 100мбит выдает стабильно 70-80мбит, в целом на практике это упирается в реализацию Wireguard (он показывал ровно столько-же), цпу при этом было 50% в обоих ситуациях.
4) В коде предусморен реконнект. Много раз проверял запуск-остановку wireguard при работающем container, как и остановку-перезагрузку container при работающем wireguard. Соединение восстанавливается успешно. Контейнер создается с опцией Start-on-boot, запускается при перезагрузке микротика.
5) Не пробовал. Это более тяжелые железки с более доступными ресурсами и немного другой направленности, там зачастую нет ограничений в размер контейнеров. Считаю, запускать на них отдельно wireguard и отдельно текущее решение избыточным. Гораздо выгоднее и проще на них запускать конкретную реализацию amneziawg client.
K0Jlya9
После того как программа написана и оттестирована на го у чатгпт есть очень большой шанс переписать ее на си ничего не испортив, бинарник может стать намного меньше.
А что это за модель микротика которая поддерживает докер но не может вытянуть 100мбит через ваиргард?
vvzvlad
Вы так говорите, как будто "поддерживать докер" — это надо какой-то мощный процессор.
kitsuned
У Микротиков весьма много железок на разновидностях MIPS (SMIPS/MMIPS/MIPS BE) — там поддержку контейнеров, увы, не завезли. И да, в роутерах весь упор на аппаратное ускорение, так что проц имеет право быть медленным и неспособным в быструю криптографию, которую хочет WireGuard.
K0Jlya9
Докер у них поддерживается только на новых армах, они должны тянуть.
8street
Не факт, что утечки связаны с контейнером автора, а не с софтом Микротика.
Тестил год назад контейнеры на hap ax2, так и не удалось добиться стабильности. Чаще просто запуск контейнера глючил, иногда случались утечки памяти, иногда контейнер просто падал, хз почему. Контейнеры пробовал разные.
Пришел к выводу, что на ax2 контейнеризацию никто не тестил и все запускают на ax3 и rb5009, хотя прошивка и архитектура проца одна и та же, вроде.