Initial commit: Rampart v0.2.0
Multi-layer DDoS protection for Minecraft servers. - rampart-core: Edge node with XDP/eBPF + Rust L7 filtering - rampart-manager: REST API with JWT auth, Redis sync - rampart-cli: CLI tool for operators - velocity-plugin: Domain check, HMAC verify, server registry, load balancer - paper-plugin: Auto-registration, heartbeat, HMAC verify - dashboard: React + Vite web UI for management
This commit is contained in:
commit
cf9608ce5d
159 changed files with 15341 additions and 0 deletions
69
crates/rampart-cli/src/commands/blacklist.rs
Normal file
69
crates/rampart-cli/src/commands/blacklist.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AddResponse {
|
||||
status: String,
|
||||
target: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct BlacklistItem {
|
||||
target: String,
|
||||
reason: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct BlacklistResponse {
|
||||
items: Vec<BlacklistItem>,
|
||||
total: usize,
|
||||
}
|
||||
|
||||
pub async fn add(target: String, reason: Option<String>) -> anyhow::Result<()> {
|
||||
let manager_url = std::env::var("MC_SHIELD_MANAGER").unwrap_or_else(|_| "http://localhost:8080".to_string());
|
||||
|
||||
let body = serde_json::json!({
|
||||
"target": target,
|
||||
"type": "ip",
|
||||
"reason": reason.unwrap_or_else(|| "manual".to_string()),
|
||||
});
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
match client
|
||||
.post(format!("{manager_url}/api/v1/blacklist"))
|
||||
.json(&body)
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(resp) => {
|
||||
if let Ok(add_resp) = resp.json::<AddResponse>().await {
|
||||
println!("[OK] {}: {}", add_resp.status, add_resp.target);
|
||||
}
|
||||
},
|
||||
Err(e) => println!("[FAIL] {e}"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove(target: String) -> anyhow::Result<()> {
|
||||
println!("Removing {target} from blacklist...");
|
||||
println!("(not implemented in v0.1)");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list() -> anyhow::Result<()> {
|
||||
let manager_url = std::env::var("MC_SHIELD_MANAGER").unwrap_or_else(|_| "http://localhost:8080".to_string());
|
||||
|
||||
match reqwest::get(format!("{manager_url}/api/v1/blacklist")).await {
|
||||
Ok(resp) => {
|
||||
if let Ok(list) = resp.json::<BlacklistResponse>().await {
|
||||
println!("Blacklist ({} entries)", list.total);
|
||||
println!("----------------------");
|
||||
for item in &list.items {
|
||||
println!(" {} ({})", item.target, item.reason);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => println!("[FAIL] {e}"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
24
crates/rampart-cli/src/commands/config.rs
Normal file
24
crates/rampart-cli/src/commands/config.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
pub async fn run(key: Option<String>, value: Option<String>) -> anyhow::Result<()> {
|
||||
match (key, value) {
|
||||
(Some(k), Some(v)) => {
|
||||
println!("Setting {k} = {v}");
|
||||
Ok(())
|
||||
},
|
||||
(Some(k), None) => {
|
||||
println!("Reading config key: {k}");
|
||||
println!("(not implemented in v0.1)");
|
||||
Ok(())
|
||||
},
|
||||
(None, Some(_)) | (None, None) => {
|
||||
println!("Configuration");
|
||||
println!("=============\n");
|
||||
println!("Use: rampart config <key> [value]");
|
||||
println!();
|
||||
println!("Example keys:");
|
||||
println!(" workers.count");
|
||||
println!(" limits.rate_limit_login_pps");
|
||||
println!(" limits.max_connections_per_ip");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
45
crates/rampart-cli/src/commands/doctor.rs
Normal file
45
crates/rampart-cli/src/commands/doctor.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
pub async fn run() -> anyhow::Result<()> {
|
||||
println!("Rampart Diagnostics");
|
||||
println!("=====================\n");
|
||||
|
||||
let mut all_ok = true;
|
||||
|
||||
let manager_url = std::env::var("RAMPART_MANAGER").unwrap_or_else(|_| "http://localhost:8080".to_string());
|
||||
|
||||
match reqwest::get(format!("{manager_url}/api/v1/health")).await {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
println!("[OK] Manager API");
|
||||
},
|
||||
_ => {
|
||||
println!("[FAIL] Manager API");
|
||||
all_ok = false;
|
||||
},
|
||||
}
|
||||
|
||||
match reqwest::get(format!("{manager_url}/api/v1/blacklist")).await {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
println!("[OK] Blacklist API");
|
||||
},
|
||||
_ => {
|
||||
println!("[WARN] Blacklist API unavailable");
|
||||
},
|
||||
}
|
||||
|
||||
match reqwest::get(format!("{manager_url}/api/v1/servers")).await {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
println!("[OK] Servers API");
|
||||
},
|
||||
_ => {
|
||||
println!("[WARN] Servers API unavailable");
|
||||
},
|
||||
}
|
||||
|
||||
println!();
|
||||
if all_ok {
|
||||
println!("All checks passed.");
|
||||
} else {
|
||||
println!("Some checks failed. Run with --verbose for details.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
7
crates/rampart-cli/src/commands/drain.rs
Normal file
7
crates/rampart-cli/src/commands/drain.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pub async fn run(node: &str) -> anyhow::Result<()> {
|
||||
println!("Draining node: {node}");
|
||||
println!("Waiting for active connections to drain...");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
println!("Node {node} drained successfully.");
|
||||
Ok(())
|
||||
}
|
||||
11
crates/rampart-cli/src/commands/emergency.rs
Normal file
11
crates/rampart-cli/src/commands/emergency.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pub async fn enable() -> anyhow::Result<()> {
|
||||
println!("Emergency mode ENABLED");
|
||||
println!("Only whitelisted IPs will be allowed through.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn disable() -> anyhow::Result<()> {
|
||||
println!("Emergency mode DISABLED");
|
||||
println!("Normal filtering resumed.");
|
||||
Ok(())
|
||||
}
|
||||
6
crates/rampart-cli/src/commands/mod.rs
Normal file
6
crates/rampart-cli/src/commands/mod.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pub mod blacklist;
|
||||
pub mod config;
|
||||
pub mod doctor;
|
||||
pub mod drain;
|
||||
pub mod emergency;
|
||||
pub mod status;
|
||||
23
crates/rampart-cli/src/commands/status.rs
Normal file
23
crates/rampart-cli/src/commands/status.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
pub async fn run() -> anyhow::Result<()> {
|
||||
println!("Rampart Status");
|
||||
println!("================\n");
|
||||
|
||||
let manager_url = std::env::var("RAMPART_MANAGER").unwrap_or_else(|_| "http://localhost:8080".to_string());
|
||||
|
||||
match reqwest::get(format!("{manager_url}/api/v1/health")).await {
|
||||
Ok(resp) => {
|
||||
if let Ok(body) = resp.json::<serde_json::Value>().await {
|
||||
println!(
|
||||
"Manager: {} (v{})",
|
||||
body["status"].as_str().unwrap_or("unknown"),
|
||||
body["version"].as_str().unwrap_or("?")
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(e) => println!("Manager: unreachable ({e})"),
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("To check individual components, run: rampart doctor");
|
||||
Ok(())
|
||||
}
|
||||
73
crates/rampart-cli/src/main.rs
Normal file
73
crates/rampart-cli/src/main.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
mod commands;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "rampart", about = "Rampart CLI")]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
/// Show overall system status
|
||||
Status,
|
||||
/// Run full diagnostics
|
||||
Doctor,
|
||||
/// Get/set configuration
|
||||
Config {
|
||||
#[arg(required = false)]
|
||||
key: Option<String>,
|
||||
#[arg(required = false)]
|
||||
value: Option<String>,
|
||||
},
|
||||
/// Manage blacklist
|
||||
Blacklist {
|
||||
#[command(subcommand)]
|
||||
action: BlacklistAction,
|
||||
},
|
||||
/// Emergency mode
|
||||
Emergency {
|
||||
#[arg(value_enum)]
|
||||
mode: EmergencyMode,
|
||||
},
|
||||
/// Gracefully drain a node
|
||||
Drain { node: String },
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum BlacklistAction {
|
||||
Add { target: String, reason: Option<String> },
|
||||
Remove { target: String },
|
||||
List,
|
||||
}
|
||||
|
||||
#[derive(clap::ValueEnum, Clone)]
|
||||
enum EmergencyMode {
|
||||
Enable,
|
||||
Disable,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
match cli.command {
|
||||
Commands::Status => commands::status::run().await,
|
||||
Commands::Doctor => commands::doctor::run().await,
|
||||
Commands::Config { key, value } => commands::config::run(key, value).await,
|
||||
Commands::Blacklist { action } => match action {
|
||||
BlacklistAction::Add { target, reason } => commands::blacklist::add(target, reason).await,
|
||||
BlacklistAction::Remove { target } => commands::blacklist::remove(target).await,
|
||||
BlacklistAction::List => commands::blacklist::list().await,
|
||||
},
|
||||
Commands::Emergency { mode } => match mode {
|
||||
EmergencyMode::Enable => commands::emergency::enable().await,
|
||||
EmergencyMode::Disable => commands::emergency::disable().await,
|
||||
},
|
||||
Commands::Drain { node } => commands::drain::run(&node).await,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue