Провижининг ноды: пробрасывает 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:
2026-07-09 16:35:46 +07:00
parent 875f679fc3
commit 379f2a26cf
2 changed files with 60 additions and 10 deletions
+43 -6
View File
@@ -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)?;