Провижининг ноды: пробрасывает port/decoy_host/auth в SSH-команду запуска
init_node.sh раньше запускал образ вообще без CLI-аргументов — все провижинингованные ноды молча использовали дефолты и не требовали авторизации, несмотря на то что admin-панель как будто настраивала per-node SNI/порт. Заодно убирает захардкоженный IP control plane. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -86,11 +86,24 @@ impl NodeService {
|
||||
ssh_password: &str,
|
||||
) -> Result<VpnNode, AppError> {
|
||||
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<String>,
|
||||
) {
|
||||
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)?;
|
||||
|
||||
+17
-4
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user