Некоторое время назад я тестировал свои наработки, которые изначально не имели к VPN никакого отношения. Мне нужен был реальный сценарий, в котором можно проверить развертывание приложений, передачу конфигурации и управление удаленными серверами. Таким сценарием стал AmneziaWG, а результатом эксперимента — awg-admin.
Предполагалось, что это будет небольшая вспомогательная панель. Инструмент использоваться для решения вполне реальных задач: настройки серверов и интерфейсов, добавления пользователей и построения туннелей. Со временем эксперимент перерос первоначальную задачу, и архитектура awg-admin потребовала переосмысления.
Во-первых, вся система была построена вокруг одного протокола. Во-вторых, туннель фактически воспринимался как связь двух серверов, тогда как на практике хотелось собирать цепочки произвольной длины. В-третьих, список серверов не давал целостного представления об инфраструктуре: где находятся узлы, как они связаны и по какому пути проходит трафик.
Так появился vpn-admin — уже не панель для AmneziaWG, а визуальная система управления VPN-инфраструктурой с поддержкой разных провайдеров, картой серверов и редактором сетевой топологии.

Что изменилось во второй версии
Переход от awg-admin к vpn-admin — это не переименование проекта и не замена нескольких форм. Изменилась основная абстракция.
В первой версии центральными объектами были сервер, AWG-интерфейс и пользователь. Во второй версии ядро работает с более общими понятиями:
-
сервер;
-
сетевой интерфейс или логический listener;
-
ресурс провайдера;
-
пользователь и его подключения;
-
направленная связь между интерфейсами;
-
VPN-сервис как приложение с конфигурацией.
Ядро vpn-admin не должно знать, как именно устроен Xray, Hysteria 2, AmneziaWG или провайдер, который появится завтра. Его задача — хранить инвентарь и желаемое состояние, показывать топологию, координировать установку и передавать провайдерам ресурсы и связи.
Специфика протокола вынесена за пределы админки.
В одном рабочем пространстве теперь находятся:
-
Map — географическая карта серверов и туннелей;
-
Network Graph — визуальный редактор интерфейсов и маршрутов;
-
Users — пользователи и их подключения на разных серверах;
-
Settings — источники дистрибутивов, резервное копирование и параметры приложения.
При этом vpn-admin по-прежнему можно использовать как desktop-приложение или запустить как standalone web server. Оба варианта используют одну модель данных, бизнес-логику и frontend.
Здесь важно различать два механизма расширения. Провайдер агента работает на управляемом сервере и владеет ресурсами конкретного протокола: интерфейсами, peers и links. Плагин развертывания вызывается процессом админки и управляет целым VPN-сервисом как связкой «приложение + конфигурация». Это разные контракты, которые дополняют друг друга. Ниже речь пойдет преимущественно о провайдерах агента.
Новый механизм развертывания
В awg-admin установка агента и настройка AWG были частью одного сценария. Для одного протокола это было удобно, но с добавлением Xray и Hysteria 2 такой подход перестал масштабироваться: зависимости и команды настройки каждого сервиса пришлось бы добавлять непосредственно в админку.
Во второй версии админка через pack доставляет дистрибутив на сервер и передает параметры установки, но не разбирается в устройстве конкретного VPN. У всех провайдеров одинаковая точка входа:
vpn-provider- deploy --config
Дальше провайдер развертывает себя самостоятельно: устанавливает нужный core и системные зависимости, подготавливает окружение и регистрируется в агенте. Содержимое конфигурации для админки остается непрозрачным — она не знает, например, что означает userspace у AWG или как Xray загружает свой core.
Постоянно работающим компонентом на сервере является vpn-agent. Он хранит желаемое состояние ресурсов, запускает провайдеры как дочерние процессы, следит за ними и предоставляет админке API. Поэтому systemd управляет только агентом, а отдельные unit-файлы для каждого VPN-сервиса не нужны.
Агент и провайдеры устанавливаются независимо. Можно сначала развернуть vpn-agent, затем добавить AWG, позже установить Xray и удалить Hysteria 2, не переустанавливая остальные компоненты. Источники дистрибутивов настраиваются в интерфейсе: поддерживаются публичные и приватные OCI registry, а также локальные файлы и каталоги.
Конфигурация без специальных форм для каждого протокола
Вторая важная часть архитектуры — динамические формы.
Каждый провайдер отдает manifest с описанием ресурсов и JSON Schema. Например, AWG предоставляет схемы Interface и Peer, Xray описывает параметры VLESS-listener, а Hysteria 2 — QUIC-listener и пользовательскую аутентификацию.
Админка получает эти схемы и строит формы через React JSON Schema Form. При создании интерфейса открывается мастер:
-
выбрать один из установленных провайдеров;
-
получить форму его ресурса;
-
заполнить конфигурацию;
-
отправить желаемое состояние агенту.
Ядро не интерпретирует поля этой конфигурации. Оно сохраняет ресурс и передает его владельцу. Провайдер выполняет собственную валидацию и возвращает состояние: Pending, Ready или Degraded, а также диагностические conditions.
Схемы кешируются локально в ~/.vpn-admin/cache. Поэтому формы доступны даже тогда, когда сервер временно недоступен. При следующем обращении к агенту кеш обновляется в фоне. Но проверка работоспособности сервера всегда использует живой агент, а не сохраненный manifest.
В результате новый провайдер не требует добавления отдельного экрана в админку. Он приносит с собой установщик, схемы, ресурсы, действия и логику применения.
AWG, Xray и Hysteria 2
На текущий момент реализованы три провайдера.
AmneziaWG / WireGuard
AWG-провайдер поддерживает:
-
kernel и userspace-режимы;
-
обфускацию AmneziaWG;
-
интерфейсы и пользовательские peers;
-
импорт существующих
wg-quickиawg-quick; -
генерацию клиентских
.conf; -
метрики трафика и handshake;
-
туннельные links и необходимые правила маршрутизации.
Существующий интерфейс можно сначала посмотреть в режиме preview, затем импортировать вместе с пользователями и ключами. Это позволяет перевести уже работающую инфраструктуру под управление v2, а не пересоздавать ее с нуля.
Xray
Xray-провайдер управляет VLESS через TCP или WebSocket, включая TLS. Каждый пользовательский peer получает собственный UUID. Для подключения формируется VLESS URI, который можно показать в виде QR-кода.
Hysteria 2
Hysteria 2 использует QUIC/UDP и аутентификацию по имени пользователя и паролю. Провайдер создает listener, управляет core-процессом и формирует hysteria2:// URI для клиентов.
Xray и Hysteria 2 создают не сетевые интерфейсы операционной системы, а логические listeners. Для визуального редактора разницы нет: и AWG-интерфейс, и proxy-listener представлены узлами, которые можно соединять маршрутами.
Пользователи при этом остаются общей сущностью админки. Один владелец может иметь подключения на разных серверах и через разные протоколы. Секреты генерируются и хранятся соответствующим провайдером, а в публичные resource specs попадают только идентификаторы.
Карта: инфраструктура перестает быть списком IP-адресов
Список серверов удобен для операций, но плохо отвечает на вопросы «где находится инфраструктура?» и «через какие регионы идет трафик?».
Поэтому в vpn-admin появилась географическая карта.
Для размещения сервера используется поле Location. По названию города vpn-admin ищет координаты в локальном кеше Data Center Atlas. Если города нет в наборе данных, Location можно оставить пустым и указать широту и долготу вручную. При установленном Location ручные координаты отключаются, чтобы не было двух конкурирующих источников.
Точка на карте показывает состояние сервера:
-
зеленая — агент доступен, ресурсы приняты и находятся в рабочем состоянии;
-
желтая — переходное, деградированное или еще не управляемое состояние;
-
красная — сервер или агент недоступны.
При наведении отображаются адрес, координаты, состояние агента и сервисы. Совпадающие точки объединяются в кластеры, которые можно раскрыть.
На карту выводятся и туннели. Цвет стрелки отражает p95 задержки за скользящее 15-минутное окно:
-
синий — p95 до 100 мс либо успешных ICMP-замеров пока нет;
-
желтый — от 100 до 250 мс;
-
красный — больше 250 мс.
Отсутствие замеров не подтверждает доступность туннеля: например, ICMP-запросы могут не проходить. Метрики загружаются асинхронно и не блокируют открытие топологии. Системные метрики и ICMP-замеры собирает агент, а метрики AWG-интерфейсов — соответствующий провайдер.
В результате карта является не декоративным экраном, а операционным представлением: видно географию, доступность узлов, маршруты и качество связей.
Многоузловые маршруты в Network Graph
В первой версии построение туннеля было сценарием «выбрать сервер A и сервер B». Для простой пары этого достаточно, но реальная маршрутизация быстро упирается в такую модель.
Network Graph — направленная связь между ресурсами. Интерфейсы соединяются стрелками, после чего админка и провайдеры настраивают соответствующие узлы.
Можно построить:
A → B → C → D → E
Один промежуточный интерфейс способен одновременно принимать предыдущий link и отправлять следующий. Для AWG каждый участок получает собственные параметры маршрутизации, отдельные таблицы и метки, производные от идентификатора link. Правила разных туннелей не собираются в одну глобальную конфигурацию.
Создание распределенного соединения проходит через координацию:
-
админка проверяет совместимость портов обоих ресурсов;
-
агенты выполняют
prepare; -
стороны обмениваются предложениями;
-
обе выполняют
commit; -
только после этого link получает состояние
Ready.
При ошибке соединение остается видимым в состоянии Degraded вместе с описанием проблемы. Удаление link убирает только созданные для него peers, hooks и маршруты. Пользовательские подключения сохраняются.
Существующий туннель можно перенести визуально: выделить стрелку, нажать m, перетащить начало или конец на другой интерфейс и подтвердить изменение. Админка сначала пытается создать новый маршрут, а затем удалить прежний, предусматривая очистку и откат при ошибках.
Длина цепочки не ограничена двумя узлами, а последовательность — одним протоколом. Направленные циклы при этом запрещены: без явной маршрутной политики они создают неоднозначность пути и риск зацикливания трафика. Для AWG-интерфейса и proxy-listener допускается один исходящий link.
Смешанные цепочки Xray, AWG и Hysteria 2
Поддержка трех провайдеров сама по себе еще не означает, что их можно объединить в один маршрут. Для этого понадобился отдельный механизм локальных route-связей.
Между серверами соединяются ресурсы одного протокола. Переключение протокола происходит внутри одного узла:

Целевой ресурс предоставляет connector. Источник использует его для отправки трафика:
-
proxy-протоколы подключаются через SOCKS5;
-
AWG использует TUN и отдельную таблицу policy routing.
Благодаря этому можно собрать хоть:
A(xray) → B(xray) → B(awg) → C(awg) → C(hysteria2) → D(hysteria2) → D(awg) → E(awg) → E(xray) → F(xray)
Для пользователя это по-прежнему визуальная цепочка стрелок. Детали запуска SOCKS5-connector, TUN, таблиц маршрутизации, NAT и firewall остаются внутри провайдеров.
Firewalld, UFW и iptables поддерживаются на уровне ресурсов: провайдер определяет активный механизм и управляет правилами своего listener или туннеля. Админка не пытается повторить эту логику самостоятельно.
Проверка на реальных цепочках
Смешанную маршрутизацию нельзя считать работающей только потому, что ресурсы получили состояние Ready. Поэтому в e2e-тестах цепочки создаются через тот же код админки, которым пользуется интерфейс, а затем проверяются реальным HTTP-трафиком.
Покрыты три сценария:
-
Xray-цепочка из пяти серверов.
-
Hysteria 2-цепочка из пяти серверов.
-
Смешанная цепочка из восьми серверов, четырнадцати endpoints и тринадцати links с несколькими переключениями между Xray, AWG и Hysteria 2.
Это позволило проверить не только создание конфигураций, но и полный путь пакетов и обратный HTTP-трафик через всю цепочку.
Итог
vpn-admin стал для меня продолжением того же эксперимента, с которого начинался awg-admin, но уже с более четкой границей ответственности. Добавление нового протокола превращается не в доработку всей системы, а в создание отдельного компонента со своим развертыванием, схемами, ресурсами и механизмом соединений.
А визуальный редактор превращает построение маршрута из последовательности ручных настроек на нескольких серверах в понятную и повторяемую операцию: разместить узлы, соединить их стрелками и увидеть получившийся путь на карте.
Проект доступен на GitHub под лицензией Apache 2.0. Готовые desktop-сборки, Docker-образ и инструкции по установке перечислены в руководстве.
Источник: habr.com