11 KiB
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- кнопки и тексты.
Установка точки
- bootstrap обоих серверов (ключ, пароль root, обновление, ssh на случайном порту, nftables)
- панель: 3x-ui с логином, паролем, портом и путем, которые сгенерировал App, сертификат на домен или ip, подписка, подключение reality
main, исходящий прокси точки - маскировка: перенаправление трафика на адрес панели
- facts:
uname -aдля карточки сервера - ключи точки восстанавливаются на панели (
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> [команда]). Ключ и отпечаток берутся из базы, ключ живет во временной папке только на время сессии. В админке и боте приватный ключ не показывается.