2024-05-28 16:03:32 +02:00
|
|
|
pub mod cli;
|
2024-05-12 14:03:26 +02:00
|
|
|
pub mod iptables_save;
|
2024-05-28 16:03:32 +02:00
|
|
|
pub mod iptables_wrapper;
|
2024-05-12 13:46:11 +02:00
|
|
|
pub mod login_attempt;
|
|
|
|
|
2024-05-28 16:03:32 +02:00
|
|
|
use cli::Arguments;
|
|
|
|
use iptables_wrapper::{allow_ip_port, ban_port, remove_ip_port, unban_port};
|
2024-05-12 13:46:11 +02:00
|
|
|
use linemux::MuxedLines;
|
|
|
|
use login_attempt::LoginAttempt;
|
2024-05-28 16:03:32 +02:00
|
|
|
use std::path::PathBuf;
|
2024-05-20 23:36:34 +02:00
|
|
|
use std::thread;
|
2024-05-12 14:54:47 +02:00
|
|
|
use std::{collections::HashMap, thread::sleep, time::Duration};
|
2024-05-28 16:03:32 +02:00
|
|
|
use structopt::StructOpt;
|
2024-05-12 13:46:11 +02:00
|
|
|
|
|
|
|
#[tokio::main]
|
2024-05-28 16:03:32 +02:00
|
|
|
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;
|
|
|
|
}
|
2024-05-28 21:13:53 +02:00
|
|
|
Arguments::BanPort {
|
|
|
|
port,
|
|
|
|
docker,
|
|
|
|
position,
|
|
|
|
} => ban_port(port, docker, position),
|
2024-05-28 17:31:14 +02:00
|
|
|
Arguments::UnbanPort { port, docker } => unban_port(port, docker),
|
2024-05-28 21:13:53 +02:00
|
|
|
Arguments::AllowIpPort {
|
|
|
|
ip,
|
|
|
|
port,
|
|
|
|
docker,
|
|
|
|
position,
|
|
|
|
} => allow_ip_port(&ip, port, docker, position),
|
|
|
|
Arguments::OnlyIpPort { ip, port, docker } => {
|
|
|
|
allow_ip_port(&ip, port, docker, Some(1));
|
|
|
|
ban_port(port, docker, Some(2));
|
|
|
|
}
|
2024-05-28 17:31:14 +02:00
|
|
|
Arguments::RemoveIpPort { ip, port, docker } => remove_ip_port(&ip, port, docker),
|
2024-05-28 16:03:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn start_ban_server(
|
|
|
|
ssh_auth_log: PathBuf,
|
|
|
|
iptables_save: Option<PathBuf>,
|
|
|
|
) -> std::io::Result<()> {
|
2024-05-12 13:46:11 +02:00
|
|
|
let iptables = iptables::new(false).unwrap();
|
|
|
|
let mut lines = MuxedLines::new()?;
|
2024-05-28 16:03:32 +02:00
|
|
|
lines.add_file(&ssh_auth_log).await?;
|
2024-05-12 13:46:11 +02:00
|
|
|
let mut login_attempts: HashMap<String, usize> = HashMap::new();
|
|
|
|
|
2024-05-28 16:03:32 +02:00
|
|
|
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");
|
|
|
|
});
|
|
|
|
}
|
2024-05-12 14:54:47 +02:00
|
|
|
|
2024-05-28 16:03:32 +02:00
|
|
|
println!("listening to changes over {}", ssh_auth_log.display());
|
2024-05-12 13:46:11 +02:00
|
|
|
while let Ok(Some(line)) = lines.next_line().await {
|
2024-05-12 14:03:26 +02:00
|
|
|
if let Some(login_attempt) = LoginAttempt::capture(line.line()) {
|
|
|
|
println!(
|
|
|
|
"failed login attempt from {}@{}:{}",
|
|
|
|
login_attempt.user, login_attempt.ip, login_attempt.port
|
|
|
|
);
|
2024-05-12 13:46:11 +02:00
|
|
|
match login_attempts.get_mut(&login_attempt.ip) {
|
|
|
|
Some(count) => {
|
|
|
|
*count += 1;
|
|
|
|
|
|
|
|
if *count == 3 {
|
2024-05-12 14:03:26 +02:00
|
|
|
match iptables.append_unique(
|
|
|
|
"filter",
|
|
|
|
"INPUT",
|
|
|
|
&format!("--source {} -j DROP", login_attempt.ip),
|
|
|
|
) {
|
|
|
|
Ok(_) => {
|
|
|
|
println!("{} banned", login_attempt.ip);
|
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
println!("{} already banned", login_attempt.ip);
|
|
|
|
}
|
|
|
|
}
|
2024-05-12 13:46:11 +02:00
|
|
|
login_attempts.remove(&login_attempt.ip);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
login_attempts.insert(login_attempt.ip.clone(), 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|