feat: System DNS, Node Enrollment, and CDN Steering
- osds: Added system DNS forwarder on 127.0.0.1:53 - SystemDnsManager for Windows/Linux DNS configuration - Auto-restore original DNS on exit - *.ospab.internal routing to master node - Encrypted DNS forwarding through OSTP tunnel - oncp: Implemented node enrollment system - EnrollmentRegistry with state machine (Pending->Approved->Active) - SQLite-backed enrollment storage - Node PSK generation on approval - REST API endpoints for enrollment workflow - oncp-master: Added enrollment CLI commands - 'node pending' - List pending enrollment requests - 'node approve <id>' - Approve and generate PSK - 'node reject <id>' - Reject enrollment - ostp-server: Auto-registration on startup - Submits enrollment request to master node - Exits if PSK='AUTO' and awaits approval - Integrates with ONCP enrollment API - oncp API: Enhanced CDN steering - Best nodes by country_code with fallback - Steering metadata (matched, fallback status) - Load-based node selection
This commit is contained in:
@@ -21,3 +21,4 @@ hex.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
rand.workspace = true
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
|
||||
@@ -55,6 +55,13 @@ struct ConfigFile {
|
||||
psk: String,
|
||||
max_connections: Option<usize>,
|
||||
log_level: Option<String>,
|
||||
|
||||
// Node enrollment settings
|
||||
master_node_url: Option<String>,
|
||||
node_name: Option<String>,
|
||||
hardware_id: Option<String>,
|
||||
region: Option<String>,
|
||||
country_code: Option<String>,
|
||||
}
|
||||
|
||||
fn setup_logging(level: &str) {
|
||||
@@ -120,7 +127,7 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
// Load config from file if specified
|
||||
let (listen, psk, log_level) = if let Some(config_path) = cli.config {
|
||||
let (listen, psk, log_level, config_path_opt) = if let Some(config_path) = cli.config.clone() {
|
||||
let content = std::fs::read_to_string(&config_path)
|
||||
.with_context(|| format!("Failed to read config file: {:?}", config_path))?;
|
||||
let config: ConfigFile = serde_json::from_str(&content)
|
||||
@@ -131,7 +138,7 @@ async fn main() -> Result<()> {
|
||||
let psk = parse_psk(&config.psk)?;
|
||||
let level = config.log_level.unwrap_or_else(|| cli.log_level.clone());
|
||||
|
||||
(addr, psk, level)
|
||||
(addr, psk, level, Some(config_path))
|
||||
} else {
|
||||
// Use CLI args
|
||||
let psk_str = cli.psk.ok_or_else(|| {
|
||||
@@ -139,7 +146,7 @@ async fn main() -> Result<()> {
|
||||
})?;
|
||||
let psk = parse_psk(&psk_str)?;
|
||||
|
||||
(cli.listen, psk, cli.log_level)
|
||||
(cli.listen, psk, cli.log_level, None)
|
||||
};
|
||||
|
||||
setup_logging(&log_level);
|
||||
@@ -153,6 +160,47 @@ async fn main() -> Result<()> {
|
||||
tracing::info!(" PSK: {}...{}", &hex::encode(&psk[..4]), &hex::encode(&psk[28..]));
|
||||
tracing::info!("");
|
||||
|
||||
// Check if node enrollment is configured
|
||||
if let Some(config_path) = config_path_opt {
|
||||
let content = std::fs::read_to_string(&config_path)?;
|
||||
let config: ConfigFile = serde_json::from_str(&content)?;
|
||||
|
||||
if let Some(master_url) = config.master_node_url {
|
||||
if config.psk == "AUTO" {
|
||||
// Node needs to enroll - request PSK from master
|
||||
tracing::info!("Node not enrolled - requesting to join network...");
|
||||
|
||||
let enrollment_result = request_enrollment(
|
||||
&master_url,
|
||||
config.node_name.as_deref().unwrap_or("ostp-node"),
|
||||
&listen.to_string(),
|
||||
config.country_code.as_deref().unwrap_or("US"),
|
||||
config.hardware_id.as_deref().unwrap_or("unknown"),
|
||||
config.region.as_deref().unwrap_or("default"),
|
||||
).await;
|
||||
|
||||
match enrollment_result {
|
||||
Ok(node_id) => {
|
||||
tracing::info!("✓ Enrollment request submitted");
|
||||
tracing::info!(" Node ID: {}", node_id);
|
||||
tracing::info!("");
|
||||
tracing::info!("Waiting for master node approval...");
|
||||
tracing::info!("Contact the administrator to approve node: {}", node_id);
|
||||
tracing::info!("Once approved, update config with provided PSK");
|
||||
|
||||
// Exit - node must wait for approval
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to submit enrollment: {}", e);
|
||||
tracing::error!("Server will not start until enrolled");
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config = ServerConfig::new(listen, psk);
|
||||
let server = OstpServer::new(config);
|
||||
|
||||
@@ -161,3 +209,59 @@ async fn main() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Submit enrollment request to master node
|
||||
async fn request_enrollment(
|
||||
master_url: &str,
|
||||
name: &str,
|
||||
address: &str,
|
||||
country_code: &str,
|
||||
hardware_id: &str,
|
||||
region: &str,
|
||||
) -> Result<String> {
|
||||
#[derive(serde::Serialize)]
|
||||
struct EnrollmentRequest {
|
||||
name: String,
|
||||
address: String,
|
||||
country_code: String,
|
||||
hardware_id: String,
|
||||
region: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
struct EnrollmentResponse {
|
||||
node_id: String,
|
||||
state: String,
|
||||
message: String,
|
||||
}
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()?;
|
||||
|
||||
let request = EnrollmentRequest {
|
||||
name: name.to_string(),
|
||||
address: address.to_string(),
|
||||
country_code: country_code.to_string(),
|
||||
hardware_id: hardware_id.to_string(),
|
||||
region: region.to_string(),
|
||||
};
|
||||
|
||||
let url = format!("{}/api/v1/enrollment/request", master_url);
|
||||
let response = client
|
||||
.post(&url)
|
||||
.json(&request)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to connect to master node")?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let text = response.text().await.unwrap_or_default();
|
||||
anyhow::bail!("Enrollment request failed: {} - {}", status, text);
|
||||
}
|
||||
|
||||
let enrollment: EnrollmentResponse = response.json().await?;
|
||||
Ok(enrollment.node_id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user