Некоторое время назад я тестировал свои наработки, которые изначально не имели к 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-<name> deploy --config <file>

Дальше провайдер развертывает себя самостоятельно: устанавливает нужный 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. При создании интерфейса открывается мастер:

  1. выбрать один из установленных провайдеров;

  2. получить форму его ресурса;

  3. заполнить конфигурацию;

  4. отправить желаемое состояние агенту.

Ядро не интерпретирует поля этой конфигурации. Оно сохраняет ресурс и передает его владельцу. Провайдер выполняет собственную валидацию и возвращает состояние: 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. Правила разных туннелей не собираются в одну глобальную конфигурацию.

Создание распределенного соединения проходит через координацию:

  1. админка проверяет совместимость портов обоих ресурсов;

  2. агенты выполняют prepare;

  3. стороны обмениваются предложениями;

  4. обе выполняют commit;

  5. только после этого 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-трафиком.

Покрыты три сценария:

  1. Xray-цепочка из пяти серверов.

  2. Hysteria 2-цепочка из пяти серверов.

  3. Смешанная цепочка из восьми серверов, четырнадцати endpoints и тринадцати links с несколькими переключениями между Xray, AWG и Hysteria 2.

Это позволило проверить не только создание конфигураций, но и полный путь пакетов и обратный HTTP-трафик через всю цепочку.

Итог

vpn-admin стал для меня продолжением того же эксперимента, с которого начинался awg-admin, но уже с более четкой границей ответственности. Добавление нового протокола превращается не в доработку всей системы, а в создание отдельного компонента со своим развертыванием, схемами, ресурсами и механизмом соединений.

А визуальный редактор превращает построение маршрута из последовательности ручных настроек на нескольких серверах в понятную и повторяемую операцию: разместить узлы, соединить их стрелками и увидеть получившийся путь на карте.

Проект доступен на GitHub под лицензией Apache 2.0. Готовые desktop-сборки, Docker-образ и инструкции по установке перечислены в руководстве.