From 371103fb3536ae317e5a5d2c9db4d432621c122a Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Sat, 18 Jan 2025 15:21:55 +0000 Subject: [PATCH 1/4] add federation allow list --- src/api/client/membership.rs | 13 ++------ src/api/client/message.rs | 9 ++---- src/api/router/auth.rs | 7 +--- src/api/server/invite.rs | 13 ++------ src/api/server/make_join.rs | 13 ++------ src/api/server/make_knock.rs | 13 ++------ src/api/server/send_join.rs | 26 ++++----------- src/api/server/send_knock.rs | 13 ++------ src/core/config/mod.rs | 10 ++++++ src/service/federation/execute.rs | 8 +---- src/service/federation/mod.rs | 4 ++- src/service/mod.rs | 1 + src/service/moderation/mod.rs | 54 +++++++++++++++++++++++++++++++ src/service/sending/mod.rs | 5 +-- src/service/services.rs | 6 ++-- 15 files changed, 101 insertions(+), 94 deletions(-) create mode 100644 src/service/moderation/mod.rs diff --git a/src/api/client/membership.rs b/src/api/client/membership.rs index fccb9b53a..760135a72 100644 --- a/src/api/client/membership.rs +++ b/src/api/client/membership.rs @@ -71,10 +71,8 @@ async fn banned_room_check( if let Some(room_id) = room_id { if services.rooms.metadata.is_banned(room_id).await || services - .server - .config - .forbidden_remote_server_names - .contains(&room_id.server_name().unwrap().to_owned()) + .moderation + .is_remote_server_forbidden(room_id.server_name().unwrap()) { warn!( "User {user_id} who is not an admin attempted to send an invite for or \ @@ -111,12 +109,7 @@ async fn banned_room_check( return Err!(Request(Forbidden("This room is banned on this homeserver."))); } } else if let Some(server_name) = server_name { - if services - .server - .config - .forbidden_remote_server_names - .contains(&server_name.to_owned()) - { + if services.moderation.is_remote_server_forbidden(server_name) { warn!( "User {user_id} who is not an admin tried joining a room which has the server \ name {server_name} that is globally forbidden. Rejecting.", diff --git a/src/api/client/message.rs b/src/api/client/message.rs index 321d80131..68cf1a446 100644 --- a/src/api/client/message.rs +++ b/src/api/client/message.rs @@ -1,6 +1,6 @@ use axum::extract::State; use conduwuit::{ - at, is_equal_to, + at, utils::{ result::{FlatOk, LogErr}, stream::{BroadbandExt, TryIgnore, WidebandExt}, @@ -241,11 +241,8 @@ pub(crate) async fn ignored_filter( if IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok() && (services.users.user_is_ignored(&pdu.sender, user_id).await || services - .server - .config - .forbidden_remote_server_names - .iter() - .any(is_equal_to!(pdu.sender().server_name()))) + .moderation + .is_remote_server_forbidden(pdu.sender().server_name())) { return None; } diff --git a/src/api/router/auth.rs b/src/api/router/auth.rs index ecea305b9..de30de16a 100644 --- a/src/api/router/auth.rs +++ b/src/api/router/auth.rs @@ -317,12 +317,7 @@ fn auth_server_checks(services: &Services, x_matrix: &XMatrix) -> Result<()> { } let origin = &x_matrix.origin; - if services - .server - .config - .forbidden_remote_server_names - .contains(origin) - { + if services.moderation.is_remote_server_forbidden(origin) { return Err!(Request(Forbidden(debug_warn!( "Federation requests from {origin} denied." )))); diff --git a/src/api/server/invite.rs b/src/api/server/invite.rs index 27a4485ce..71a6861e5 100644 --- a/src/api/server/invite.rs +++ b/src/api/server/invite.rs @@ -36,21 +36,14 @@ pub(crate) async fn create_invite_route( } if let Some(server) = body.room_id.server_name() { - if services - .server - .config - .forbidden_remote_server_names - .contains(&server.to_owned()) - { + if services.moderation.is_remote_server_forbidden(server) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } } if services - .server - .config - .forbidden_remote_server_names - .contains(body.origin()) + .moderation + .is_remote_server_forbidden(body.origin()) { warn!( "Received federated/remote invite from banned server {} for room ID {}. Rejecting.", diff --git a/src/api/server/make_join.rs b/src/api/server/make_join.rs index b753346c4..f0e45706e 100644 --- a/src/api/server/make_join.rs +++ b/src/api/server/make_join.rs @@ -42,10 +42,8 @@ pub(crate) async fn create_join_event_template_route( .await?; if services - .server - .config - .forbidden_remote_server_names - .contains(body.origin()) + .moderation + .is_remote_server_forbidden(body.origin()) { warn!( "Server {} for remote user {} tried joining room ID {} which has a server name that \ @@ -58,12 +56,7 @@ pub(crate) async fn create_join_event_template_route( } if let Some(server) = body.room_id.server_name() { - if services - .server - .config - .forbidden_remote_server_names - .contains(&server.to_owned()) - { + if services.moderation.is_remote_server_forbidden(server) { return Err!(Request(Forbidden(warn!( "Room ID server name {server} is banned on this homeserver." )))); diff --git a/src/api/server/make_knock.rs b/src/api/server/make_knock.rs index 423e202de..5175ebaac 100644 --- a/src/api/server/make_knock.rs +++ b/src/api/server/make_knock.rs @@ -34,10 +34,8 @@ pub(crate) async fn create_knock_event_template_route( .await?; if services - .server - .config - .forbidden_remote_server_names - .contains(body.origin()) + .moderation + .is_remote_server_forbidden(body.origin()) { warn!( "Server {} for remote user {} tried knocking room ID {} which has a server name \ @@ -50,12 +48,7 @@ pub(crate) async fn create_knock_event_template_route( } if let Some(server) = body.room_id.server_name() { - if services - .server - .config - .forbidden_remote_server_names - .contains(&server.to_owned()) - { + if services.moderation.is_remote_server_forbidden(server) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } } diff --git a/src/api/server/send_join.rs b/src/api/server/send_join.rs index 2b8a0eef9..756d0e6f4 100644 --- a/src/api/server/send_join.rs +++ b/src/api/server/send_join.rs @@ -270,10 +270,8 @@ pub(crate) async fn create_join_event_v1_route( body: Ruma, ) -> Result { if services - .server - .config - .forbidden_remote_server_names - .contains(body.origin()) + .moderation + .is_remote_server_forbidden(body.origin()) { warn!( "Server {} tried joining room ID {} through us who has a server name that is \ @@ -285,12 +283,7 @@ pub(crate) async fn create_join_event_v1_route( } if let Some(server) = body.room_id.server_name() { - if services - .server - .config - .forbidden_remote_server_names - .contains(&server.to_owned()) - { + if services.moderation.is_remote_server_forbidden(server) { warn!( "Server {} tried joining room ID {} through us which has a server name that is \ globally forbidden. Rejecting.", @@ -318,21 +311,14 @@ pub(crate) async fn create_join_event_v2_route( body: Ruma, ) -> Result { if services - .server - .config - .forbidden_remote_server_names - .contains(body.origin()) + .moderation + .is_remote_server_forbidden(body.origin()) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } if let Some(server) = body.room_id.server_name() { - if services - .server - .config - .forbidden_remote_server_names - .contains(&server.to_owned()) - { + if services.moderation.is_remote_server_forbidden(server) { warn!( "Server {} tried joining room ID {} through us which has a server name that is \ globally forbidden. Rejecting.", diff --git a/src/api/server/send_knock.rs b/src/api/server/send_knock.rs index b07620af2..3e9454282 100644 --- a/src/api/server/send_knock.rs +++ b/src/api/server/send_knock.rs @@ -22,10 +22,8 @@ pub(crate) async fn create_knock_event_v1_route( body: Ruma, ) -> Result { if services - .server - .config - .forbidden_remote_server_names - .contains(body.origin()) + .moderation + .is_remote_server_forbidden(body.origin()) { warn!( "Server {} tried knocking room ID {} who has a server name that is globally \ @@ -37,12 +35,7 @@ pub(crate) async fn create_knock_event_v1_route( } if let Some(server) = body.room_id.server_name() { - if services - .server - .config - .forbidden_remote_server_names - .contains(&server.to_owned()) - { + if services.moderation.is_remote_server_forbidden(server) { warn!( "Server {} tried knocking room ID {} which has a server name that is globally \ forbidden. Rejecting.", diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 415c9ba94..f6b08bc0e 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1335,12 +1335,22 @@ pub struct Config { /// sender user's server name, inbound federation X-Matrix origin, and /// outbound federation handler. /// + /// Additionally, it will hide messages from these servers for all users + /// on this server. + /// /// Basically "global" ACLs. /// /// default: [] #[serde(default)] pub forbidden_remote_server_names: HashSet, + /// The inverse of `forbidden_remote_server_names`. By default, allows all + /// servers. `forbidden_remote_server_names` takes precidence. + /// + /// default: [] + #[serde(default)] + pub allowed_remote_server_names: HashSet, + /// 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. diff --git a/src/service/federation/execute.rs b/src/service/federation/execute.rs index 3146bb8a7..f29ffda38 100644 --- a/src/service/federation/execute.rs +++ b/src/service/federation/execute.rs @@ -65,13 +65,7 @@ where return Err!(Config("allow_federation", "Federation is disabled.")); } - if self - .services - .server - .config - .forbidden_remote_server_names - .contains(dest) - { + if self.services.moderation.is_remote_server_forbidden(dest) { return Err!(Request(Forbidden(debug_warn!("Federation with {dest} is not allowed.")))); } diff --git a/src/service/federation/mod.rs b/src/service/federation/mod.rs index dacdb20e0..258af87f3 100644 --- a/src/service/federation/mod.rs +++ b/src/service/federation/mod.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use conduwuit::{Result, Server}; -use crate::{client, resolver, server_keys, Dep}; +use crate::{client, moderation, resolver, server_keys, Dep}; pub struct Service { services: Services, @@ -15,6 +15,7 @@ struct Services { client: Dep, resolver: Dep, server_keys: Dep, + moderation: Dep, } impl crate::Service for Service { @@ -25,6 +26,7 @@ impl crate::Service for Service { client: args.depend::("client"), resolver: args.depend::("resolver"), server_keys: args.depend::("server_keys"), + moderation: args.depend::("moderation"), }, })) } diff --git a/src/service/mod.rs b/src/service/mod.rs index 71bd0eb48..937aeb6a8 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -15,6 +15,7 @@ pub mod federation; pub mod globals; pub mod key_backups; pub mod media; +pub mod moderation; pub mod presence; pub mod pusher; pub mod resolver; diff --git a/src/service/moderation/mod.rs b/src/service/moderation/mod.rs new file mode 100644 index 000000000..7b984a0fe --- /dev/null +++ b/src/service/moderation/mod.rs @@ -0,0 +1,54 @@ +use std::sync::Arc; + +use conduwuit::{implement, Result, Server}; +use ruma::ServerName; + +use crate::{globals, Dep}; + +pub struct Service { + services: Services, +} + +struct Services { + pub server: Arc, + pub globals: Dep, +} + +impl crate::Service for Service { + fn build(args: crate::Args<'_>) -> Result> { + Ok(Arc::new(Self { + services: Services { + server: args.server.clone(), + globals: args.depend::("globals"), + }, + })) + } + + fn name(&self) -> &str { crate::service::make_name(std::module_path!()) } +} + +#[implement(Service)] +#[must_use] +pub fn is_remote_server_forbidden(&self, server_name: &ServerName) -> bool { + // Forbidden if NOT (allowed is empty OR allowed contains server OR is self) + // OR forbidden contains server + !(self + .services + .server + .config + .allowed_remote_server_names + .is_empty() + || self + .services + .server + .config + .allowed_remote_server_names + .contains(server_name) + || server_name == self.services.globals.server_name()) + || self + .services + .server + .config + .forbidden_remote_server_names + .contains(server_name) +} diff --git a/src/service/sending/mod.rs b/src/service/sending/mod.rs index b146ad493..61661245d 100644 --- a/src/service/sending/mod.rs +++ b/src/service/sending/mod.rs @@ -30,8 +30,9 @@ pub use self::{ sender::{EDU_LIMIT, PDU_LIMIT}, }; use crate::{ - account_data, client, federation, globals, presence, pusher, rooms, - rooms::timeline::RawPduId, users, Dep, + account_data, client, federation, globals, presence, pusher, + rooms::{self, timeline::RawPduId}, + users, Dep, }; pub struct Service { diff --git a/src/service/services.rs b/src/service/services.rs index fb334b969..35e9c2b4a 100644 --- a/src/service/services.rs +++ b/src/service/services.rs @@ -12,8 +12,8 @@ use tokio::sync::Mutex; use crate::{ account_data, admin, appservice, client, config, emergency, federation, globals, key_backups, manager::Manager, - media, presence, pusher, resolver, rooms, sending, server_keys, service, - service::{Args, Map, Service}, + media, moderation, presence, pusher, resolver, rooms, sending, server_keys, + service::{self, Args, Map, Service}, sync, transaction_ids, uiaa, updates, users, }; @@ -31,6 +31,7 @@ pub struct Services { pub pusher: Arc, pub resolver: Arc, pub rooms: rooms::Service, + pub moderation: Arc, pub federation: Arc, pub sending: Arc, pub server_keys: Arc, @@ -98,6 +99,7 @@ impl Services { typing: build!(rooms::typing::Service), user: build!(rooms::user::Service), }, + moderation: build!(moderation::Service), federation: build!(federation::Service), sending: build!(sending::Service), server_keys: build!(server_keys::Service), From 81a797945b19e94cfcbfeb5699133003c05db025 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Sat, 18 Jan 2025 15:40:43 +0000 Subject: [PATCH 2/4] forbid requesting room directories or media of forbidden servers --- src/api/client/directory.rs | 2 ++ src/core/config/mod.rs | 4 ++++ src/service/media/mod.rs | 4 +++- src/service/media/remote.rs | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/api/client/directory.rs b/src/api/client/directory.rs index 9166eed90..1018469db 100644 --- a/src/api/client/directory.rs +++ b/src/api/client/directory.rs @@ -45,6 +45,7 @@ pub(crate) async fn get_public_rooms_filtered_route( .config .forbidden_remote_room_directory_server_names .contains(server) + || services.moderation.is_remote_server_forbidden(server) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } @@ -87,6 +88,7 @@ pub(crate) async fn get_public_rooms_route( .config .forbidden_remote_room_directory_server_names .contains(server) + || services.moderation.is_remote_server_forbidden(server) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index f6b08bc0e..b1ad45622 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1324,6 +1324,8 @@ pub struct Config { /// Vector list of servers that conduwuit will refuse to download remote /// media from. /// + /// This is in addition to `forbidden_remote_server_names`. + /// /// default: [] #[serde(default)] pub prevent_media_downloads_from: HashSet, @@ -1355,6 +1357,8 @@ pub struct Config { /// room directory requests for. Useful for preventing our users from /// wandering into bad servers or spaces. /// + /// This is in addition to `forbidden_remote_server_names`. + /// /// default: [] #[serde(default = "HashSet::new")] pub forbidden_remote_room_directory_server_names: HashSet, diff --git a/src/service/media/mod.rs b/src/service/media/mod.rs index 0d98853d1..66638e4b0 100644 --- a/src/service/media/mod.rs +++ b/src/service/media/mod.rs @@ -22,7 +22,7 @@ use tokio::{ use self::data::{Data, Metadata}; pub use self::thumbnail::Dim; -use crate::{client, globals, sending, Dep}; +use crate::{client, globals, moderation, sending, Dep}; #[derive(Debug)] pub struct FileMeta { @@ -42,6 +42,7 @@ struct Services { client: Dep, globals: Dep, sending: Dep, + moderation: Dep, } /// generated MXC ID (`media-id`) length @@ -64,6 +65,7 @@ impl crate::Service for Service { client: args.depend::("client"), globals: args.depend::("globals"), sending: args.depend::("sending"), + moderation: args.depend::("moderation"), }, })) } diff --git a/src/service/media/remote.rs b/src/service/media/remote.rs index ca73c3efe..089c028d3 100644 --- a/src/service/media/remote.rs +++ b/src/service/media/remote.rs @@ -427,6 +427,10 @@ fn check_fetch_authorized(&self, mxc: &Mxc<'_>) -> Result<()> { .config .prevent_media_downloads_from .contains(mxc.server_name) + || self + .services + .moderation + .is_remote_server_forbidden(mxc.server_name) { // we'll lie to the client and say the blocked server's media was not found and // log. the client has no way of telling anyways so this is a security bonus. From 5fdb33e2c02137ed1637e0871225dbca06f5e1b1 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Sat, 18 Jan 2025 18:45:31 +0000 Subject: [PATCH 3/4] generate example config --- conduwuit-example.toml | 12 ++++++++++++ src/core/config/mod.rs | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 4062ba993..7040e8b28 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1160,6 +1160,8 @@ # Vector list of servers that conduwuit will refuse to download remote # media from. # +# This is in addition to `forbidden_remote_server_names`. +# #prevent_media_downloads_from = [] # List of forbidden server names that we will block incoming AND outgoing @@ -1169,14 +1171,24 @@ # sender user's server name, inbound federation X-Matrix origin, and # outbound federation handler. # +# Additionally, it will hide messages from these servers for all users +# on this server. +# # Basically "global" ACLs. # #forbidden_remote_server_names = [] +# The inverse of `forbidden_remote_server_names`. By default, allows all +# servers. `forbidden_remote_server_names` takes precedence. +# +#allowed_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. # +# This is in addition to `forbidden_remote_server_names`. +# #forbidden_remote_room_directory_server_names = [] # Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index b1ad45622..e9fc4a36a 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1347,7 +1347,7 @@ pub struct Config { pub forbidden_remote_server_names: HashSet, /// The inverse of `forbidden_remote_server_names`. By default, allows all - /// servers. `forbidden_remote_server_names` takes precidence. + /// servers. `forbidden_remote_server_names` takes precedence. /// /// default: [] #[serde(default)] From 8cf8af723d490d55ed3a5123783085c88a21ffce Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Sat, 18 Jan 2025 16:16:16 +0000 Subject: [PATCH 4/4] fix incorrect capacity calc --- src/core/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index e9fc4a36a..9d3135ea2 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1885,7 +1885,7 @@ impl Config { let mut addrs = Vec::with_capacity( self.get_bind_hosts() .len() - .saturating_add(self.get_bind_ports().len()), + .saturating_mul(self.get_bind_ports().len()), ); for host in &self.get_bind_hosts() { for port in &self.get_bind_ports() {