javascript

AWG-Easy 3: как с помощью вайб-кода сделал простую панель для AmneziaWG 3.x

  • пятница, 28 августа 2026 г. в 00:00:10
https://habr.com/ru/articles/1074986/

Привет, Хабр!

Я не программист и не разработчик. Мне ближе системное администрирование: развернуть сервис, разобраться с сетью, найти конфликт портов, проверить маршрутизацию и добиться, чтобы всё это пережило повторную установку. Но благодаря нейросетям у людей вроде меня появилась возможность не только описывать идеи, но и доводить их до работающего кода.

Так появился AWG-Easy 3 — небольшая Docker-панель для AmneziaWG 3.x с управлением клиентами, изоляцией Home/Guest, IPv6, диагностикой соединений и веб-интерфейсом, доступным только из самой VPN-сети.

Это история не о том, как я внезапно стал разработчиком. Я им не стал и на это звание не претендую. Это история о вайбкодинге форка: я формулировал требования, принимал продуктовые решения, готовил инфраструктуру и методично ломал результат проверками, а ИИ выступал архитектором, программистом, ревьюером и техническим напарником.

Возможно, мы изобрели велосипед. Но идея родилась самостоятельно и не из желания переписать существующее, а из конкретной потребности, для которой я не нашёл готового решения.

Откуда взялась идея

Уже существовал проект JohnnyVBut/awg-easy: понятная панель и простой способ установить AmneziaWG 2.x. Но с появлением нового поколения протокола мне захотелось получить такой же простой инструмент для AWG 3.x.

На момент начала работы нам не удалось найти открытую веб-панель, ориентированную именно на AmneziaWG 3.x. Требовалось не просто заменить номер версии в конфигурации. В AWG 3.x появились новые параметры защиты заголовков, padding и timings, а совместимость с предыдущим форматом отсутствует. Нужны были соответствующий движок, корректный экспорт профилей и проверка реальными клиентами.

Изначально задача выглядела довольно скромно:

  1. Взять понятную концепцию AWG-Easy.

  2. Заменить AWG 2.x на AWG 3.x.

  3. Сохранить установку через Docker и одну понятную команду.

Почти сразу список требований стал значительно интереснее.

Мне хотелось, чтобы панель не только выпускала конфиги для выхода в интернет, но и формировала небольшую внутреннюю сеть между доверенными устройствами. При этом знакомым, временным пользователям и другим людям можно было бы выдавать исключительно доступ в интернет — без доступа к этой сети и к администрированию.

Так появилась модель Home/Guest, похожая на домашнюю и гостевую Wi-Fi-сети.

Home и Guest вместо иерархии пользователей

Напротив каждого клиента в панели есть переключатель:

  • Home видит панель и других Home-клиентов;

  • Guest получает выход в интернет, но не видит панель, Home-сеть и других Guest-клиентов.

Мы намеренно не стали строить систему ролей, аккаунтов и разрешений. Панель должна оставаться Easy. Для входа используется один пароль, который можно сменить или сбросить как локальной командой на сервере, так и в самой панели.

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

При этом Guest-профиль остаётся обычным изолированным VPN-доступом. Во время испытаний мы отдельно проверили, что Guest-клиент не может открыть панель, а после удаления профиля установленное на устройстве соединение действительно перестаёт работать.

Политика реализована через отдельные наборы адресов и правила nftables. Панель не очищает чужие таблицы и не заменяет правила других сервисов. Если политика Docker требует дополнительных разрешений в DOCKER-USER, проект добавляет только узкие помеченные правила для собственного интерфейса awg0 и удаляет именно их при остановке.

Почему панель не торчит в публичном интернете

Отдельной задачей стала авторизация.

Публиковать административную панель на внешнем интерфейсе только затем, чтобы защищать её паролем и отдельно настраивать домен, HTTPS и сертификаты, мне не хотелось. На VPS уже может находиться другая инфраструктура, и новая панель не должна вмешиваться в неё.

В результате веб-интерфейс доступен по адресу http://10.8.0.1:51821 только внутри AWG-сети и только Home-клиентам. Публичного TCP-проброса у него нет. Пароль остаётся дополнительным уровнем проверки, но до формы входа сначала нужно попасть в доверенную часть VPN.

При первой установке автоматически создаётся профиль Home admin. Установщик показывает пароль панели и ссылку подключения. После импорта профиля администратор подключается к VPN, открывает внутренний адрес панели и уже там создаёт остальных клиентов.

Если ссылка потерялась, переустанавливать сервер не требуется:

sudo awg-easy-3 export-client "Home admin"

Если забыт пароль:

sudo awg-easy-3 reset-password

Последнего Home-клиента нельзя случайно превратить в Guest через API. Это небольшая, но важная защита от потери административного доступа.

Такой подход не отменяет обычную осторожность: доступ к VPS и профили Home всё равно нужно защищать. Но он позволяет не выставлять ещё одну административную поверхность в публичную сеть.

Протокол оказался только частью задачи

AWG-Easy 3 использует закреплённую версию userspace-движка AmneziaWG 3.1 и собирается для linux/amd64 — наиболее распространённой архитектуры недорогих VPS.

Поддерживаются новые параметры AWG 3.x, включая HeaderProtectionKey, настройки content padding и timings, а также появившиеся в 3.1 RandomTrailers и DisableCookies. При запуске приложение проверяет возможности установленного движка и не должно молча создавать частично применённую конфигурацию.

Но первая же полевая проверка показала: корректно поднятый интерфейс ещё не означает, что пользователь сможет подключиться.

Обычный QR-код клиент на Android проигнорировал. Нативный .conf импортировался, но часть параметров AWG 3.x не попадала во внутреннюю модель приложения, из-за чего защищённый handshake не работал как ожидалось. Рабочим основным форматом оказался vpn://, содержащий структуру, которую понимает AmneziaVPN.

Для этого пришлось сформировать объект конфигурации приложения, упаковать JSON совместимым с Qt способом и закодировать его в base64url. .conf мы сохранили как дополнительный формат для совместимых клиентов и диагностики, но основной путь подключения — ссылка vpn://.

Сейчас для созданных профилей требуется AmneziaVPN 5.0.0.5 или новее. Обычные WireGuard-клиенты и отдельное приложение AmneziaWG пока не поддерживают этот формат AWG 3.x. Проект не связан с AmneziaVPN и не одобрен его разработчиками — это просто клиент, с которым мы проверили совместимость.

IPv4, IPv6 и DNS

По умолчанию клиент получает full-tunnel для IPv4. Если на VPS обнаружены рабочий глобальный IPv6-адрес и маршрут по умолчанию, IPv6 включается автоматически — отдельный переключатель пользователю не нужен.

Не каждый провайдер делегирует VPN-клиентам маршрутизируемую подсеть, поэтому проект создаёт случайную ULA /64 и применяет NAT66 только к ней. Правила находятся в принадлежащей проекту таблице nftables и не должны затрагивать остальной IPv6-трафик сервера.

DNS по умолчанию:

94.140.14.14
94.140.15.15
2a10:50c0::ad1:ff
2a10:50c0::ad2:ff

IPv6 DNS добавляются только тогда, когда клиенту действительно выдаётся IPv6.

На тестовой VPS мы проверили одновременный выход через IPv4 и IPv6. Проверка также помогла отказаться от преждевременной реализации GeoIP-маршрутизации.

Идея была привлекательной: российские ресурсы направлять напрямую, остальной трафик — через VPN. Но сервер получает пакет уже после того, как клиент выбрал маршрут. Значит, реализация либо перекладывала бы большую таблицу маршрутов на каждый клиент, либо зависела бы от возможностей конкретного приложения. Это противоречило концепции лёгкой и управляемой сервером панели. Поэтому GeoIP отложен до появления аккуратного серверного дизайна, а не добавлен «для галочки».

Discovery без открытия портов

Для Home-клиентов хотелось получить не только соединение по известному IP, но и обнаружение сервисов: mDNS и SSDP, который часто отображается в интерфейсах как UPnP.

Здесь важно уточнение: AWG-Easy 3 не реализует UPnP IGD, NAT-PMP или PCP и не позволяет клиентам автоматически открывать порты. Речь исключительно об обнаружении сервисов внутри доверенной Home-сети.

WireGuard-подобный интерфейс — не Ethernet-мост. Мультикаст одного peer не начинает автоматически поступать всем остальным peer на том же awg0. Поэтому был добавлен небольшой ретранслятор с контролируемым fan-out только между Home-адресами для:

  • mDNS: 224.0.0.251:5353;

  • SSDP: 239.255.255.250:1900.

Для SSDP сервер также заменяет адрес в LOCATION на VPN-адрес устройства, иначе обнаруженный сервис может сообщить клиенту недоступный локальный адрес физической сети.

Серверную часть мы проверили захватом пакетов между двумя реальными peer: ретрансляция и подмена адреса работали. Однако результат в приложениях оказался неодинаковым. Windows-клиент видел DLNA-сервис, а VLC на Android продолжал искать сетевые ресурсы. Обратный эксперимент дал похожий результат: прямой доступ к веб-интерфейсу телефона по VPN работал, но discovery в приложении не появлялся.

Причина в том, что некоторые сочетания Android, VPN и приложений привязывают multicast discovery только к физическому интерфейсу. Мы решили не удалять корректно работающую серверную функцию из-за ограничений одного класса клиентов, но честно указали эту особенность в документации.

Панель как средство диагностики

После первых испытаний стало понятно, что одного списка клиентов мало. Для отладки хотелось видеть, существует ли handshake и идёт ли через профиль трафик прямо сейчас.

В результате карточка клиента показывает:

  • статус «в сети» или «нет связи»;

  • текущую скорость приёма и передачи;

  • время последнего handshake;

  • внешний endpoint клиента;

  • MTU;

  • Persistent Keepalive.

Накопительная статистика трафика намеренно не хранится: это диагностический экран, а не средство наблюдения за пользователями. Скорость вычисляется по разнице счётчиков между опросами и исчезает вместе с текущей сессией панели.

Интерфейс переведён на английский, русский, фарси, испанский и упрощённый китайский. Переключатель языка доступен и до входа, и внутри панели. Даже эта простая на вид функция потребовала полевых исправлений: браузер продолжал использовать старые ресурсы, а выбранный язык менялся только в списке. Решением стали корректное состояние интерфейса и версионирование статических файлов против кэша.

Когда браузер говорит «скопировано», но ничего не копирует

Ещё одна неожиданно показательная проблема возникла с кнопкой копирования vpn://.

Панель работает по HTTP внутри зашифрованного туннеля. Для нашей сетевой модели это нормально, но Clipboard API и Web Share API в современных браузерах часто доступны только в secure context — обычно через HTTPS или на localhost.

В одном браузере кнопка просто не срабатывала. После fallback появлялось зелёное сообщение «Скопировано!», хотя буфер обмена оставался пустым. Попытка заменить копирование системным меню «Поделиться» на Android и PC привела почти к тому же поведению.

Вместо имитации успеха мы оставили одну честную кнопку «Показать ссылку». Она открывает поле с выделенным текстом и понятной инструкцией. Не так магически, зато одинаково работает в браузерах и не сообщает об операции, которую браузер запретил.

Это хороший пример того, почему интерфейс нельзя считать готовым только потому, что обработчик кнопки написан правильно.

Установщик, который мы пытались напугать

Я хотел, чтобы AWG-Easy 3 можно было поставить на недорогую VPS одной командой и без специальных знаний о Docker Compose:

git clone https://github.com/alexsemenovru/awg-easy-3.git
cd awg-easy-3
sudo ./install.sh --host ПУБЛИЧНЫЙ_IP_ИЛИ_ДОМЕН --lang ru

Но «одна команда» не должна означать «работает только на идеально чистой Ubuntu».

Мы несколько раз полностью обнуляли тестовую VPS и ставили перед панелью другую инфраструктуру, включая Docker-сервисы и панели с собственными сетевыми правилами. Я намеренно не всегда рассказывал ИИ, что именно установлено: тест должен был показать, сможет ли установщик безопасно разобраться сам.

Перед изменениями он проверяет:

  • платформу и архитектуру;

  • /dev/net/tun;

  • наличие Docker Engine и Compose v2;

  • наличие iproute2 и nftables;

  • занятость UDP- и TCP-портов;

  • конфликт интерфейса awg0, подсети и принадлежащих проекту имён Docker/nftables.

Если Docker или другие зависимости отсутствуют, установщик определяет пакетный менеджер и ставит их из репозиториев системы. Поддержаны APT, DNF, YUM, Zypper, Pacman и APK.

Если порт по умолчанию занят, интерактивная установка предлагает ближайший свободный и позволяет ввести другой. Если занятый порт передан явно через параметр, установка завершается ошибкой — молча менять явно заданную конфигурацию опасно.

Чужие контейнеры, интерфейсы, маршруты и таблицы установщик автоматически не удаляет и не переименовывает. Принцип простой: «кто первый занял ресурс, того и тапки»; наша задача — объяснить конфликт, а не чинить его ценой чужой инфраструктуры.

FreeBSD, OpenBSD, NetBSD, macOS и WSL отклоняются до установки пакетов: текущая архитектура требует нативных Linux TUN, forwarding, nftables и Docker. Для NixOS автоматическое редактирование configuration.nix тоже было бы плохой идеей, поэтому установщик выводит декларативный модуль и команды, которые пользователь может проверить и применить самостоятельно.

Установка — только половина удобства

В какой-то момент выяснилось, что простой установщик ещё не делает проект действительно простым. Панель нужно так же понятно обновлять, диагностировать и удалять.

После установки доступна глобальная команда:

awg-easy-3 start
awg-easy-3 stop
awg-easy-3 restart
awg-easy-3 status
awg-easy-3 settings
awg-easy-3 logs
awg-easy-3 diagnose
awg-easy-3 update
awg-easy-3 reset-password
awg-easy-3 export-client "Home admin"
awg-easy-3 reinstall
awg-easy-3 uninstall

Путь, из которого пользователь запускает команду, не имеет значения. Это кажется очевидным после реализации, но ранняя версия пыталась выполнять проверку конфликтов до обнаружения уже установленного экземпляра. В итоге запуск второго клона сообщал, что awg0 занят, хотя он был занят самой AWG-Easy 3. Этот случай заставил поменять порядок preflight-проверок.

Удаление и чистая переустановка требуют явного подтверждения. Они удаляют клиентов, ключи, пароль и настройки проекта, но не Docker целиком, не чужие контейнеры и не текущие настройки forwarding хоста. После одной из переустановок status показал активную nftables policy при неактивном интерфейсе — обнаружился оставшийся runtime-объект. Очистку сделали точечной: удаляются только ресурсы, которыми владеет AWG-Easy 3.

Все эти сценарии проверялись не только командами, но и повторным подключением реальных устройств после установки, удаления клиента и переустановки.

Как выглядел наш процесс вайбкодинга

Я не писал техническое задание на сотню страниц. Диалог развивался естественно: я описывал желаемое поведение, задавал вопросы и по мере чтения ответов придумывал следующие проверки.

Распределение ролей получилось примерно таким:

Я:

  • сформулировал исходную идею и ограничения;

  • определял, какие функции нужны, а какие сделают панель слишком тяжёлой;

  • придумал модель Home/Guest и правила доступа к панели;

  • арендовал и многократно переустанавливал VPS;

  • проверял Android и Windows, браузеры, IPv4/IPv6, удаление профилей и discovery;

  • забрасывал скриншотами и описывал каждое странное поведение с точки зрения пользователя.

Нейросеть:

  • исследовала исходный проект и компоненты AWG 3.x;

  • проектировала изменения;

  • писала и перерабатывала код;

  • добавляла тесты;

  • анализировала логи, сетевые правила и захваты пакетов;

  • исправляла ошибки и поддерживала документацию на пяти языках. Я сам смог проверить только русский аглийский и испанский.

Мы не принимали зелёную надпись в интерфейсе за доказательство результата. Если кнопка говорила «скопировано», я проверял буфер. Если панель показывала IPv6 — открывал внешний тест. Если Home обещал внутреннюю сеть — запускал TorrServe на телефоне и открывал его с компьютера. Если Guest не должен видеть панель — пытался открыть её именно из Guest.

В репозитории сейчас 115 автоматических тестов. Но важнейшей частью процесса стали ручные проверки на настоящей VPS и настоящих клиентах: они нашли проблемы, которые модульный тест интерфейса или генератора конфигурации не заметил бы.

Такой подход не отменяет необходимости понимать риски. ИИ может уверенно реализовать неверное предположение, а пользователь без опыта разработки — не заметить архитектурную ошибку. В нашем случае роль сисадмина и настойчивое тестирование оказались не менее важны, чем генерация кода.

Что получилось в v0.1.0

Первый стабильный релиз включает:

  • закреплённый userspace-движок AmneziaWG 3.1;

  • Docker-образ для linux/amd64;

  • интерактивный установщик с установкой зависимостей и проверкой конфликтов;

  • безопасное сосуществование с другой инфраструктурой на VPS;

  • глобальную команду управления;

  • создание, отключение, удаление и повторный экспорт клиентов;

  • режимы Home и Guest;

  • панель, доступную только Home-клиентам через VPN;

  • текущий статус, скорость и диагностику peer;

  • IPv4 и автоматический IPv6 через ULA/NAT66;

  • заданные по умолчанию DNS-серверы AdGuard;

  • Home-only mDNS и SSDP discovery без управления портами;

  • интерфейс и README на пяти языках;

  • импорт через vpn:// и дополнительную выгрузку .conf.

Проект намеренно не содержит миграции с 2.x, backup/restore, сложных ролей, GeoIP-маршрутизации и legacy WireGuard backend. Сейчас поддерживается только чистая установка. Ограниченный объём — часть концепции, а не список забытых галочек.

Итог

В начале у меня была довольно простая мысль: раз существует удобная панель для предыдущего поколения AmneziaWG, почему бы не сделать такую же простую вещь для нового?

В процессе стало понятно, что настоящий продукт начинается после слов «интерфейс поднялся». Нужно безопасно жить рядом с чужими контейнерами, не ломать firewall, восстанавливать доступ, честно обрабатывать ограничения браузеров, проверять несовместимость клиентов и отличать серверную сетевую проблему от поведения Android-приложения.

AWG-Easy 3 уже не просто заменяет версию протокола в существующей панели. Он создаёт небольшую управляемую VPN-сеть: доверенные Home-устройства могут общаться между собой, Guest-клиенты получают только интернет, а административный интерфейс не публикуется наружу.

Я по-прежнему не называю себя разработчиком. Это вайбкод-форк, основанный на чужом открытом проекте, с существенной помощью ИИ и обязательной атрибуцией исходного автора. Но он устанавливается, работает, прошёл серию реальных испытаний и теперь опубликован открыто.

Репозиторий: github.com/alexsemenovru/awg-easy-3
Первый стабильный релиз: v0.1.0

Буду рад технической критике, проверкам на других VPS и клиентских системах, сообщениям об ошибках и pull request. Особенно интересен опыт с multicast discovery на разных ОС и приложениях.

А ещё мне интересно мнение сообщества: является ли такой процесс полноценной разработкой с новым распределением ролей — или пока лишь очень продвинутым способом формулировать техническое задание?