1
0

better err handling, and WIP iptables asve

This commit is contained in:
midefos 2024-05-12 14:03:26 +02:00
parent f4a3062663
commit bb9235b6ef
4 changed files with 43 additions and 23 deletions

View File

@ -6,6 +6,7 @@ services:
restart: always restart: always
build: build:
context: . context: .
pull_policy: build
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
@ -13,3 +14,4 @@ services:
volumes: volumes:
- /var/log/auth.log:/host_ssh/auth.log - /var/log/auth.log:/host_ssh/auth.log
- /etc/iptables/rules.v4:/host_iptables/rules.v4

8
src/iptables_save.rs Normal file
View File

@ -0,0 +1,8 @@
use std::process::Command;
pub fn save_iptables() {
let _ = Command::new("iptables-save")
.arg(">")
.arg("/host_iptables/rules.v4")
.output();
}

View File

@ -1,7 +1,9 @@
use regex::Regex;
pub struct LoginAttempt { pub struct LoginAttempt {
pub ip: String, pub ip: String,
user: String, pub user: String,
port: String, pub port: String,
} }
impl LoginAttempt { impl LoginAttempt {
@ -12,4 +14,14 @@ impl LoginAttempt {
port: port.to_string(), port: port.to_string(),
} }
} }
pub fn capture(line: &str) -> Option<Self> {
let regex = Regex::new(r#"Failed password for (?:invalid user )?(?P<user>\S+) from (?P<ip>\S+) port (?P<port>\d+)"#).unwrap();
let captured = regex.captures(line)?;
Some(Self::new(
captured.name("ip").unwrap().as_str(),
captured.name("user").unwrap().as_str(),
captured.name("port").unwrap().as_str(),
))
}
} }

View File

@ -1,8 +1,8 @@
pub mod iptables_save;
pub mod login_attempt; pub mod login_attempt;
use linemux::MuxedLines; use linemux::MuxedLines;
use login_attempt::LoginAttempt; use login_attempt::LoginAttempt;
use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
#[tokio::main] #[tokio::main]
@ -12,23 +12,31 @@ async fn main() -> std::io::Result<()> {
lines.add_file("/host_ssh/auth.log").await?; lines.add_file("/host_ssh/auth.log").await?;
let mut login_attempts: HashMap<String, usize> = HashMap::new(); let mut login_attempts: HashMap<String, usize> = HashMap::new();
println!("Listening to changes over /host_ssh/auth.log"); println!("listening to changes over /host_ssh/auth.log");
while let Ok(Some(line)) = lines.next_line().await { while let Ok(Some(line)) = lines.next_line().await {
if let Some(login_attempt) = capture_failed_login_attempt(line.line()) { if let Some(login_attempt) = LoginAttempt::capture(line.line()) {
println!(
"failed login attempt from {}@{}:{}",
login_attempt.user, login_attempt.ip, login_attempt.port
);
match login_attempts.get_mut(&login_attempt.ip) { match login_attempts.get_mut(&login_attempt.ip) {
Some(count) => { Some(count) => {
*count += 1; *count += 1;
if *count == 3 { if *count == 3 {
iptables match iptables.append_unique(
.append_unique( "filter",
"filter", "INPUT",
"INPUT", &format!("--source {} -j DROP", login_attempt.ip),
&format!("--source {} -j DROP", login_attempt.ip), ) {
) Ok(_) => {
.unwrap(); println!("{} banned", login_attempt.ip);
}
Err(_) => {
println!("{} already banned", login_attempt.ip);
}
}
login_attempts.remove(&login_attempt.ip); login_attempts.remove(&login_attempt.ip);
println!("{} banned", login_attempt.ip);
} }
} }
None => { None => {
@ -39,13 +47,3 @@ async fn main() -> std::io::Result<()> {
} }
Ok(()) Ok(())
} }
fn capture_failed_login_attempt(line: &str) -> Option<LoginAttempt> {
let regex = Regex::new(r#"Failed password for (?:invalid user )?(?P<user>\S+) from (?P<ip>\S+) port (?P<port>\d+)"#).unwrap();
let captured = regex.captures(line)?;
Some(LoginAttempt::new(
captured.name("ip").unwrap().as_str(),
captured.name("user").unwrap().as_str(),
captured.name("port").unwrap().as_str(),
))
}