Self-hosted VPN reseller panel — VLESS+Reality/gRPC/XHTTP/WS-TLS + Hysteria2, Telegram bot, admin panel, one-command node install
Find a file
2026-09-10 22:28:31 +05:00
.github/workflows ci: import api/bot at CI time, exercise payments+HWID db logic 2026-09-10 22:06:15 +05:00
site site: offer + privacy policy templates for YooKassa compliance, README payments section 2026-09-10 21:49:10 +05:00
systemd deploy: one-command installer, systemd units, mbs CLI 2026-09-10 17:45:43 +05:00
.env.example hwid: per-user device limit like Remnawave (x-hwid header, opt-in) 2026-09-10 22:06:15 +05:00
.gitignore core: env-driven config, sqlite schema, xray/node management, subscription links 2026-09-10 17:45:36 +05:00
admin.html design: staggered reveal animation on every table/list row across the panel 2026-09-10 22:19:34 +05:00
api.py admin: full user card — subscription history, manual grant, devices/HWID in one modal 2026-09-10 22:17:16 +05:00
bot.py payments: Platega + YooKassa integration, opt-in via PAYMENTS_ENABLED 2026-09-10 21:44:45 +05:00
config.py hwid: per-user device limit like Remnawave (x-hwid header, opt-in) 2026-09-10 22:06:15 +05:00
db.py perf: indices on hot query columns, WAL+NORMAL synchronous for write throughput 2026-09-10 22:24:52 +05:00
install.sh install: retry flaky network steps (apt/git/pip/certbot/xray), pipefail safety 2026-09-10 22:28:31 +05:00
LICENSE docs: readme, MIT license, CI workflow 2026-09-10 17:45:43 +05:00
links.py core: env-driven config, sqlite schema, xray/node management, subscription links 2026-09-10 17:45:36 +05:00
mbs deploy: one-command installer, systemd units, mbs CLI 2026-09-10 17:45:43 +05:00
nodeprov.py admin: reset traffic counter per subscription (xray api statsquery -reset) 2026-09-10 22:14:16 +05:00
payments.py payments: Platega + YooKassa integration, opt-in via PAYMENTS_ENABLED 2026-09-10 21:44:45 +05:00
README.md docs: update feature list with HWID limit, user card, traffic reset 2026-09-10 22:20:25 +05:00
requirements.txt core: env-driven config, sqlite schema, xray/node management, subscription links 2026-09-10 17:45:36 +05:00
xray_manager.py admin: reset traffic counter per subscription (xray api statsquery -reset) 2026-09-10 22:14:16 +05:00

MBS Panel

Самостоятельная VPN-панель на VLESS+Reality (+ gRPC/XHTTP/WS-TLS транспорты) и Hysteria2. Телеграм-бот для выдачи подписок, веб-сайт с личным кабинетом, и админ-панель для управления нодами, юзерами и трафиком — всё в одном репозитории, без сторонних панелей типа x-ui или Marzban под капотом.

Сделано by savsis. Изначально писалось под конкретный проект (шеринг VPN среди своих), но получилось достаточно универсально, чтобы выложить как есть.

Что внутри

  • Бот (aiogram 3) — выдача подписок по кнопкам, гифт-коды, привязка тарифов (7 дней / месяц / 3 месяца / полгода / год), автоматическое отключение по истечении подписки (не раз в полчаса, а раз в 90 секунд — важно, чтобы просрочка реально обрывала доступ, а не продолжала работать).
  • API + сайт (FastAPI) — страница подписки со ссылкой happ:// и QR-кодом, личный кабинет, JSON API для сайта.
  • Админ-панель (чистый HTML/CSS/JS, без фреймворков и сборки) — дашборд, полная карточка юзера (история подписок, ручная выдача, устройства), подписки, гифт-коды, ноды (полное редактирование, не только вкл/выкл), трафик по Stats API самого Xray со сбросом счётчика по клику.
  • Мультинодовость — добавляешь новую ноду в панели, получаешь одну команду bash <(curl ...), вставляешь на чистый сервер — нода сама ставит Xray, генерит ключи, регистрируется в панели. Как у Remnawave/3x-ui, только свой велосипед.
  • Протоколы на выбор при добавлении ноды: VLESS TCP+Reality, VLESS gRPC+Reality, VLESS XHTTP+Reality, VLESS WS+TLS (с реальным Let's Encrypt сертификатом), Hysteria2 (QUIC, отдельный процесс, obfs).
  • Лимит устройств (HWID) — как у Remnawave, опционально: ограничение числа устройств на подписку через x-hwid заголовок.
  • Приём оплаты — ЮKassa и Platega из коробки, опционально; без них бот просто бесплатно выдаёт по кнопке.
  • CLI mbs — управление панелью прямо с сервера: пароль, статус, рестарт, логи.

Архитектура

┌─────────────┐       ┌──────────────────────────────┐
│  Telegram    │──────▶│  bot.py (aiogram)             │
│  юзер        │       │  выдаёт токен подписки         │
└─────────────┘       └──────────────┬────────────────┘
                                       │ SQLite (db.py)
┌─────────────┐       ┌──────────────▼────────────────┐
│  браузер /   │──────▶│  api.py (FastAPI)              │
│  Happ клиент │       │  /sub/{token}, /admin/api/*    │
└─────────────┘       │  админка на / (admin.html)     │
                       └──────────────┬────────────────┘
                                       │ SSH (paramiko)
                       ┌──────────────▼────────────────┐
                       │  Xray на этой же ноде          │
                       │  + управляемые ноды по SSH     │
                       └─────────────────────────────────┘

Панель и первая VPN-нода живут на одном сервере. На 443 порту нельзя одновременно держать и настоящий HTTPS (для сайта/подписки), и замаскированный под HTTPS VLESS+Reality трафик — поэтому перед Xray стоит nginx stream модуль с ssl_preread, который смотрит SNI входящего TLS-соединения и роутит: домены панели/подписки идут на nginx-бэкенд, всё остальное (в том числе Reality-трафик с левым SNI) — на Xray.

Установка — одна команда

Нужен чистый сервер на Ubuntu 22.04/24.04 или Debian 11/12, root-доступ и три поднятых DNS A-записи (см. таблицу ниже).

git clone https://github.com/devsavsis/mbs-panel.git && cd mbs-panel && sudo bash install.sh

Скрипт спросит домен панели, домен подписки, токен бота от @BotFather и список Telegram ID админов — и дальше всё сам: ставит зависимости, Xray, nginx, выпускает сертификаты Let's Encrypt, генерирует Reality-ключи, поднимает systemd-сервисы, настраивает firewall (ufw) и fail2ban. В конце покажет пароль от админки и ссылку на панель.

DNS-записи

Подними их до запуска install.sh — иначе Let's Encrypt не сможет выпустить сертификаты.

Запись Тип Куда указывает Зачем
panel.example.com A IP сервера панели Админка (веб-интерфейс)
sub.example.com A IP сервера панели Страница подписки + личный кабинет
de1.example.com A IP сервера панели Адрес, на который подключаются VPN-клиенты (первая нода)

Для каждой дополнительной ноды (добавляется позже через панель) — ещё одна A-запись на IP той ноды, например de2.example.com, nl1.example.com и т.д. Панель сама подскажет, какую запись нужно создать, и выдаст готовую команду установки, когда жмёшь «Добавить ноду».

Если Cloudflare — держи эти записи DNS only (серое облако), не проксируй: и Reality-трафику, и Let's Encrypt нужен прямой доступ до сервера.

После установки

  • Зайди на https://panel.example.com, залогинься паролем из вывода скрипта.
  • Смени пароль в любой момент: mbs pass новый_пароль (без аргумента — сгенерит случайный).
  • В боте у себя (Telegram ID из ADMIN_IDS) появится админ-меню.

В конце установки install.sh шлёт один пинг на stats.api.savsis.xyz (только название ОС) — просто счётчик "сколько раз панель установили", никаких доменов/токенов/паролей туда не уходит, IP не сохраняется. Отключить: MBS_SKIP_STATS=1 sudo bash install.sh.

CLI mbs

Ставится автоматически в /usr/local/bin/mbs.

mbs pass [пароль]   сменить пароль админ-панели (без аргумента — случайный)
mbs status           статус bot / api / xray / nginx
mbs restart          перезапустить bot + api
mbs logs [bot|api|xray]   последние строки лога (по умолчанию api)
mbs domain            текущий домен панели

Добавление ноды

В панели: Ноды → Добавить ноду → выбираешь страну, протоколы (Reality-транспорты всегда включены, WS+TLS и Hysteria2 — опционально) → получаешь команду вида:

bash <(curl -Ls https://panel.example.com/install/ТОКЕН.sh)

Вставляешь на чистый сервер (тот же Ubuntu 22/24 или Debian 11/12) — нода сама всё ставит и регистрируется. Если включал WS+TLS — скрипт выпустит для неё отдельный Let's Encrypt сертификат (нужна ещё одна A-запись, см. таблицу выше). Если включал Hysteria2 — поднимет отдельный процесс на UDP.

Приём оплаты

По умолчанию бот выдаёт подписки бесплатно по кнопке — платежи выключены (PAYMENTS_ENABLED=false). Чтобы продавать доступ:

  1. Заведи аккаунт в ЮKassa и/или Platega — оба требуют реального ИП/самозанятости и проходят собственную проверку (реквизиты, сайт с офертой). Панель это не автоматизирует.
  2. В .env: PAYMENTS_ENABLED=true, цены на тарифы (PRICE_7D, PRICE_1M и т.д. в рублях), и креды провайдера(ов) — YOOKASSA_SHOP_ID/YOOKASSA_SECRET_KEY и/или PLATEGA_MERCHANT_ID/PLATEGA_SECRET, включив соответствующий *_ENABLED.
  3. В личном кабинете провайдера пропиши webhook на https://<PANEL_DOMAIN>/payments/webhook/yookassa и/или .../payments/webhook/platega.
  4. mbs restart — бот начнёт показывать способ оплаты вместо мгновенной выдачи, подписка активируется автоматически по вебхуку с уведомлением в Telegram.

Заполни реальными данными site/offer.html (публичная оферта) и site/privacy.html (политика конфиденциальности) перед подачей заявки в ЮKassa — они нужны для их проверки, шаблоны уже на сайте (/offer.html, /privacy.html), но с плейсхолдерами вместо твоих реквизитов.

Лимит устройств (HWID)

Как в Remnawave — ограничение, сколько разных устройств может использовать одну подписку. Работает не через сам VPN-протокол (Xray физически не видит "железо" клиента), а на уровне выдачи самой подписки: современные клиенты (Happ, v2rayTun и т.д.) при запросе /sub/{token} шлют заголовок x-hwid — уникальный ID устройства. Панель запоминает первые N увиденных hwid на юзера; при попытке добавить N+1-е устройство — отказ (404 + заголовок x-hwid-max-devices-reached).

Выключено по умолчанию (HWID_LIMIT_ENABLED=false) — клиенты, которые не шлют x-hwid, при включённом лимите вообще не получат подписку, так что включай только если знаешь, что твои пользователи сидят на приложениях с поддержкой этого заголовка. HWID_FALLBACK_LIMIT — лимит по умолчанию для всех, в админке (Подписки → кнопка «Устройства» у юзера) можно посмотреть/удалить привязанные устройства и задать индивидуальный лимит.

Разработка / вклад

Код простой — без сборки фронта, без ORM, без лишних абстракций. admin.html — один файл, vanilla JS. Питон-часть — обычные функции, SQLite напрямую.

PR и issues welcome. CI на каждый пуш гоняет compile-check по питону, синтаксис-проверку шелл-скриптов и smoke-тест генерации install-скрипта ноды.

Лицензия

MIT — см. LICENSE.