# `modules/nodes` — Control Plane VPN-нод Оркестратор физических VPN-серверов: добавление новой ноды по SSH и раздача списка живых нод клиентам/лендингу. Единственный модуль без зависимостей от остальных. ## Роуты - `public_router` (за `auth_guard`, обычный юзер): - `GET /nodes` — список активных нод (`status = 'online'`), отсортирован по возрастанию `current_load` — то есть новые юзеры естественным образом идут на самые свободные сервера. - `GET /nodes/stats` — агрегаты для лендинга (страна, загрузка, статус, `last_seen`). - `GET /nodes/routing` — полный `TunnelConfig` для клиентского приложения: `ip/port`, `tunnel_address/prefix/dns/mtu`, `public_key`/`sni_domain` ноды и одноразовый (60с) `session_token` (HMAC-подписанный, `NodeService::generate_session_token`) — `null`, если у юзера нет активной подписки. - `admin_router` (за `auth_guard` **и** `admin_guard`, в таком порядке — `admin_guard` ожидает, что `auth_guard` уже положил юзера в extensions): - `GET /admin/nodes` — список **всех** нод независимо от статуса (в отличие от публичного `GET /nodes`, который отдаёт только `online`) — нужен админке, чтобы видеть ноды в `provisioning`/`offline`. - `POST /admin/nodes` — сразу вставляет ноду со статусом `provisioning` и отвечает `202 Accepted`; реальный SSH-провижининг идёт в фоне (`spawn_provisioning`). - `DELETE /admin/nodes/{id}` — удалить ноду из БД (саму VPS-машину это не трогает). - `POST /admin/nodes/{id}/restart` — перезапускает прокси-контейнер на ноде по SSH (уже по мастер-ключу, не по паролю — см. ниже). ## Провижининг (`service.rs`) `add_node` вставляет ноду в БД со статусом `provisioning` и сразу возвращает управление контроллеру (`202 Accepted`); сам SSH-провижининг (`provision_node_via_ssh`) выполняется в фоне (`tokio::spawn` + `spawn_blocking` для блокирующего `ssh2`), по завершении переводит ноду в `online` (успех) или `offline` (провал, с логом причины): 1. Подключается к `ip:22` с таймаутом 15с, логинится по паролю root (временный пароль, передаётся один раз при создании ноды). 2. Гоняет `templates/init_node.sh` (обязателен — если файла нет, провижининг падает с ошибкой, а не деплоит полуживую ноду) с переменными `NODE_IP`/`GH_TOKEN`. 3. Прописывает публичный ключ control plane (`keys/netrunner_master_ed25519.pub`, создаётся заранее, см. `main.rs::ensure_master_ssh_key`) в `authorized_keys` и **жёстко выключает парольный SSH-вход** (hardening-скрипт внутри метода) — после провижининга зайти на ноду можно только по ключу. Именно поэтому `restart_node` подключается уже по ключу (`connect_ssh_with_key`), а не по паролю, которого больше нет. 4. Не более 3 одновременных провижинингов (`Semaphore::new(3)`), чтобы не заDDoSить сам control plane при массовом добавлении нод. Health-check (`src/tasks.rs`, раз в `NODE_HEALTH_CHECK_INTERVAL` секунд, по умолчанию 60) TCP-пингует каждую не-`provisioning` ноду и переоценивает `online`/`offline` сам — control plane больше не может бесконечно долго считать мёртвую ноду живой.