1
0

moving code from command server into cli

This commit is contained in:
midefos 2024-05-28 16:03:32 +02:00
parent 8a6021aa83
commit d53ca8a33a
8 changed files with 323 additions and 146 deletions

212
Cargo.lock generated
View File

@ -26,6 +26,26 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.3.0"
@ -67,9 +87,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.97"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
[[package]]
name = "cfg-if"
@ -78,19 +98,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossbeam-channel"
version = "0.5.12"
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "filetime"
@ -135,6 +170,24 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.9"
@ -200,9 +253,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.154"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "linemux"
@ -229,6 +282,7 @@ dependencies = [
"iptables",
"linemux",
"regex",
"structopt",
"tokio",
]
@ -240,9 +294,9 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
dependencies = [
"adler",
]
@ -293,7 +347,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"hermit-abi 0.3.9",
"libc",
]
@ -319,10 +373,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.82"
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
dependencies = [
"unicode-ident",
]
@ -408,16 +486,66 @@ dependencies = [
]
[[package]]
name = "syn"
version = "2.0.63"
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "tokio"
version = "1.37.0"
@ -443,7 +571,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.66",
]
[[package]]
@ -452,6 +580,30 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.5.0"
@ -468,6 +620,22 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.8"
@ -477,6 +645,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"

View File

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
structopt = "0.3.26"
iptables = "0.5.1"
linemux = "0.3.0"
regex = "1.10.4"

View File

@ -8,4 +8,4 @@ FROM ubuntu:latest
RUN apt update && apt upgrade -y && apt install iptables iptables-persistent -y
COPY --from=builder /target/release/martillo-maldito ./
CMD ["/martillo-maldito"]
CMD [ "/martillo-maldito", "ban-server", "-f", "/host_ssh/auth.log", "-s", "/host_iptables/rules.v4" ]

44
src/cli.rs Normal file
View File

@ -0,0 +1,44 @@
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "martillo-maldito", about = "A simple iptables ban server")]
pub enum Arguments {
#[structopt(about = "Initialize ban server")]
BanServer {
#[structopt(name = "Ssh auth log file", short = "f", long = "ssh-file")]
ssh_auth_log: PathBuf,
#[structopt(name = "Iptables save file", short = "s", long = "iptables-save")]
iptables_save: Option<PathBuf>,
},
#[structopt(about = "Ban port")]
BanPort {
#[structopt(name = "Port to ban", short = "p", long = "port")]
port: u16,
},
#[structopt(about = "Unban port")]
UnbanPort {
#[structopt(name = "Port to unban", short = "p", long = "port")]
port: u16,
},
#[structopt(about = "Allow ip and port")]
AllowIpPort {
#[structopt(name = "Ip to allow", short = "i", long = "ip")]
ip: String,
#[structopt(name = "Port to allow", short = "p", long = "port")]
port: u16,
},
#[structopt(about = "Remove ip and port")]
RemoveIpPort {
#[structopt(name = "Ip to remove", short = "i", long = "ip")]
ip: String,
#[structopt(name = "Port to remove", short = "p", long = "port")]
port: u16,
},
}

View File

@ -1,7 +1,7 @@
use std::process::Command;
use std::{path::Path, process::Command};
pub fn save_iptables() {
pub fn save_iptables(path: &Path) {
let _ = Command::new("iptables-save")
.args(&["-f", "/host_iptables/rules.v4"])
.args(["-f", path.to_str().unwrap()])
.output();
}

41
src/iptables_wrapper.rs Normal file
View File

@ -0,0 +1,41 @@
pub fn ban_port(port: u16) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.append_unique(
"filter",
"INPUT",
&format!("-p tcp --dport {} -j DROP", port),
);
println!("banned port {}", port);
}
pub fn unban_port(port: u16) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.delete(
"filter",
"INPUT",
&format!("-p tcp --dport {} -j DROP", port),
);
println!("unbanned port {}", port);
}
pub fn allow_ip_port(ip: &str, port: u16) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.append_unique(
"filter",
"INPUT",
&format!("-p tcp --dport {} -s {} -j ACCEPT", port, ip),
);
println!("allowed {} to access {}", ip, port);
}
pub fn remove_ip_port(ip: &str, port: u16) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.delete(
"filter",
"INPUT",
&format!("-p tcp --dport {} -s {} -j ACCEPT", port, ip),
);
println!("removed access {} to {}", ip, port);
}

View File

@ -1,37 +1,57 @@
pub mod cli;
pub mod iptables_save;
pub mod iptables_wrapper;
pub mod login_attempt;
pub mod tpc_command_server;
use crate::tpc_command_server::start_tcp_command_server;
use cli::Arguments;
use iptables_wrapper::{allow_ip_port, ban_port, remove_ip_port, unban_port};
use linemux::MuxedLines;
use login_attempt::LoginAttempt;
use std::path::PathBuf;
use std::thread;
use std::{collections::HashMap, thread::sleep, time::Duration};
use structopt::StructOpt;
#[tokio::main]
async fn main() -> std::io::Result<()> {
async fn main() {
let opts = Arguments::from_args();
match opts {
Arguments::BanServer {
ssh_auth_log,
iptables_save,
} => {
let _ = start_ban_server(ssh_auth_log, iptables_save).await;
}
Arguments::BanPort { port } => ban_port(port),
Arguments::UnbanPort { port } => unban_port(port),
Arguments::AllowIpPort { ip, port } => allow_ip_port(&ip, port),
Arguments::RemoveIpPort { ip, port } => remove_ip_port(&ip, port),
}
}
async fn start_ban_server(
ssh_auth_log: PathBuf,
iptables_save: Option<PathBuf>,
) -> std::io::Result<()> {
let iptables = iptables::new(false).unwrap();
let mut lines = MuxedLines::new()?;
lines.add_file("/host_ssh/auth.log").await?;
lines.add_file(&ssh_auth_log).await?;
let mut login_attempts: HashMap<String, usize> = HashMap::new();
let seconds_iptables = Duration::from_secs(60);
println!(
"starting iptables-save, run every {} seconds",
seconds_iptables.as_secs()
);
if let Some(iptables_save) = iptables_save {
let seconds_iptables = Duration::from_secs(60);
println!(
"starting iptables-save, run every {} seconds",
seconds_iptables.as_secs()
);
thread::spawn(move || loop {
sleep(seconds_iptables);
iptables_save::save_iptables(&iptables_save);
println!("saved iptables rules");
});
}
thread::spawn(move || loop {
sleep(seconds_iptables);
iptables_save::save_iptables();
println!("saved iptables rules");
});
thread::spawn(|| {
start_tcp_command_server();
});
println!("listening to changes over /host_ssh/auth.log");
println!("listening to changes over {}", ssh_auth_log.display());
while let Ok(Some(line)) = lines.next_line().await {
if let Some(login_attempt) = LoginAttempt::capture(line.line()) {
println!(

View File

@ -1,103 +0,0 @@
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;
pub fn start_tcp_command_server() {
let listener = TcpListener::bind("127.0.0.1:9999").unwrap();
println!("listening on port 9999 for tcp commands");
for stream in listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(move || handle_client(&stream));
}
Err(e) => {
eprintln!("err with tcp conn: {}", e);
}
}
}
}
fn handle_client(mut stream: &TcpStream) {
for line in BufReader::new(stream).lines() {
let buffer = match line {
Ok(data) => data,
Err(_) => return,
};
if buffer.is_empty() {
break;
}
let mut parts = buffer.trim().split_whitespace();
if let Some(command) = parts.next() {
let arguments: Vec<&str> = parts.collect();
let response = handle_command(command, arguments);
stream.write_all(response.as_bytes()).unwrap();
} else {
stream.write_all("invalid command".as_bytes()).unwrap();
}
}
}
fn handle_command(command: &str, arguments: Vec<&str>) -> String {
match command {
"banport" => {
if let Some(port) = arguments.get(0) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.append_unique(
"filter",
"INPUT",
&format!("-p tcp --dport {} -j DROP", port),
);
format!("banned port {} for all ips", port)
} else {
"missing args for banport: port".to_string()
}
}
"unbanport" => {
if let Some(port) = arguments.get(0) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.delete(
"filter",
"INPUT",
&format!("-p tcp --dport {} -j DROP", port),
);
format!("unbanned port {}", port)
} else {
"missing args for unbanport: port".to_string()
}
}
"allowipport" => {
if let (Some(ip), Some(port)) = (arguments.get(0), arguments.get(1)) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.append_unique(
"filter",
"INPUT",
&format!("-s {} -p tcp --dport {} -j ACCEPT", ip, port),
);
format!("allowed {} to access {}", ip, port)
} else {
"missing args for allowipport: ip and port".to_string()
}
}
"removeipport" => {
if let (Some(ip), Some(port)) = (arguments.get(0), arguments.get(1)) {
let iptables = iptables::new(false).unwrap();
let _ = iptables.delete(
"filter",
"INPUT",
&format!("-s {} -p tcp --dport {} -j ACCEPT", ip, port),
);
format!("rm {} access to {}", ip, port)
} else {
"missing args for rmipport: ip and port".to_string()
}
}
_ => {
format!("unknown command: {}", command)
}
}
}