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-07-23 21:14:51 +02:00
|
|
|
use cli::Cli;
|
2024-07-21 13:01:23 +02:00
|
|
|
use iptables_wrapper::{
|
2024-07-23 21:14:51 +02:00
|
|
|
allow_ip_for_port, is_port_secured, list_banned_ips, list_secured_ports,
|
|
|
|
map_secured_ports_allowed_ips, remove_allow_ip_for_port, secure_port, unsecure_port,
|
2024-07-21 13:01:23 +02:00
|
|
|
};
|
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-07-23 21:14:51 +02:00
|
|
|
use std::thread::spawn;
|
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() {
|
2024-07-23 21:14:51 +02:00
|
|
|
match Cli::from_args() {
|
|
|
|
Cli::BanServer {
|
2024-05-28 16:03:32 +02:00
|
|
|
ssh_auth_log,
|
|
|
|
iptables_save,
|
|
|
|
} => {
|
|
|
|
let _ = start_ban_server(ssh_auth_log, iptables_save).await;
|
|
|
|
}
|
2024-07-23 21:14:51 +02:00
|
|
|
Cli::ListBannedIps => {
|
|
|
|
println!("{}", serde_json::to_string(&list_banned_ips()).unwrap());
|
2024-07-21 13:01:23 +02:00
|
|
|
}
|
2024-07-23 21:14:51 +02:00
|
|
|
Cli::ListSecuredPorts { docker } => {
|
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
serde_json::to_string(&list_secured_ports(docker)).unwrap()
|
|
|
|
);
|
2024-06-09 15:17:36 +02:00
|
|
|
}
|
2024-07-23 21:14:51 +02:00
|
|
|
Cli::MapSecuredPortsAllowedIps { docker } => {
|
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
serde_json::to_string(&map_secured_ports_allowed_ips(docker)).unwrap()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Cli::IsPortSecured { port, docker } => {
|
|
|
|
println!("{}", is_port_secured(port, docker));
|
|
|
|
}
|
|
|
|
Cli::SecurePort {
|
2024-05-28 21:13:53 +02:00
|
|
|
port,
|
|
|
|
docker,
|
|
|
|
position,
|
2024-07-23 22:56:03 +02:00
|
|
|
} => println!("{}", secure_port(port, docker, position).is_ok()),
|
|
|
|
Cli::UnsecurePort { port, docker } => println!("{}", unsecure_port(port, docker).is_ok()),
|
2024-07-23 21:14:51 +02:00
|
|
|
Cli::AllowIpForPort {
|
2024-05-28 21:13:53 +02:00
|
|
|
ip,
|
|
|
|
port,
|
|
|
|
docker,
|
|
|
|
position,
|
2024-07-23 22:56:03 +02:00
|
|
|
} => println!("{}", allow_ip_for_port(&ip, port, docker, position).is_ok()),
|
2024-07-23 21:14:51 +02:00
|
|
|
Cli::OnlyIpForPort { ip, port, docker } => {
|
2024-07-23 22:56:03 +02:00
|
|
|
let allowed = allow_ip_for_port(&ip, port, docker, Some(1));
|
|
|
|
let secured = secure_port(port, docker, Some(2));
|
|
|
|
println!("{}", allowed.is_ok() && secured.is_ok());
|
|
|
|
}
|
|
|
|
Cli::RemoveAllowIpPort { ip, port, docker } => {
|
|
|
|
println!("{}", remove_allow_ip_for_port(&ip, port, docker).is_ok())
|
2024-07-23 21:14:51 +02:00
|
|
|
}
|
|
|
|
Cli::SaveIPTables { iptables_save } => {
|
|
|
|
let path = if let Some(iptables_save) = iptables_save {
|
|
|
|
iptables_save
|
|
|
|
} else {
|
|
|
|
PathBuf::from("/etc/iptables/rules.v4")
|
|
|
|
};
|
|
|
|
|
2024-07-23 22:56:03 +02:00
|
|
|
println!("{}", iptables_save::save_iptables(&path).is_ok())
|
2024-05-28 21:13:53 +02:00
|
|
|
}
|
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!(
|
2024-07-23 21:14:51 +02:00
|
|
|
"Saving IPTables every {} seconds",
|
2024-05-28 16:03:32 +02:00
|
|
|
seconds_iptables.as_secs()
|
|
|
|
);
|
2024-07-23 21:14:51 +02:00
|
|
|
|
|
|
|
spawn(move || loop {
|
2024-05-28 16:03:32 +02:00
|
|
|
sleep(seconds_iptables);
|
|
|
|
iptables_save::save_iptables(&iptables_save);
|
|
|
|
});
|
|
|
|
}
|
2024-05-12 14:54:47 +02:00
|
|
|
|
2024-07-23 21:14:51 +02:00
|
|
|
println!("Listeging to changer over file: {}", ssh_auth_log.display());
|
|
|
|
loop {
|
|
|
|
while let Ok(Some(line)) = lines.next_line().await {
|
|
|
|
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
|
|
|
|
2024-07-23 21:14:51 +02:00
|
|
|
match login_attempts.get_mut(&login_attempt.ip) {
|
|
|
|
Some(count) => {
|
|
|
|
*count += 1;
|
|
|
|
|
|
|
|
if *count == 3 {
|
|
|
|
if iptables
|
|
|
|
.append_unique(
|
|
|
|
"filter",
|
|
|
|
"INPUT",
|
|
|
|
&format!("--source {} -j DROP", login_attempt.ip),
|
|
|
|
)
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
println!("IP {} banned", login_attempt.ip);
|
|
|
|
} else {
|
|
|
|
println!("IP {} already banned", login_attempt.ip);
|
2024-05-12 14:03:26 +02:00
|
|
|
}
|
2024-07-23 21:14:51 +02:00
|
|
|
|
|
|
|
login_attempts.remove(&login_attempt.ip);
|
2024-05-12 14:03:26 +02:00
|
|
|
}
|
2024-05-12 13:46:11 +02:00
|
|
|
}
|
2024-07-23 21:14:51 +02:00
|
|
|
None => {
|
|
|
|
login_attempts.insert(login_attempt.ip.clone(), 1);
|
|
|
|
}
|
2024-05-12 13:46:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|