## App Django приложение связывающее все части сайта в одном месте. Никто не импортирует App, кроме MyPointVPN, а App может импортировать все. -[x] Реализует кастомный менеджер модели и кастомную модель пользователя с отключенным email. -[x] Модель данных для поддержки интерфейса и функционала -[ ] Настройки домена и прокси у готовой точки -[x] Клиенты и статистика через API панели 3x-ui Зависимости: `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=`. - `tasks.py` - задачи dramatiq: запускают сценарий и присылают результат в чат. Воркер: `make dj_worker`. - `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 - [x] для панели 3x-ui нужно активировать ssl, панель умеет сама создавать сертификаты для домена или ip адреса. Скрипт 3x-ui при ошибке молча оставлял панель на http и при повторной установке SSL не трогал. Теперь сертификат выпускает serverus и падает, если не вышло. На ip без публичного адреса сертификат не выпустить: `ssl=no`. - [x] можно ли поставить домен после того как настроили сервер на ip? я имею ввиду поставить домен и выпустить ssl сертификат от letsencrypt. Да: `services.set_domain(point, domain)`, в serverus - `XuiPanel.issue_certificate(domain)`. В боте кнопки пока нет. - [x] что нужно сделать чтобы сервис мог нормально работать и раздавать прокси интернет через cloudflare? когда домен за прокси клауда. - [x] если используется и прокси сервер и домен за claudflare, то будет работать? Решение: за Cloudflare не работаем. Reality через проксирование Cloudflare невозможен: Cloudflare сам завершает TLS. - [x] нужна функция установки прокси в исходящие (outbound) При установке ставится `point.outbound_proxy`, у готовой точки - `services.set_outbound_proxy(point, proxy)`. Поддерживаются socks5, http(s) и cf_warp. - [x] кнопки "Домен" и "Прокси" у готовой точки: задачи `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=` (или `manage.py serve_ssh [команда]`). Ключ и отпечаток берутся из базы, ключ живет во временной папке только на время сессии. В админке и боте приватный ключ не показывается.