From 379f2a26cf53712c41a4dedd7e927a6b608069a2 Mon Sep 17 00:00:00 2001 From: nineap Date: Thu, 9 Jul 2026 16:35:46 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B2=D0=B8=D0=B6=D0=B8?= =?UTF-8?q?=D0=BD=D0=B8=D0=BD=D0=B3=20=D0=BD=D0=BE=D0=B4=D1=8B:=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B1=D1=80=D0=B0=D1=81=D1=8B=D0=B2=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=20port/decoy=5Fhost/auth=20=D0=B2=20SSH-=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B4=D1=83=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81?= =?UTF-8?q?=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit init_node.sh раньше запускал образ вообще без CLI-аргументов — все провижинингованные ноды молча использовали дефолты и не требовали авторизации, несмотря на то что admin-панель как будто настраивала per-node SNI/порт. Заодно убирает захардкоженный IP control plane. Co-Authored-By: Claude Sonnet 5 --- src/modules/nodes/service.rs | 49 +++++++++++++++++++++++++++++++----- templates/init_node.sh | 21 +++++++++++++--- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/modules/nodes/service.rs b/src/modules/nodes/service.rs index 052ac5e..a979438 100644 --- a/src/modules/nodes/service.rs +++ b/src/modules/nodes/service.rs @@ -86,11 +86,24 @@ impl NodeService { ssh_password: &str, ) -> Result { let node = self.repo.add_vpn_node_pending(payload).await?; - self.spawn_provisioning(node.id, node.ip_address.clone(), ssh_password.to_string()); + self.spawn_provisioning( + node.id, + node.ip_address.clone(), + ssh_password.to_string(), + node.port, + node.sni_domain.clone(), + ); Ok(node) } - fn spawn_provisioning(&self, node_id: Uuid, ip_address: String, password: String) { + fn spawn_provisioning( + &self, + node_id: Uuid, + ip_address: String, + password: String, + port: i32, + sni_domain: Option, + ) { let repo = self.repo.clone(); let semaphore = self.deploy_semaphore.clone(); @@ -101,7 +114,7 @@ impl NodeService { }; let provision_result = tokio::task::spawn_blocking(move || { - Self::provision_node_via_ssh(&ip_address, &password) + Self::provision_node_via_ssh(&ip_address, &password, port, sni_domain.as_deref()) }) .await; @@ -123,7 +136,12 @@ impl NodeService { }); } - fn provision_node_via_ssh(ip: &str, password: &str) -> Result<(), AppError> { + fn provision_node_via_ssh( + ip: &str, + password: &str, + port: i32, + sni_domain: Option<&str>, + ) -> Result<(), AppError> { use std::fs; // 1. Просто считываем уже гарантированно созданный на старте сервера ключ @@ -138,6 +156,25 @@ impl NodeService { let gh_token = std::env::var("GHCR_TOKEN").map_err(|_| { AppError::Internal("GHCR_TOKEN не задан — провижининг ноды невозможен".into()) })?; + // Секрет и публичный URL этого же бэкенда — нода должна ходить именно + // сюда для проверки токенов/лимитов (--require-auth), а не работать + // без авторизации, как было раньше при отсутствии этих переменных. + let proxy_internal_secret = std::env::var("PROXY_INTERNAL_SECRET").map_err(|_| { + AppError::Internal( + "PROXY_INTERNAL_SECRET не задан — провижининг ноды с --require-auth невозможен" + .into(), + ) + })?; + let backend_url = std::env::var("WEB_APP_BASE_URL").map_err(|_| { + AppError::Internal("WEB_APP_BASE_URL не задан — провижининг ноды невозможен".into()) + })?; + // Домен-декой конкретно этой ноды — если админ не указал при добавлении, + // используем тот же дефолт, что и сам netrunner-proxy без --decoy-host + // (netrunner_core::net::DEFAULT_DECOY_HOST в том, отдельном репозитории — + // здесь недоступен как зависимость, поэтому продублирован явно). + const DEFAULT_DECOY_HOST: &str = "www.debian.org"; + let sni_domain = sni_domain.unwrap_or(DEFAULT_DECOY_HOST); + // Если скрипт инициализации отсутствует — жёстко падаем, а не деплоим // полуживую ноду заглушкой `echo`. let init_script = fs::read_to_string("templates/init_node.sh").map_err(|e| { @@ -161,8 +198,8 @@ impl NodeService { ); let full_command = format!( - "NODE_IP='{}' GH_TOKEN='{}'\n{}\n{}", - ip, gh_token, init_script, harden_script + "NODE_IP='{}' GH_TOKEN='{}' NODE_PORT='{}' SNI_DOMAIN='{}' BACKEND_URL='{}' PROXY_INTERNAL_SECRET='{}'\n{}\n{}", + ip, gh_token, port, sni_domain, backend_url, proxy_internal_secret, init_script, harden_script ); Self::exec_ssh_command(&sess, &full_command)?; diff --git a/templates/init_node.sh b/templates/init_node.sh index 354f885..b5fe135 100644 --- a/templates/init_node.sh +++ b/templates/init_node.sh @@ -16,7 +16,15 @@ echo "$GH_TOKEN" | docker login ghcr.io -u nineap --password-stdin echo "[*] Step 4: Pulling the latest Netrunner image..." docker pull ghcr.io/nineap/netrunner-proxy:latest -echo "[*] Step 5: Starting Netrunner Proxy at port 443..." +echo "[*] Step 5: Starting Netrunner Proxy at port ${NODE_PORT}..." +# NODE_PORT/SNI_DOMAIN/BACKEND_URL/PROXY_INTERNAL_SECRET подставляет +# NodeService::provision_node_via_ssh (см. src/modules/nodes/service.rs) — +# конкретные для ЭТОЙ ноды значения, а не общий дефолт на все ноды разом. +# Раньше control-plane URL был захардкожен прямо тут на конкретный IP, а +# --decoy-host/--require-auth/--health-port вообще не передавались — нода +# всегда стартовала с дефолтным декоем (www.debian.org, одинаковым для всех +# нод сразу) и БЕЗ проверки токена/лимитов трафика, независимо от того, что +# админ вводил при добавлении ноды. docker run -d \ --name netrunner-proxy \ --restart always \ @@ -25,9 +33,14 @@ docker run -d \ --cap-add NET_RAW \ --cap-add DAC_OVERRIDE \ --device /dev/net/tun:/dev/net/tun \ - -e CONTROL_PLANE_URL=http://147.45.43.70:8080 \ - -e NODE_IP=$NODE_IP \ - ghcr.io/nineap/netrunner-proxy:latest + -e PROXY_INTERNAL_SECRET="$PROXY_INTERNAL_SECRET" \ + ghcr.io/nineap/netrunner-proxy:latest \ + ./netrunner-proxy \ + --port "$NODE_PORT" \ + --decoy-host "$SNI_DOMAIN" \ + --require-auth \ + --backend-url "$BACKEND_URL" \ + --health-port 9091 echo "[*] Step 6: Starting Watchtower for auto-updates..." docker run -d \