From 0333f33b29e2bff0073a48e0e54aa9311292e01e Mon Sep 17 00:00:00 2001 From: savsis Date: Thu, 10 Sep 2026 17:45:43 +0500 Subject: [PATCH] docs: readme, MIT license, CI workflow Co-Authored-By: Claude Sonnet 5 --- .github/workflows/ci.yml | 62 +++++++++++++++++++++++++ LICENSE | 21 +++++++++ README.md | 98 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 LICENSE create mode 100644 README.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7c007ca --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,62 @@ +name: CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Compile check all Python files + run: python -m compileall -q . + + - name: Shell syntax check + run: | + bash -n install.sh + bash -n mbs + + - name: Smoke test install-script rendering + env: + BOT_TOKEN: "x" + BOT_USERNAME: "x" + ADMIN_IDS: "1" + ADMIN_PANEL_PASSWORD: "x" + PANEL_DOMAIN: "panel.test" + SUB_DOMAIN: "sub.test" + SITE_DOMAIN: "test" + XRAY_PUBLIC_KEY: "x" + XRAY_SHORT_ID_TCP: "x" + XRAY_SHORT_ID_GRPC: "x" + XRAY_SHORT_ID_XHTTP: "x" + run: | + python - << 'PYEOF' + import json + import nodeprov + + transports = nodeprov.build_transports("fi2.example.com", 443, "www.microsoft.com", "PUBKEY", include_ws=True) + node = { + "provision_token": "TESTTOKEN", + "address": "fi2.example.com", + "sni": "www.microsoft.com", + "private_key": "PRIVKEY", + "transports_json": json.dumps(transports), + "hysteria_enabled": 1, + "hysteria_port": 443, + "hysteria_password": "hypass", + "hysteria_obfs_password": "obfspass", + } + script = nodeprov.render_install_script(node) + assert "PRIVKEY" in script + assert len(script) > 500 + print("node install script rendered OK,", len(script), "bytes") + PYEOF diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..504c799 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 savsis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc038f2 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# 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). +- **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-записи (см. таблицу ниже). + +```bash +git clone https://github.com/ImSavsis/mbs-panel.git && cd mbs-panel && sudo bash install.sh +``` + +Скрипт спросит домен панели, домен подписки, токен бота от [@BotFather](https://t.me/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) появится админ-меню. + +## 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 +bash <(curl -Ls https://panel.example.com/install/ТОКЕН.sh) +``` + +Вставляешь на чистый сервер (тот же Ubuntu 22/24 или Debian 11/12) — нода сама всё ставит и регистрируется. Если включал WS+TLS — скрипт выпустит для неё отдельный Let's Encrypt сертификат (нужна ещё одна A-запись, см. таблицу выше). Если включал Hysteria2 — поднимет отдельный процесс на UDP. + +## Разработка / вклад + +Код простой — без сборки фронта, без ORM, без лишних абстракций. `admin.html` — один файл, vanilla JS. Питон-часть — обычные функции, SQLite напрямую. + +PR и issues welcome. CI на каждый пуш гоняет compile-check по питону, синтаксис-проверку шелл-скриптов и smoke-тест генерации install-скрипта ноды. + +## Лицензия + +MIT — см. [LICENSE](LICENSE).