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:
loki5512344 2026-07-20 20:53:32 +02:00
commit cf9608ce5d
Signed by: boba
GPG key ID: 253067914055423B
159 changed files with 15341 additions and 0 deletions

View 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(())
}

View 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(())
},
}
}

View 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(())
}

View 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(())
}

View 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(())
}

View file

@ -0,0 +1,6 @@
pub mod blacklist;
pub mod config;
pub mod doctor;
pub mod drain;
pub mod emergency;
pub mod status;

View 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(())
}

View 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,
}
}