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:
@@ -87,6 +87,18 @@ enum NodeCommands {
|
||||
/// Node ID
|
||||
id: String,
|
||||
},
|
||||
/// List pending enrollment requests
|
||||
Pending,
|
||||
/// Approve enrollment request
|
||||
Approve {
|
||||
/// Node ID to approve
|
||||
id: String,
|
||||
},
|
||||
/// Reject enrollment request
|
||||
Reject {
|
||||
/// Node ID to reject
|
||||
id: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
@@ -328,6 +340,71 @@ async fn handle_node_command(state: Arc<AppState>, action: NodeCommands) -> Resu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeCommands::Pending => {
|
||||
use oncp::EnrollmentState;
|
||||
|
||||
let pending = state.enrollment.list_by_state(EnrollmentState::Pending)?;
|
||||
|
||||
println!("{}", style("Pending Enrollment Requests").green().bold());
|
||||
println!("{}", style("─").dim().to_string().repeat(80));
|
||||
|
||||
if pending.is_empty() {
|
||||
println!("{}", style("No pending requests").dim());
|
||||
} else {
|
||||
println!("{:<36} {:<15} {:<20} {:<8}",
|
||||
style("Node ID").bold(),
|
||||
style("Name").bold(),
|
||||
style("Address").bold(),
|
||||
style("Country").bold()
|
||||
);
|
||||
|
||||
for node in pending {
|
||||
println!("{:<36} {:<15} {:<20} {:<8}",
|
||||
node.node_id,
|
||||
node.name,
|
||||
node.address,
|
||||
node.country_code
|
||||
);
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("{}", style("Use 'node approve <id>' to approve").dim());
|
||||
}
|
||||
}
|
||||
|
||||
NodeCommands::Approve { id } => {
|
||||
let uuid = uuid::Uuid::parse_str(&id)?;
|
||||
|
||||
match state.enrollment.approve(&uuid) {
|
||||
Ok(node_psk) => {
|
||||
println!("{} Node approved", style("✓").green().bold());
|
||||
println!();
|
||||
println!(" Node ID: {}", style(uuid).yellow());
|
||||
println!(" Node PSK: {}", style(&node_psk).yellow().bold());
|
||||
println!();
|
||||
println!("{}", style("⚠ IMPORTANT: Save this PSK securely!").red().bold());
|
||||
println!("{}", style(" Send it to the node operator via secure channel").dim());
|
||||
println!("{}", style(" The node must use this PSK to connect").dim());
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{} Failed to approve: {}", style("✗").red().bold(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeCommands::Reject { id } => {
|
||||
let uuid = uuid::Uuid::parse_str(&id)?;
|
||||
|
||||
match state.enrollment.reject(&uuid) {
|
||||
Ok(()) => {
|
||||
println!("{} Node enrollment rejected", style("✓").green().bold());
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{} Failed to reject: {}", style("✗").red().bold(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user