diff --git a/echoircd.conf.example b/echoircd.conf.example index d8e9af3..2eb9283 100644 --- a/echoircd.conf.example +++ b/echoircd.conf.example @@ -421,6 +421,8 @@ amu_target = both # connectban_ipv4cidr = 32 # range width for IPv4 counting (default /32) # connectban_ipv6cidr = 128 # range width for IPv6 counting (default /128) # connectban_banmessage = Too many connections from your address +# connectban_exempt = 10.0.0.0/8 # never ban this glob/CIDR (repeatable); +# # loopback (127.0.0.0/8, ::1) is always exempt # --- hashident: replace ident with a stable opaque token per IP --- # hashident = yes # hashident_key = CHANGE_THIS_SECRET # HMAC key; makes the mapping unforgeable diff --git a/src/modules/connectban.rs b/src/modules/connectban.rs index c665240..0cf12c2 100644 --- a/src/modules/connectban.rs +++ b/src/modules/connectban.rs @@ -67,12 +67,25 @@ fn range_of(ip: IpAddr, v4cidr: u8, v6cidr: u8) -> (String, String) { } } +/// Whether `ip` matches a configured `connectban_exempt` glob/CIDR (repeatable). +fn connectban_exempt(s: &Server, ip: IpAddr) -> bool { + let ipstr = ip.to_string(); + s.conf_all("connectban_exempt") + .iter() + .any(|m| crate::modules::connclass::ip_matches(m, &ipstr)) +} + /// Record a new connection from `ip`, z-lining its range if it crosses the limit. /// No-op when connectban is disabled or still inside the boot-grace window. pub fn on_connect(s: &mut Server, ip: IpAddr) { if !s.conf_bool("connectban", false) { return; } + // never connect-ban loopback (local services, bridges, admin tooling all dial in + // over 127.0.0.1 / ::1) or an admin-configured exempt range + if ip.is_loopback() || connectban_exempt(s, ip) { + return; + } let threshold = s.conf_num("connectban_threshold", 10u32).max(2); let v4 = s.conf_num("connectban_ipv4cidr", 32u8).clamp(1, 32); let v6 = s.conf_num("connectban_ipv6cidr", 128u8).clamp(1, 128); diff --git a/src/modules/connflood.rs b/src/modules/connflood.rs index 13194ea..e839071 100644 --- a/src/modules/connflood.rs +++ b/src/modules/connflood.rs @@ -24,6 +24,10 @@ fn cfg(s: &Server) -> Option<(u32, u64)> { /// Record a connection from `ip`; returns true when it exceeds the limit (the /// caller should refuse it). No-op → false when connflood is unconfigured. pub fn over_limit(s: &mut Server, ip: IpAddr) -> bool { + // loopback (local services / bridges / admin) is never connection-throttled + if ip.is_loopback() { + return false; + } let Some((max, secs)) = cfg(s) else { return false; };