Files
mypointvpn/src/App/README.md
T

11 KiB
Raw Blame History

App

Django приложение связывающее все части сайта в одном месте. Никто не импортирует App, кроме MyPointVPN, а App может импортировать все.

-[x] Реализует кастомный менеджер модели и кастомную модель пользователя с отключенным email. -[x] Модель данных для поддержки интерфейса и функционала -[ ] Настройки домена и прокси у готовой точки -[x] Клиенты и статистика через API панели 3x-ui -[x] Супервизор долгих задач и ежедневная проверка точек

Зависимости: Telegram (интерфейс бота, пользователи, уведомления) и serverus (настройка серверов).

Модель данных

  • Point - точка доступа: сервер с панелью 3x-ui и необязательный сервер маскировки. Владелец - Telegram.User. Хранит домен, прокси на выходе и доступ к панели.
  • Server - сервер точки с ролью panel или mask. Хранит текущие порт ssh, пароль root, ключ и отпечаток ssh, а также пароль root, присланный пользователем при добавлении (default_root_password).
  • Key - ключ клиента на панели точки: имя (оно же email клиента в панели), uuid и sub_id. Клиенту выдается key.subscription_url, после переустановки ключ восстанавливается с теми же ссылками.

С маскировкой клиенты подключаются к серверу маскировки, он перенаправляет трафик на панель и скрывает ее адрес. Маскировка требует домен.

Работа с записями идет через менеджеры и методы моделей, а не через filter/create напрямую:

  • создание: Point.objects.create_point(owner, panel, mask=None, *, domain, outbound_proxy) создает точку вместе с серверами, имя и доступ к панели генерирует сама. Server.objects.create_server(point, role, credentials). Credentials - адрес и пароль root, которые прислал пользователь
  • поиск: Point.objects.owned_by(owner), дальше find_by_name, find_by_pk, find_last. У серверов find_by_address и is_address_taken, в том числе в пределах точки через point.servers
  • изменение: point.set_status(status, error), server.reset_fingerprint(). Статусы: installing, updating (меняется домен или прокси готовой точки), ready, error, releasing. Пока точка занята, с ней ничего не сделать. Неудачная смена домена или прокси оставляет точку ready с текстом ошибки: serverus применяет их только после выпуска сертификата и проверки прокси
  • ключи: services.issue_key(point, name), services.revoke_key(key), поиск point.keys.find_by_name(name)
  • удаление: point.delete(), серверы удаляются вместе с точкой

Соглашение для поиска: get_* и point.server(role) бросают DoesNotExist, если записи нет, а find_* возвращают None, и это видно по типу. point.panel всегда есть, потому что панель создается вместе с точкой, а point.mask может быть None.

Модули

  • services.py - сценарии поверх serverus: install_point, reinstall, release_point, у готовой точки set_domain (новый сертификат) и set_outbound_proxy. Ключи через API панели: issue_key, revoke_key, key_traffic, restore_keys.
  • fields.py - EncryptedTextField: секреты в базе зашифрованы Fernet ключами FIELD_ENCRYPTION_KEYS из .env.
  • management/commands/serve_ssh.py - ручной вход на сервер: make serve_ssh ip=<ip>.
  • tasks.py - задачи dramatiq: запускают сценарий и присылают результат в чат. Воркер: make dj_worker.
    • Долгая задача запускается через tasks.start(actor, point, status, chat, *args): точка получает статус и token задачи, вместе с задачей стартует supervise. Задача раз в 30 секунд отмечается в точке (task_heartbeat), супервизор раз в минуту проверяет отметку и, если задача молчит 3 минуты (или не начиналась час), снимает ее: статус по Point.INTERRUPTED, уведомление в чат. Кончилась задача - кончился и супервизор. Повторная доставка сообщения после падения воркера задачу не повторяет, а снимает.
    • check_point - ежедневная проверка точки (services.check_point): своя цепочка у каждой точки, запускается после установки и командой check_points (ее выполняет воркер в docker при старте). Сутки проходятся перескоками по 15 минут: RabbitMQ не держит неподтвержденное отложенное сообщение дольше consumer_timeout.
  • backups.py и команды backup, restore - бэкап базы pg_dump/pg_restore, generate_secrets - секреты для .env.
  • bot/interface.py - интерфейс бота MyPointInterface (выбирается у бота в админке).
  • bot/texts.py - кнопки и тексты.

Установка точки

  1. bootstrap обоих серверов (ключ, пароль root, обновление, ssh на случайном порту, nftables)
  2. панель: 3x-ui с логином, паролем, портом и путем, которые сгенерировал App, сертификат на домен или ip, подписка, подключение reality main, исходящий прокси точки
  3. маскировка: перенаправление трафика на адрес панели
  4. facts: uname -a для карточки сервера
  5. ключи точки восстанавливаются на панели (restore_keys), при первой установке их нет

"Переустановить" сбрасывает отпечаток ssh сервера и повторяет установку с новым паролем от провайдера или, по кнопке "Использовать старый", с паролем, присланным при добавлении сервера. Отпечаток можно сбросить и в админке.

TODO

  • для панели 3x-ui нужно активировать ssl, панель умеет сама создавать сертификаты для домена или ip адреса. Скрипт 3x-ui при ошибке молча оставлял панель на http и при повторной установке SSL не трогал. Теперь сертификат выпускает serverus и падает, если не вышло. На ip без публичного адреса сертификат не выпустить: ssl=no.
  • можно ли поставить домен после того как настроили сервер на ip? я имею ввиду поставить домен и выпустить ssl сертификат от letsencrypt. Да: services.set_domain(point, domain), в serverus - XuiPanel.issue_certificate(domain). В боте кнопки пока нет.
  • что нужно сделать чтобы сервис мог нормально работать и раздавать прокси интернет через cloudflare? когда домен за прокси клауда.
  • если используется и прокси сервер и домен за claudflare, то будет работать? Решение: за Cloudflare не работаем. Reality через проксирование Cloudflare невозможен: Cloudflare сам завершает TLS.
  • нужна функция установки прокси в исходящие (outbound) При установке ставится point.outbound_proxy, у готовой точки - services.set_outbound_proxy(point, proxy). Поддерживаются socks5, http(s) и cf_warp.
  • кнопки "Домен" и "Прокси" у готовой точки: задачи tasks.set_domain и tasks.set_outbound_proxy, прокси в брокер уходит зашифрованным

Секреты

Ключи и пароли серверов (private_key, root_password, default_root_password), доступ к панели (panel_password, panel_api_token), исходящий прокси и ключи клиентов (uuid, sub_id) хранятся зашифрованными (App.fields.EncryptedTextField, Fernet). Дамп базы без ключа не дает доступа к серверам. Искать по этим полям нельзя.

  • Ключ - FIELD_ENCRYPTION_KEYS в .env (настройки сами читают .env, как бы ни был запущен сайт, воркер или бот). Потеря ключа - потеря доступа ко всем серверам: храните его отдельно от бэкапов базы.
  • Смена ключа: новый ключ первым через запятую, старый следом, пересохранить записи, убрать старый.
  • Ручной вход на сервер: make serve_ssh ip=<ip> (или manage.py serve_ssh <ip> [команда]). Ключ и отпечаток берутся из базы, ключ живет во временной папке только на время сессии. В админке и боте приватный ключ не показывается.