diff --git a/chorus-bin/src/main.rs b/chorus-bin/src/main.rs index 4049446..b7e5250 100644 --- a/chorus-bin/src/main.rs +++ b/chorus-bin/src/main.rs @@ -131,7 +131,7 @@ async fn main() -> Result<(), Error> { }; // Possibly IP block - if ! GLOBALS.config.read().dont_ip_block { + if GLOBALS.config.read().enable_ip_blocking { let ip_data = GLOBALS.store.get().unwrap().get_ip_data(hashed_peer.ip())?; if ip_data.is_banned() { log::debug!(target: "Client", @@ -394,9 +394,12 @@ async fn handle_http_request( let old_num_websockets = GLOBALS.num_clients.fetch_sub(1, Ordering::SeqCst); // Update ip data (including ban time) + // if GLOBALS.config.read().enable_ip_blocking { let mut ban_seconds = 0; + let minimum_ban_seconds = GLOBALS.config.read().minimum_ban_seconds; if let Ok(mut ip_data) = GLOBALS.store.get().unwrap().get_ip_data(peer.ip()) { - ban_seconds = ip_data.update_on_session_close(session_exit); + ban_seconds = + ip_data.update_on_session_close(session_exit, minimum_ban_seconds); let _ = GLOBALS .store .get() @@ -460,7 +463,9 @@ impl WebSocketService { let mut last_message_at = Instant::now(); - let mut interval = tokio::time::interval(Duration::from_secs(5)); + let timeout_seconds = GLOBALS.config.read().timeout_seconds; + + let mut interval = tokio::time::interval(Duration::from_secs(1)); let _ = interval.tick().await; // consume the first tick tokio::pin!(interval); @@ -469,8 +474,8 @@ impl WebSocketService { instant = interval.tick() => { // Drop them if they have no subscriptions if self.subscriptions.is_empty() { - // And they are idle for 5 seconds with no subscriptions - if last_message_at + Duration::from_secs(5) < instant { + // And they are idle for timeout_seconds with no subscriptions + if last_message_at + Duration::from_secs(timeout_seconds) < instant { self.websocket.send(Message::Close(None)).await?; return Err(ChorusError::TimedOut.into()); } diff --git a/chorus-lib/src/config.rs b/chorus-lib/src/config.rs index f6d427b..e1c18de 100644 --- a/chorus-lib/src/config.rs +++ b/chorus-lib/src/config.rs @@ -30,7 +30,9 @@ pub struct FriendlyConfig { pub server_log_level: String, pub library_log_level: String, pub client_log_level: String, - pub dont_ip_block: bool, + pub enable_ip_blocking: bool, + pub minimum_ban_seconds: u64, + pub timeout_seconds: u64, } impl Default for FriendlyConfig { @@ -59,7 +61,9 @@ impl Default for FriendlyConfig { server_log_level: "Info".to_string(), library_log_level: "Info".to_string(), client_log_level: "Info".to_string(), - dont_ip_block: false, + enable_ip_blocking: true, + minimum_ban_seconds: 1, + timeout_seconds: 30, } } } @@ -90,7 +94,9 @@ impl FriendlyConfig { server_log_level, library_log_level, client_log_level, - dont_ip_block, + enable_ip_blocking, + minimum_ban_seconds, + timeout_seconds, } = self; let mut public_key: Option = None; @@ -137,7 +143,9 @@ impl FriendlyConfig { server_log_level, library_log_level, client_log_level, - dont_ip_block, + enable_ip_blocking, + minimum_ban_seconds, + timeout_seconds, }) } } @@ -168,7 +176,9 @@ pub struct Config { pub server_log_level: log::LevelFilter, pub library_log_level: log::LevelFilter, pub client_log_level: log::LevelFilter, - pub dont_ip_block: bool, + pub enable_ip_blocking: bool, + pub minimum_ban_seconds: u64, + pub timeout_seconds: u64, } impl Default for Config { diff --git a/chorus-lib/src/ip.rs b/chorus-lib/src/ip.rs index 49a3743..d063f08 100644 --- a/chorus-lib/src/ip.rs +++ b/chorus-lib/src/ip.rs @@ -123,13 +123,17 @@ pub struct IpData { } impl IpData { - pub fn update_on_session_close(&mut self, session_exit: SessionExit) -> u64 { + pub fn update_on_session_close( + &mut self, + session_exit: SessionExit, + minimum_ban_seconds: u64, + ) -> u64 { // Update reputation self.reputation.update(session_exit); // Compute ban_until let mut until = Time::now(); - let seconds = self.ban_seconds(session_exit); + let seconds = self.ban_seconds(session_exit, minimum_ban_seconds); until.0 += seconds; self.ban_until = Time(self.ban_until.0.max(until.0)); @@ -141,14 +145,14 @@ impl IpData { self.ban_until > Time::now() } - fn ban_seconds(&self, session_exit: SessionExit) -> u64 { + fn ban_seconds(&self, session_exit: SessionExit, minimum_ban_seconds: u64) -> u64 { let multiplier = self.reputation.ban_multiplier(); match session_exit { - SessionExit::Ok => 2, - SessionExit::ErrorExit => 2 + (2.0 * multiplier) as u64, - SessionExit::TooManyErrors => 2 + (5.0 * multiplier) as u64, - SessionExit::Timeout => 2 + (4.0 * multiplier) as u64, + SessionExit::Ok => minimum_ban_seconds, + SessionExit::ErrorExit => minimum_ban_seconds + (2.0 * multiplier) as u64, + SessionExit::TooManyErrors => minimum_ban_seconds + (5.0 * multiplier) as u64, + SessionExit::Timeout => minimum_ban_seconds + (1.0 * multiplier) as u64, } } } diff --git a/contrib/chorus.toml b/contrib/chorus.toml index 58aeba3..ef5410a 100644 --- a/contrib/chorus.toml +++ b/contrib/chorus.toml @@ -215,3 +215,33 @@ library_log_level = "Info" # Default is Info # client_log_level = "Info" + + +# Whether to block incoming connections based on recent prior behavior +# +# Chorus normally blocks IP addresses for a short period preventing quick reconnections, +# and for a longer period if the previous connection ended in some error condition. +# +# By setting this variable to false, it will allow all connections, incluing poorly behaving +# clients that reconnect over and over in a tight loop. +# +# Default is true +# +enable_ip_blocking = true + + +# Number of seconds to ban an IP address after disconnection. +# +# Enforcing this minimum ban prevents clients from immediately reconnecting which can cause tight loops. +# Only relevant if enable_ip_blocking is true. +# +# Default is 1 +# +minimum_ban_seconds = 1 + + +# Number of seconds beyond which chorus times out a client that has no open subscriptions. +# +# Default is 30 +# +timeout_seconds = 30 \ No newline at end of file diff --git a/docs/CONFIG.md b/docs/CONFIG.md index e866d4c..a132124 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -189,10 +189,28 @@ Possible values are: Trace, Debug, Info, Warn, Error Default is Info -### dont_ip_block +### enable_ip_blocking + +Whether to block incoming connections based on recent prior behavior Chorus normally blocks IP addresses for a short period preventing quick reconnections, and for a longer period if the previous connection ended in some error condition. -By setting this variable to true, it will allow all connections. +By setting this variable to false, it will allow all connections, incluing poorly behaving clients that reconnect over and over in a tight loop. -Default is false. +Default is true + +### minimum_ban_seconds + +Number of seconds to ban an IP address after disconnection. + +Enforcing this minimum ban prevents clients from immediately reconnecting which can cause tight loops. + +Only relevant if enable_ip_blocking is true. + +Default is 1 + +### timeout_seconds + +Number of seconds beyond which chorus times out a client that has no open subscriptions. + +Default is 30 diff --git a/sample/sample.config.toml b/sample/sample.config.toml index 658af33..d3ac3d0 100644 --- a/sample/sample.config.toml +++ b/sample/sample.config.toml @@ -22,4 +22,7 @@ serve_ephemeral = true serve_relay_lists = true server_log_level = "Info" library_log_level = "Info" -client_log_level = "Warn" \ No newline at end of file +client_log_level = "Warn" +enable_ip_blocking = true +minimum_ban_seconds = 1 +timeout_seconds = 30