Detect and ban port scanners as well as other forms of abuse (closes #820)

This commit is contained in:
mdecimus
2024-10-08 11:48:03 +02:00
parent 1561a603ab
commit 581533b09c
14 changed files with 303 additions and 62 deletions

View File

@@ -209,9 +209,12 @@ impl<T: SessionStream> Session<T> {
To = rcpt.address_lcase.clone(),
);
self.data.rcpt_to.pop();
let rcpt_to = self.data.rcpt_to.pop().unwrap().address_lcase;
return self
.rcpt_error(b"550 5.1.2 Mailbox does not exist.\r\n")
.rcpt_error(
b"550 5.1.2 Mailbox does not exist.\r\n",
rcpt_to,
)
.await;
}
}
@@ -243,8 +246,10 @@ impl<T: SessionStream> Session<T> {
To = rcpt.address_lcase.clone(),
);
self.data.rcpt_to.pop();
return self.rcpt_error(b"550 5.1.2 Relay not allowed.\r\n").await;
let rcpt_to = self.data.rcpt_to.pop().unwrap().address_lcase;
return self
.rcpt_error(b"550 5.1.2 Relay not allowed.\r\n", rcpt_to)
.await;
}
}
Err(err) => {
@@ -275,8 +280,10 @@ impl<T: SessionStream> Session<T> {
To = rcpt.address_lcase.clone(),
);
self.data.rcpt_to.pop();
return self.rcpt_error(b"550 5.1.2 Relay not allowed.\r\n").await;
let rcpt_to = self.data.rcpt_to.pop().unwrap().address_lcase;
return self
.rcpt_error(b"550 5.1.2 Relay not allowed.\r\n", rcpt_to)
.await;
}
if self.is_allowed().await {
@@ -301,17 +308,22 @@ impl<T: SessionStream> Session<T> {
self.write(b"250 2.1.5 OK\r\n").await
}
async fn rcpt_error(&mut self, response: &[u8]) -> Result<(), ()> {
async fn rcpt_error(&mut self, response: &[u8], rcpt: String) -> Result<(), ()> {
tokio::time::sleep(self.params.rcpt_errors_wait).await;
self.data.rcpt_errors += 1;
let has_too_many_errors = self.data.rcpt_errors >= self.params.rcpt_errors_max;
match self.server.is_rcpt_fail2banned(self.data.remote_ip).await {
match self
.server
.is_rcpt_fail2banned(self.data.remote_ip, &rcpt)
.await
{
Ok(true) => {
trc::event!(
Security(SecurityEvent::BruteForceBan),
Security(SecurityEvent::AbuseBan),
SpanId = self.data.session_id,
RemoteIp = self.data.remote_ip,
To = rcpt,
);
}
Ok(false) => {
@@ -320,6 +332,7 @@ impl<T: SessionStream> Session<T> {
Smtp(SmtpEvent::TooManyInvalidRcpt),
SpanId = self.data.session_id,
Limit = self.params.rcpt_errors_max,
To = rcpt,
);
}
}

View File

@@ -17,7 +17,7 @@ use smtp_proto::{
*,
};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use trc::{NetworkEvent, SmtpEvent};
use trc::{NetworkEvent, SecurityEvent, SmtpEvent};
use crate::core::{Session, State};
@@ -236,6 +236,32 @@ impl<T: SessionStream> Session<T> {
Err(err) => match err {
Error::NeedsMoreData { .. } => break 'outer,
Error::UnknownCommand | Error::InvalidResponse { .. } => {
// Check for port scanners
if !self.is_authenticated() {
match self
.server
.is_scanner_fail2banned(self.data.remote_ip)
.await
{
Ok(true) => {
trc::event!(
Security(SecurityEvent::ScanBan),
SpanId = self.data.session_id,
RemoteIp = self.data.remote_ip,
Reason = "Invalid SMTP command",
);
return Err(());
}
Ok(false) => {}
Err(err) => {
trc::error!(err
.span_id(self.data.session_id)
.details("Failed to check for fail2ban"));
}
}
}
trc::event!(
Smtp(SmtpEvent::InvalidCommand),
SpanId = self.data.session_id,