Без TELEGRAM_FILES_URL адрес скачивания был голым путем, а текст ошибки с путем (в нем токен бота) уходил в чат. Теперь сайт не стартует без TELEGRAM_FILES_URL при заданном TELEGRAM_API_URL, а в ошибке скачивания только тип исключения. env_example: 127.0.0.1 вместо localhost, чтобы не пробовать ::1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
My Point VPN
Intro
Предоставляет интерфейс чат бота в телеграмм посредством которого позволяет разворачивать 3x-ui на своем сервере и выпускать ключи.
Базово конфигурируется 3x-ui на создание vless подключений с tls+reality маскировкой трафика. Настраивается dns, подписка и базовая безопасность сервера. Пользователь может прикрутить прокси на выход или включить cf_warp прокси. По каждому серверу дает базовую статистику у ручки управления.
Если вам нужен приватный доступ в интернет, передайте сервер боту и он настроит вам его. Выглядит это так: пользователь заводит аккаунт хостинг провайдера, арендует сервер, ему выдают ip и пароль root, он передает их боту, оный настраивает сервер, теперь пользователь может выпускать ключи vless и подключаться к интернету безопасно и приватно.
- статистика сервера:
- сколько трафика пропустил
- сколько пользователей
- можно добавить пользователя
- статистика по пользователю:
- онлайн?
- сколько трафика пропустил
- можно удалить пользователя
Серверу можно настроить:
- прокси на выходе (cf или свои)
- домен (сертификат Let's Encrypt; без домена сертификат выпускается на ip)
За Cloudflare точки не работают: reality через проксирование Cloudflare невозможен, Cloudflare сам завершает TLS. Скрыть адрес панели от клиентов можно сервером маскировки.
Окружение
- Linux Mint 22
- Python 3.12.3
- PyCharm 2026.2.1
Интерфейс чата
Переход в главное меню должен быть реализован кнопкой в клавиатуре. /start и кнопка "главное меню" должны выполняться одним методом.
Сейчас у пользователя нет данных и это его первое сообщение, поэтому система показывает ему сообщение заглушку.
--->
/start
<---
привет. выбери действие
[ФАК][добавить сервер][помощь]
Пользователь нажимает на кнопку добавления сервера и переходит в меню добавления сервера.
Меню добавления сервера
--->
добавить сервер
<---
Сервер: -
Сервер маскировки: -
Домен: -
Прокси: -
[домен][прокси][маскировка]
[отмена][установить][сервер][помощь]
--->
сервер/маскировка
<---
отправь данные для подключения к серверу
`ip:root_password`
[отмена]
После получения данных выполняется процесс настройки сервера. Этот процесс занимает время, поэтому выполняется в фоне.
--->
127.0.0.1:toor
<---
Сервер: 127.0.0.1
Домен: -
Прокси: -
[домен][прокси][маскировка]
[отмена][установить][сервер][помощь]
--->
прокси
<---
Можно указать свои или включить cf_warp.
[cf_warp][отмена][удалить]
--->
http://127.0.0.1:9050
<---
Сервер: 127.0.0.1
Домен: -
Прокси: http://127.0.0.1:9050
[домен][прокси][маскировка]
[отмена][установить][сервер][помощь]
--->
установить
<---
Запускаю установку сервера. Отправлю уведомление когда все будет готово.
А по окончании генерируется уведомление.
--->
<---
сервер ip - проименован как `NAME`, готов к работе.
[меню сервера]
Меню
Пользователь опять отправляет /start это нормально, так он получает главное меню с перечислением его серверов.
--->
/start
<---
point-1a2b3c активен
[ФАК][добавить сервер][помощь]
[управление сервером]
Чтобы перейти к управлению сервером нужно вот такой квест пройти, надо все эти id в копируемый pre сунуть.
--->
управление сервером
<---
Пришлите server_name
[отмена]
Меню управления сервером
--->
меню сервера
<---
Сервер: NAME
Состояние: активен
Панель: 1.2.3.4
Маскировка: -
Ключи: 2
Домен: -
Прокси: -
[Вернуть][Переустановить]
[Домен][Прокси]
[Клиенты]
[главное меню]
Возврат сервера связан с отключением его от платформы. Доступы к серверу изымаются, ставится новый пароль рута, а данные остаются как есть. Просто отдаем сервер назад.
--->
Вернуть
<---
Сервер будет освобожден из-под моего контроля,
будет установлен случайный пароль рута,
мои доступы будут убраны, данные на сервере сохранены.
Будет отправлено уведомление с новым паролем. Продолжить?
[Да][Нет]
Возможно потребуется переустановка после сброса сервера. Бывает такое когда это нужно. Обычно после сброса сервера задается новый пароль.
--->
Переустановить
<---
Будет произведена повторная установка всех компонентов.
Вызывайте эту функцию, только после того, как в панеле управления
сервером в аккаунте хостинг провайдера вызовите переустановку сервера.
Если уверены, то пришлите новый пароль сервера.
[Отмена][Использовать старый]
Меню управления доменом. После установки выпускаются ссл сертификаты, а если домена нет, то сертификат выпускаем на ip.
--->
Домен
<---
Сейчас установлен: -
Доменное имя лучше подходит для маскировки вашей точки доступа.
[установить][отключить]
Прокси полезно ставить. Есть 2 варианта, когда пользователь покупает прокси и задает их тут, или когда активирует cf_warp.
--->
Прокси
<---
Сейчас установлено: -
Позволяют маскировать ip адрес вашей точки доступа от сайтов,
которые посещаются.
X <> MyPoint <> PROXY <- Web
Сайты видят ip прокси, а не ip вашей точки доступа.
[установить][отключить]
--->
установить
<---
пришли прокси в формате `protocol://username:password@host:port` или нажми кнопку.
[cloudflare warp][отмена]
Меню управления клиентами
Домен и прокси меняются на горячую, в фоне: точка на это время "настраивается", по окончании приходит уведомление. Новый прокси сначала проверяется, а сертификат на новый домен сначала выпускается. Если не вышло, точка остается активной с прежними настройками, а ошибка видна в карточке. У точки с маскировкой домен можно сменить, но не убрать. Смена домена меняет ссылки подписок: ключи нужно раздать заново. Сообщение с прокси бот удаляет из чата, пароль прокси в карточке скрыт.
--->
Клиенты
<---
Ключи point-1a2b3c
mom-phone ↓ 1.2 ГБ ↑ 80.0 МБ, в сети 26.09 14:05
laptop ↓ 0 Б ↑ 0 Б, не подключался
Пришли имя ключа, чтобы получить ссылку подписки.
[выпустить ключ][отозвать ключ]
[меню сервера]
--->
mom-phone
<---
Ключ mom-phone: ↓ 1.2 ГБ ↑ 80.0 МБ, в сети 26.09 14:05
Подписка:
https://1.2.3.4:12839/9g72PmSGAQ3V/<subId>
"выпустить ключ" просит имя (латиница, цифры, - и _), "отозвать ключ" - имя существующего. Клиенту выдается URL подписки, а не vless-ссылка: он переживает переустановку точки, ключи восстанавливаются с прежними uuid и subId. Ограничений на число ключей нет, выпускает их владелец точки.
Настройка сервера
Чтобы сервера были в безопасности их следует правильно настроить. От пользователя ожидается на вход девственный сервер с только что установленной операционной системой.
- Конфигурирование ssh
- Смена пароля рута
- Конфигурирование nftables
- Обновление операционной системы
- Установка 3x-ui
- Выпуск сертификата
- Настройка vless+tls+reality
- Настройка прокси
Функционал бота
Добавление сервера
Сервер добавляется после установки ОС. Для бота требуется, чтобы ОС была debian-12 или debian-13. Свежий сервер:
- открыт публично 22 порт ssh
- имеет ip
- рут может войти по ssh используя пароль
Алгоритм действий для нового сервера, чтобы включить его в работу.
- Заходим на сервер по паролю
- прописываем ему свой ключ
- меняем пароль, но сохраняем оба
- Выполняем обновление системы
apt update -y && apt dist-upgrade -y - Настраиваем ssh
- отключаем вход по паролю
- жестко фиксируем авторизацию только по ключам
- меняем порт
- сохраняем конфиг sshd, перезапускаем сервис, проверяем коннект
- Настраиваем nftables
Система общается с сервером только через авторизацию по ssh ключам. Когда пользовать захочет вернуть сервер себе, следует:
- Откатываем настройки ssh
- Сбросить nftables
- Удалить файл /root/.ssh/authorized_keys
- Отдать ip:password
После базовой настройки нового сервера можно его специализировать. Этим занимается пакет serverus, который умеет готовить специализированные сервера под определенный вид задач.
Пакет также занимается и настройкой серверов базово. И позволяет сделать из сервера прокси или панель 3x-ui. Для наших целей требуется оба и в интерфейсе добавления сервера есть пункт "Сервер маскировки" - это сервер из которого нужно сделать прозрачный прокси. В App заводим модель в которой объединяем эти 2 сервера в единую систему. Так как кнопка "Вернуть" вернет оба сервера и они связаны настройками маршрутизации.
Запуск
cp env_example .envи заполнить: база, RabbitMQ,FIELD_ENCRYPTION_KEYS,SERVERUS_CERTIFICATES,TELEGRAM_WEBHOOK_URL,ALLOWED_HOSTS.make sync.make dev- Postgres, RabbitMQ и свой Bot API сервер в docker, миграции, затем сайт (dj_run: админка и webhook ботов) и воркер (dj_worker: установка и возврат серверов) в одном терминале. Ctrl+C останавливает оба,make dev_stop- инфраструктуру. По отдельности:docker compose up -d postgres rabbitmq telegram telegram-files,make dj_migrate,make dj_run,make dj_worker.- При первом запуске -
make dj_superuser. - В админке добавить бота с токеном, выбрать интерфейс MyPoint и опубликовать.
Подробности: src/App/README.md (сценарии, секреты, ручной вход make serve_ssh), src/serverus/README.md (плейбуки, ручная проверка на сервере), src/Telegram/README.md (боты).
Локальный стенд: SERVERUS_CERTIFICATES=local, сертификаты выпускает свой CA из extra/serverus/ca, ca.pem нужно добавить в доверенные на машине с клиентом. Let's Encrypt на серых адресах не работает.
Модель данных
Пользователи - приходят из телеграма, у них есть id, который уникален и проверен, его можно использовать для авторегистрации. И следует обязательно запоминать, так как боту недоступна информация о подключенных к нему пользователях, только в момент когда пользователь взаимодействует с ботом.
- telegram user id - запоминаем id пользователя телеграм
Точка - наша выдуманная абстракция, чтобы объединить в группу (кластер) 2 сервера. Домен. Прокси (или аккаунт cf_warp).
- сервер 3x-ui - обязательно должен быть, ради него все и затевается
- сервер маскировки - если пользователь хочет особый режим, то он может добавить еще один сервер купленный в своей стране
- домен - если сервер маскировки указан, то домен обязательно нужно указать
- ssl сертификаты - если домен не указан, то сертификаты выпускаются на IP сервера 3x-ui, а сервера маскировки нет; а если домен есть то он привязывается к серверу маскировки и на него выпускаются ssl ключи, который хранятся на сервере 3x-ui, потому что сервер маскировки лишь прозрачно проксирует. Храним и домен, и ip, по ssh ходим только по ip
- ssh ключи - доступ к серверам, находящимся под нашей ведомостью, всегда осуществляется асимметричным способом (ключ-файл)
- прокси - можно задать свои и с авторизацией, а можно воспользоваться cf_warp прокси, которые можно регистрировать автоматически через x-ui
- ключи - клиенты панели: имя, uuid и subId, чтобы восстановить их после переустановки
Сервер - реальные сервера с ip и доступом к root. Настраиваем безопасное окружение по всем канонам.
Сервис - На серверах у нас живут сервисы. Поднимается или настраивается какой-то сервис, чтобы решить какую-то полезную задачу. И реализуют api для управления сервисом на сервере. Сейчас эту функцию выполняет Роль сервера, потому что подразумевается что сервис может быть 1.
Запуск в docker
Образ (Dockerfile) - сайт с webhook ботов на gunicorn и воркер dramatiq, настройки MyPointVPN.settings.docker: все из окружения, DEBUG=False, статика админки через whitenoise. nginx не нужен: webhook принимает сайт внутри сети compose, Bot API сервер ходит на него по имени сервиса (TELEGRAM_WEBHOOK_URL=http://web:8000), домен не нужен.
make dj_secrets- дописывает в.envнедостающие секреты (SECRET_KEY,FIELD_ENCRYPTION_KEYS, пароли базы и RabbitMQ), заданные не трогает.make docker_build- собирает wheels и образ.make docker_up- инфраструктура, миграции (сервисmigrate),webиworker. Адреса сервисов внутри сети compose задаются вdocker-compose.yamlповерх.env, так что один.envгодится и для разработки с хоста.- Админка только на
127.0.0.1:8000хоста, с другой машины - черезssh -L 8000:127.0.0.1:8000.manage.py createsuperuser:docker compose --profile app exec web python manage.py createsuperuser.
Бэкап: make dj_backup keep=14 (или docker compose --profile app exec web python manage.py backup --keep 14 по cron) - pg_dump в extra/backups, секреты в нем зашифрованы. Восстановление: остановить web и worker, make dj_restore file=extra/backups/<файл>.dump, команда проверит, что FIELD_ENCRYPTION_KEYS подходит. pg_dump нужен той же версии, что сервер (16): в образе он есть, на хосте - apt install postgresql-client-16. FIELD_ENCRYPTION_KEYS храните отдельно от бэкапов: без него база бесполезна, с ним бэкап - доступ ко всем серверам.
Надежность
- Долгие задачи (установка, переустановка, возврат, смена домена и прокси) идут с супервизором: он запускается вместе с задачей, просыпается раз в минуту, пока задача работает, и снимает ее, если она перестала отмечаться (упал или перезапущен воркер, вышел лимит времени). Точка получает понятный статус, пользователь - уведомление. Повторно доставленное после падения воркера сообщение задачу не повторяет.
- Раз в сутки каждая готовая точка проверяется: панель отвечает, Xray работает, сертификат подписки не истекает. О проблеме и о восстановлении владелец получает сообщение, текущая проблема видна в карточке ("Проверка: ...").
TODO
Проверено на локальном стенде (Debian 13, свой CA): установка точки из бота с маскировкой, DoT и запасной DoH, ключи и подписка, socks5 и WARP на выходе, отказ нерабочего прокси, переустановка с восстановлением ключей, возврат серверов, serve_ssh, шифрование секретов в Postgres, бэкап и восстановление через образ.
- Let's Encrypt на домен и на ip на сервере с публичным адресом, продление короткого сертификата по cron.
- Возврат серверов на настоящем сервере, Debian 12.
- Точка с маскировкой и настоящим доменом.
- Прогнать установку в docker целиком:
web+telegram+ webhook по имени сервиса. - Пароль root при переустановке уходит в задачу dramatiq открытым текстом (сообщения RabbitMQ лежат на диске): шифровать, как прокси.
Решено оставить как есть: маскировка - перенаправление портов nftables; статистика - только у ключей; ботом может пользоваться любой.