Делюсь способом интеграции ядра XRay (V2Ray) в телеграм бота с использованием системы marzban

Новый протокол подключения
Ряд последних событий тонко намекает на то, что использование VPN в РФ с каждым днем будет только усложняться. К очередной блокировке нужно быть готовым заранее, поэтому добавим новый протокол VLESS на базе ядра XRay (V2Ray) в телеграм бота.
Полученный результат можно посмотреть в этом боте:
Немного теории
V2Ray, V2Fly, XRay (VMess, VLESS, XTLS)
XRay - это форк V2Fly, когда некоторые разработчики из-за ряда разногласий с остальным сообществом ушли из проекта V2Fly и продолжили развивать код параллельно под названием XRay, придумав ему слоган “Penetrates everything”, что очень недалеко от правды.
Формат конфигурационных файлов остался прежним, но при этом новая реализация считается более эффективной в плане производительности, а самое главное - разработчики добавили туда несколько очень крутых фич, направленных в том числе на снижение детектируемости подключений на DPI (например, с помощью выявления TLS-in-TLS), таких как XTLS, речь о которых пойдет ниже.V2Ray/XRay - это не протокол, а, можно сказать, фреймворк - разные протоколы с разными транспортами и расширениями под одной крышей в одном приложении. Идея простая: что клиент, что сервер - это один бинарник. В конфигурации задаются inbounds (обработчики входящих подключений) и outbound (обработчики исходящих подключений).
На клиенте inbound обычно будет работать как HTTP- или SOCKS-прокси сервер, принимая подключения от браузеров и других программ, а outbound будет настроен как клиент какого-нибудь прокси-протокола для подключения к удаленному серверу.
VLESS - это более новый протокол. В отличие от VMess он не предусматривает механизма шифрования (подразумевается, что шифрование должно производиться нижележащим транспортным протоколом, например TLS), а только проверку “свой/чужой” и паддинг данных (изменение размеров пакетов для затруднения детектирования паттернов траффика).
В протоколе исправлен ряд уязвимостей старого VMess, и он активно развивается - например, автор планирует добавить поддержку компрессии алгоритмом Zstd - не сколько для производительности, сколько для затруднения анализа “снаружи”. При этом, при установлении соединения (хендшейке) клиент и сервер обмениваются версией протокола и списком поддерживаемых фич, то есть при дальнейшем развитии должна сохраняться обратная совместимость. В общем и целом, на сегодняшний день это самый свежий и прогрессивный протокол.
Источник: https://habr.com/ru/articles/727868/
Интеграция
Далее я покажу на базовом примере, как можно организовать выдачу ключей для подключения через VLESS.
Допустим у вас есть телеграм бот, в котором вы генерируете ключи для ваших пользователей, ваш код выглядит примерно так.
Ниже очень упрощенный пример, если у вас уже есть бот - вы сможете адаптировать его под свою реализацию.
class MarzbanBackend: def __init__(self): self.session = requests.Session() self.headers = {"accept": "application/json"} self.base_url = S.MARZBAN_API_URL if not self.headers.get("Authorization"): self.authorize() def _get(self, path: str) -> dict: url = f"{self.base_url}/{path}" response = self.session.request("GET", url, verify=False, headers=self.headers) if response.status_code == 200: return response.json() def _post(self, path: str, data=None) -> dict: url = f"{self.base_url}/{path}" if not path == "api/admin/token": data = json.dumps(data) response = self.session.request("POST", url, verify=False, headers=self.headers, data=data) if response.status_code == 201 or response.status_code == 200: return response.json() def _put(self, path: str, data=None) -> dict: url = f"{self.base_url}/{path}" json_data = json.dumps(data) response = self.session.put(url, verify=False, headers=self.headers, data=json_data) if response.status_code == 200: logger.info(f"cmd xray PUT {path}, data: {data}") return response.json() else: logger.error(f"cmd xray PUT not 200 status_code! {path}, data: {data}") def authorize(self) -> None: data = { "username": S.MARZBAN_USER, "password": S.MARZBAN_PASSWORD } response = self._post("api/admin/token", data=data) token = response.get("access_token") self.headers["Authorization"] = f"Bearer {token}" def create_user(self, name: str) -> dict: data = { "username": name, "proxies": { "vless": { "flow": "xtls-rprx-vision", }, }, "inbounds": { "vless": ["VLESS TCP REALITY"], }, "data_limit": 15 * 1024 * 1024 * 1024, "data_limit_reset_strategy": "day", } response = self._post("api/user", data=data) return response def get_user(self, name: str) -> dict: response = self._get(f"api/user/{name}") user = response.get("username") status = response.get("status") logger.info(f"get user: {user}, status: {status}") return response def disable_user(self, name: str) -> dict: data = { "status": "disabled" } response = self._put(f"api/user/{name}", data=data) if response: logger.info(f"Disable xray user: {name} success, {response.get('username', 'unknown username')}") check = self.get_user(name) if check.get("status") != data.get("status"): logger.error(f"After disable user {name}, user is not disabled!") return response else: logger.warning(f"xray user {name} not found") return {} def enable_user(self, name: str) -> dict: data = { "status": "active" } response = self._put(f"api/user/{name}", data=data) if response: logger.info(f"Enable xray user: {name} success, {response.get('username', 'unknown username')}") return response else: logger.warning(f"xray user {name} not found") return {}
Хотелось бы обратить ваше внимание на функции create_user()
и disable_user()
, рассмотрим каждую из них подробнее.
В функцию create_user()
я не передаю параметр expire
, так как в боте используется несколько протоколов и уже существует логика управления пользователями через включение-выключение доступа (функции enable_user()
, disable_user()
).
Но если у вас новый бот и вы не используете другие протоколы - управление через expire
выглядит хорошим вариантом.
В функции disable_user()
реализована дополнительная логика на проверку отключения в следующем виде:
time.sleep(0.25) check = self.get_user(name) if check.get("status") != data.get("status"): logger.error(f"After disable user {name}, user is not disabled!")
В данный момент сталкиваюсь со случаями, в которых успешно меняется статус, но в панели Marzban пользователь остается со статусом “active”.
Пока этот вариант используется для отладки, если баг будет сохраняться - можно будет сделать рекурсивный вариант внутри функции.
После создания пользователя необходимо отдать ему ссылку на подписку, которая формируется функцией create_user()
, ключ: subscription_url
, если вы не планируете полностью использовать API Marzban для доступа к данным сервера - то результат (данные о пользователях) лучше сохранять в своё отдельно хранилище, например postgresql, с которой взаимодействует бот.
Список проверенных хостеров
- aeza - бонус 15% к пополнению баланса
- veesp
- 4vps - 20% к первому пополнению или скидка 20% на первый заказ
- vdsina - скидка 10%
- inferno-solutions + скидка 25% на первый платеж, промокод:
artydev
- pq-hosting
Итог
Результаты подключения протокола VLESS можете посмотреть в моём боте.
Cсылка: https://t.me/artydev_wg_payment_bot?start=xray
Также рекомендую к прочтению:
- Marzban - добавить подписку в 1 клик
- Marzban: миграция с sqlite3 на MySQL
- Speedtest сетевого канала сервера
На пиво
Если данный материал оказался вам полезен - готов принять ваши копеечки :)
в крипте:
USDT (BEP20): 0xcdc3231527a1ad105d527678ccbcf5e827747e7b
ETH (ERC20): 0xcdc3231527a1ad105d527678ccbcf5e827747e7b
TON: UQAiIMLC2_j9tPlmQakdbz2Zh0rkTHH7tK2RTcO3rYAkr8QV
в рублях: https://pay.cloudtips.ru/p/2a3d8e06
Большое спасибо всем за внимание! Если вам интересны подобные рассуждения - подписывайтесь на мой канал artydev & Co.