diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 0baba84f..2b92eac0 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -214,6 +214,15 @@ registration_token = "change this token for something specific to your server" # No default. # forbidden_alias_names = [] +# List of forbidden server names that we will block all client room joins, incoming federated room directory requests, incoming federated invites for, and incoming federated joins. This check is applied on the room ID, room alias, sender server name, and sender user's server name. +# Basically "global" ACLs. For our user (client) checks, admin users are allowed. +# No default. +# forbidden_remote_server_names = [] + +# List of forbidden server names that we will block all outgoing federated room directory requests for. Useful for preventing our users from wandering into bad servers or spaces. +# No default. +# forbidden_remote_room_directory_server_names = [] + # Set this to true to allow your server's public room directory to be federated. # Set this to false to protect against /publicRooms spiders, but will forbid external users # from viewing your server's public room directory. If federation is disabled entirely diff --git a/src/api/client_server/directory.rs b/src/api/client_server/directory.rs index c9bdfc53..2ec9bf67 100644 --- a/src/api/client_server/directory.rs +++ b/src/api/client_server/directory.rs @@ -34,6 +34,19 @@ use crate::{services, Error, Result, Ruma}; pub async fn get_public_rooms_filtered_route( body: Ruma, ) -> Result { + if let Some(server) = &body.server { + if services() + .globals + .forbidden_remote_room_directory_server_names() + .contains(server) + { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Server is banned on this homeserver.", + )); + } + } + let response = get_public_rooms_filtered_helper( body.server.as_deref(), body.limit, @@ -58,6 +71,19 @@ pub async fn get_public_rooms_filtered_route( pub async fn get_public_rooms_route( body: Ruma, ) -> Result { + if let Some(server) = &body.server { + if services() + .globals + .forbidden_remote_room_directory_server_names() + .contains(server) + { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Server is banned on this homeserver.", + )); + } + } + let response = get_public_rooms_filtered_helper( body.server.as_deref(), body.limit, diff --git a/src/api/client_server/membership.rs b/src/api/client_server/membership.rs index f31bc026..28dd797a 100644 --- a/src/api/client_server/membership.rs +++ b/src/api/client_server/membership.rs @@ -55,6 +55,26 @@ pub async fn join_room_by_id_route(body: Ruma) -> )); } + if let Some(server) = body.room_id.server_name() { + if services() + .globals + .config + .forbidden_remote_server_names + .contains(&server.to_owned()) + && !services().users.is_admin(sender_user)? + { + warn!( + "User {sender_user} tried joining room ID {} which has a server name that is globally forbidden. \ + Rejecting.", + body.room_id + ); + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "This remote server is banned on this homeserver.", + )); + } + } + // There is no body.server_name for /roomId/join let mut servers = services() .rooms @@ -112,6 +132,25 @@ pub async fn join_room_by_id_or_alias_route( )); } + if let Some(server) = room_id.server_name() { + if services() + .globals + .config + .forbidden_remote_server_names + .contains(&server.to_owned()) + && !services().users.is_admin(sender_user)? + { + warn!( + "User {sender_user} tried joining room ID {room_id} which has a server name that is globally \ + forbidden. Rejecting.", + ); + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "This remote server is banned on this homeserver.", + )); + } + } + let mut servers = body.server_name.clone(); servers.extend( @@ -136,13 +175,13 @@ pub async fn join_room_by_id_or_alias_route( ); if let Some(server) = room_id.server_name() { - servers.push(server.into()); + servers.push(server.to_owned()); } (servers, room_id) }, Err(room_alias) => { - let response = get_alias_helper(room_alias).await?; + let response = get_alias_helper(room_alias.clone()).await?; if services().rooms.metadata.is_banned(&response.room_id)? && !services().users.is_admin(sender_user)? { return Err(Error::BadRequest( @@ -151,6 +190,44 @@ pub async fn join_room_by_id_or_alias_route( )); } + if services() + .globals + .config + .forbidden_remote_server_names + .contains(&room_alias.server_name().to_owned()) + && !services().users.is_admin(sender_user)? + { + warn!( + "User {sender_user} tried joining room alias {} with room ID {} which has a server name that is \ + globally forbidden. Rejecting.", + &room_alias, &response.room_id + ); + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "This remote server is banned on this homeserver.", + )); + } + + if let Some(server) = response.room_id.server_name() { + if services() + .globals + .config + .forbidden_remote_server_names + .contains(&server.to_owned()) + && !services().users.is_admin(sender_user)? + { + warn!( + "User {sender_user} tried joining room alias {} with room ID {} which has a server name that \ + is globally forbidden. Rejecting.", + &room_alias, &response.room_id + ); + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "This remote server is banned on this homeserver.", + )); + } + } + (response.servers, response.room_id) }, }; @@ -210,6 +287,20 @@ pub async fn invite_user_route(body: Ruma) -> Result) -> Resu )); } + if let Some(server) = body.room_id.server_name() { + if services() + .globals + .config + .forbidden_remote_server_names + .contains(&server.to_owned()) + { + warn!( + "Received federated/remote invite from banned server {sender_servername} for room ID {}. Rejecting.", + body.room_id + ); + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Server is banned on this homeserver.", + )); + } + } + + if services() + .globals + .config + .forbidden_remote_server_names + .contains(&sender_servername.to_owned()) + { + warn!( + "Received federated/remote invite from banned server {sender_servername} for room ID {}. Rejecting.", + body.room_id + ); + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Server is banned on this homeserver.", + )); + } + if let Some(via) = &body.via { if via.is_empty() { return Err(Error::BadRequest(ErrorKind::InvalidParam, "via field must not be empty.")); diff --git a/src/config/mod.rs b/src/config/mod.rs index 0a616be1..df4a9335 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -269,6 +269,10 @@ pub struct Config { #[serde(default = "Vec::new")] pub prevent_media_downloads_from: Vec, + #[serde(default = "Vec::new")] + pub forbidden_remote_server_names: Vec, + #[serde(default = "Vec::new")] + pub forbidden_remote_room_directory_server_names: Vec, #[serde(default = "default_ip_range_denylist")] pub ip_range_denylist: Vec, @@ -689,6 +693,20 @@ impl fmt::Display for Config { } &lst.join(", ") }), + ("Forbidden Remote Server Names (\"Global\" ACLs)", { + let mut lst = vec![]; + for domain in &self.forbidden_remote_server_names { + lst.push(domain.host()); + } + &lst.join(", ") + }), + ("Forbidden Remote Room Directory Server Names", { + let mut lst = vec![]; + for domain in &self.forbidden_remote_room_directory_server_names { + lst.push(domain.host()); + } + &lst.join(", ") + }), ("Outbound Request IP Range Denylist", { let mut lst = vec![]; for item in self.ip_range_denylist.iter().cloned().enumerate() { diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index 307aaff3..874ba22e 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -323,6 +323,12 @@ impl Service<'_> { pub fn prevent_media_downloads_from(&self) -> &[OwnedServerName] { &self.config.prevent_media_downloads_from } + pub fn forbidden_remote_server_names(&self) -> &[OwnedServerName] { &self.config.forbidden_remote_server_names } + + pub fn forbidden_remote_room_directory_server_names(&self) -> &[OwnedServerName] { + &self.config.forbidden_remote_room_directory_server_names + } + pub fn ip_range_denylist(&self) -> &[String] { &self.config.ip_range_denylist } pub fn well_known_support_page(&self) -> &Option { &self.config.well_known.support_page }