Added context to SMTP rate limiter keys

This commit is contained in:
mdecimus 2025-01-19 11:36:13 +01:00
parent 26e00b44a8
commit 3c7caddd98
7 changed files with 27 additions and 18 deletions

View file

@ -74,7 +74,7 @@ pub const KV_RATE_LIMIT_RCPT: u8 = 2;
pub const KV_RATE_LIMIT_SCAN: u8 = 3;
pub const KV_RATE_LIMIT_LOITER: u8 = 4;
pub const KV_RATE_LIMIT_AUTH: u8 = 5;
pub const KV_RATE_LIMIT_HASH: u8 = 6;
pub const KV_RATE_LIMIT_SMTP: u8 = 6;
pub const KV_RATE_LIMIT_CONTACT: u8 = 7;
pub const KV_RATE_LIMIT_HTTP_AUTHENTICATED: u8 = 8;
pub const KV_RATE_LIMIT_HTTP_ANONYMOUS: u8 = 9;

View file

@ -152,7 +152,7 @@ impl ManageStore for Server {
Some("rate-scan") => vec![KV_RATE_LIMIT_SCAN].into(),
Some("rate-loiter") => vec![KV_RATE_LIMIT_LOITER].into(),
Some("rate-auth") => vec![KV_RATE_LIMIT_AUTH].into(),
Some("rate-hash") => vec![KV_RATE_LIMIT_HASH].into(),
Some("rate-smtp") => vec![KV_RATE_LIMIT_SMTP].into(),
Some("rate-contact") => vec![KV_RATE_LIMIT_CONTACT].into(),
Some("rate-http-authenticated") => {
vec![KV_RATE_LIMIT_HTTP_AUTHENTICATED].into()

View file

@ -8,7 +8,7 @@ use common::{
config::smtp::*,
expr::{functions::ResolveVariable, *},
listener::SessionStream,
ThrottleKey, KV_RATE_LIMIT_HASH,
ThrottleKey, KV_RATE_LIMIT_SMTP,
};
use queue::QueueQuota;
use trc::SmtpEvent;
@ -17,11 +17,11 @@ use utils::config::Rate;
use super::Session;
pub trait NewKey: Sized {
fn new_key(&self, e: &impl ResolveVariable) -> ThrottleKey;
fn new_key(&self, e: &impl ResolveVariable, context: &str) -> ThrottleKey;
}
impl NewKey for QueueQuota {
fn new_key(&self, e: &impl ResolveVariable) -> ThrottleKey {
fn new_key(&self, e: &impl ResolveVariable, _: &str) -> ThrottleKey {
let mut hasher = blake3::Hasher::new();
if (self.keys & THROTTLE_RCPT) != 0 {
@ -72,7 +72,7 @@ impl NewKey for QueueQuota {
}
impl NewKey for QueueRateLimiter {
fn new_key(&self, e: &impl ResolveVariable) -> ThrottleKey {
fn new_key(&self, e: &impl ResolveVariable, context: &str) -> ThrottleKey {
let mut hasher = blake3::Hasher::new();
if (self.keys & THROTTLE_RCPT) != 0 {
@ -129,8 +129,9 @@ impl NewKey for QueueRateLimiter {
if (self.keys & THROTTLE_LOCAL_IP) != 0 {
hasher.update(e.resolve_variable(V_LOCAL_IP).to_string().as_bytes());
}
hasher.update(&self.rate.period.as_secs().to_ne_bytes()[..]);
hasher.update(&self.rate.requests.to_ne_bytes()[..]);
hasher.update(&self.rate.period.as_secs().to_be_bytes()[..]);
hasher.update(&self.rate.requests.to_be_bytes()[..]);
hasher.update(context.as_bytes());
ThrottleKey {
hash: hasher.finalize().into(),
@ -170,7 +171,7 @@ impl<T: SessionStream> Session<T> {
}
// Build throttle key
let key = t.new_key(self);
let key = t.new_key(self, "inbound");
// Check rate
match self
@ -178,7 +179,7 @@ impl<T: SessionStream> Session<T> {
.core
.storage
.lookup
.is_rate_allowed(KV_RATE_LIMIT_HASH, key.hash.as_slice(), &t.rate, false)
.is_rate_allowed(KV_RATE_LIMIT_SMTP, key.hash.as_slice(), &t.rate, false)
.await
{
Ok(Some(_)) => {
@ -220,7 +221,7 @@ impl<T: SessionStream> Session<T> {
.storage
.lookup
.is_rate_allowed(
KV_RATE_LIMIT_HASH,
KV_RATE_LIMIT_SMTP,
hasher.finalize().as_bytes(),
rate,
false,

View file

@ -129,7 +129,7 @@ impl HasQueueQuota for Server {
.await
.unwrap_or(false)
{
let key = quota.new_key(envelope);
let key = quota.new_key(envelope, "");
if let Some(max_size) = quota.size {
let used_size = self
.core

View file

@ -7,7 +7,7 @@
use std::future::Future;
use common::{
config::smtp::QueueRateLimiter, expr::functions::ResolveVariable, Server, KV_RATE_LIMIT_HASH,
config::smtp::QueueRateLimiter, expr::functions::ResolveVariable, Server, KV_RATE_LIMIT_SMTP,
};
use store::write::now;
@ -37,13 +37,13 @@ impl IsAllowed for Server {
.await
.unwrap_or(false)
{
let key = throttle.new_key(envelope);
let key = throttle.new_key(envelope, "outbound");
match self
.core
.storage
.lookup
.is_rate_allowed(KV_RATE_LIMIT_HASH, key.as_ref(), &throttle.rate, false)
.is_rate_allowed(KV_RATE_LIMIT_SMTP, key.as_ref(), &throttle.rate, false)
.await
{
Ok(Some(next_refill)) => {

View file

@ -41,9 +41,19 @@ impl HttpStoreGet for Arc<HttpStore> {
fn contains(&self, key: &str) -> bool {
#[cfg(feature = "test_mode")]
{
if key.contains("open") || key.contains("tank") {
if self.config.url.contains("phishtank.com")
|| self.config.url.contains("openphish.com")
{
return (self.config.url.contains("open") && key.contains("open"))
|| (self.config.url.contains("tank") && key.contains("tank"));
} else if self.config.url.contains("disposable.github.io") {
return key.ends_with("guerrillamail.com") || key.ends_with("disposable.org");
} else if self.config.url.contains("free_email_provider_domains.txt") {
return key.ends_with("gmail.com")
|| key.ends_with("googlemail.com")
|| key.ends_with("yahoomail.com")
|| key.ends_with("outlook.com")
|| key.ends_with("freemail.org");
}
}

View file

@ -119,8 +119,6 @@ allow-invalid-certs = true
"url-redirectors" = {"bit.ly", "redirect.io", "redirect.me", "redirect.org", "redirect.com", "redirect.net", "t.ly", "tinyurl.com"}
"spam-traps" = {"spamtrap@*"}
"trusted-domains" = {"stalw.art"}
"freemail-providers" = {"gmail.com", "googlemail.com", "yahoomail.com", "outlook.com", "*freemail.org"}
"disposable-providers" = {"guerrillamail.com", "*disposable.org"}
"surbl-hashbl" = {"bit.ly", "drive.google.com", "lnkiy.in"}
"#;