Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
Jade Ellis
8cf8af723d
fix incorrect capacity calc 2025-01-29 19:50:26 +00:00
Jade Ellis
5fdb33e2c0
generate example config 2025-01-29 19:49:04 +00:00
Jade Ellis
81a797945b
forbid requesting room directories or media of forbidden servers 2025-01-29 19:49:04 +00:00
Jade Ellis
371103fb35
add federation allow list 2025-01-29 19:48:59 +00:00
19 changed files with 127 additions and 96 deletions

View file

@ -1160,6 +1160,8 @@
# Vector list of servers that conduwuit will refuse to download remote # Vector list of servers that conduwuit will refuse to download remote
# media from. # media from.
# #
# This is in addition to `forbidden_remote_server_names`.
#
#prevent_media_downloads_from = [] #prevent_media_downloads_from = []
# List of forbidden server names that we will block incoming AND outgoing # 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 # sender user's server name, inbound federation X-Matrix origin, and
# outbound federation handler. # outbound federation handler.
# #
# Additionally, it will hide messages from these servers for all users
# on this server.
#
# Basically "global" ACLs. # Basically "global" ACLs.
# #
#forbidden_remote_server_names = [] #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 # List of forbidden server names that we will block all outgoing federated
# room directory requests for. Useful for preventing our users from # room directory requests for. Useful for preventing our users from
# wandering into bad servers or spaces. # wandering into bad servers or spaces.
# #
# This is in addition to `forbidden_remote_server_names`.
#
#forbidden_remote_room_directory_server_names = [] #forbidden_remote_room_directory_server_names = []
# Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you # Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you

View file

@ -45,6 +45,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
.config .config
.forbidden_remote_room_directory_server_names .forbidden_remote_room_directory_server_names
.contains(server) .contains(server)
|| services.moderation.is_remote_server_forbidden(server)
{ {
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
@ -87,6 +88,7 @@ pub(crate) async fn get_public_rooms_route(
.config .config
.forbidden_remote_room_directory_server_names .forbidden_remote_room_directory_server_names
.contains(server) .contains(server)
|| services.moderation.is_remote_server_forbidden(server)
{ {
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }

View file

@ -71,10 +71,8 @@ async fn banned_room_check(
if let Some(room_id) = room_id { if let Some(room_id) = room_id {
if services.rooms.metadata.is_banned(room_id).await if services.rooms.metadata.is_banned(room_id).await
|| services || services
.server .moderation
.config .is_remote_server_forbidden(room_id.server_name().unwrap())
.forbidden_remote_server_names
.contains(&room_id.server_name().unwrap().to_owned())
{ {
warn!( warn!(
"User {user_id} who is not an admin attempted to send an invite for or \ "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."))); return Err!(Request(Forbidden("This room is banned on this homeserver.")));
} }
} else if let Some(server_name) = server_name { } else if let Some(server_name) = server_name {
if services if services.moderation.is_remote_server_forbidden(server_name) {
.server
.config
.forbidden_remote_server_names
.contains(&server_name.to_owned())
{
warn!( warn!(
"User {user_id} who is not an admin tried joining a room which has the server \ "User {user_id} who is not an admin tried joining a room which has the server \
name {server_name} that is globally forbidden. Rejecting.", name {server_name} that is globally forbidden. Rejecting.",

View file

@ -1,6 +1,6 @@
use axum::extract::State; use axum::extract::State;
use conduwuit::{ use conduwuit::{
at, is_equal_to, at,
utils::{ utils::{
result::{FlatOk, LogErr}, result::{FlatOk, LogErr},
stream::{BroadbandExt, TryIgnore, WidebandExt}, stream::{BroadbandExt, TryIgnore, WidebandExt},
@ -241,11 +241,8 @@ pub(crate) async fn ignored_filter(
if IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok() if IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok()
&& (services.users.user_is_ignored(&pdu.sender, user_id).await && (services.users.user_is_ignored(&pdu.sender, user_id).await
|| services || services
.server .moderation
.config .is_remote_server_forbidden(pdu.sender().server_name()))
.forbidden_remote_server_names
.iter()
.any(is_equal_to!(pdu.sender().server_name())))
{ {
return None; return None;
} }

View file

@ -317,12 +317,7 @@ fn auth_server_checks(services: &Services, x_matrix: &XMatrix) -> Result<()> {
} }
let origin = &x_matrix.origin; let origin = &x_matrix.origin;
if services if services.moderation.is_remote_server_forbidden(origin) {
.server
.config
.forbidden_remote_server_names
.contains(origin)
{
return Err!(Request(Forbidden(debug_warn!( return Err!(Request(Forbidden(debug_warn!(
"Federation requests from {origin} denied." "Federation requests from {origin} denied."
)))); ))));

View file

@ -36,21 +36,14 @@ pub(crate) async fn create_invite_route(
} }
if let Some(server) = body.room_id.server_name() { if let Some(server) = body.room_id.server_name() {
if services if services.moderation.is_remote_server_forbidden(server) {
.server
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
} }
if services if services
.server .moderation
.config .is_remote_server_forbidden(body.origin())
.forbidden_remote_server_names
.contains(body.origin())
{ {
warn!( warn!(
"Received federated/remote invite from banned server {} for room ID {}. Rejecting.", "Received federated/remote invite from banned server {} for room ID {}. Rejecting.",

View file

@ -42,10 +42,8 @@ pub(crate) async fn create_join_event_template_route(
.await?; .await?;
if services if services
.server .moderation
.config .is_remote_server_forbidden(body.origin())
.forbidden_remote_server_names
.contains(body.origin())
{ {
warn!( warn!(
"Server {} for remote user {} tried joining room ID {} which has a server name that \ "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 let Some(server) = body.room_id.server_name() {
if services if services.moderation.is_remote_server_forbidden(server) {
.server
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
return Err!(Request(Forbidden(warn!( return Err!(Request(Forbidden(warn!(
"Room ID server name {server} is banned on this homeserver." "Room ID server name {server} is banned on this homeserver."
)))); ))));

View file

@ -34,10 +34,8 @@ pub(crate) async fn create_knock_event_template_route(
.await?; .await?;
if services if services
.server .moderation
.config .is_remote_server_forbidden(body.origin())
.forbidden_remote_server_names
.contains(body.origin())
{ {
warn!( warn!(
"Server {} for remote user {} tried knocking room ID {} which has a server name \ "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 let Some(server) = body.room_id.server_name() {
if services if services.moderation.is_remote_server_forbidden(server) {
.server
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
} }

View file

@ -270,10 +270,8 @@ pub(crate) async fn create_join_event_v1_route(
body: Ruma<create_join_event::v1::Request>, body: Ruma<create_join_event::v1::Request>,
) -> Result<create_join_event::v1::Response> { ) -> Result<create_join_event::v1::Response> {
if services if services
.server .moderation
.config .is_remote_server_forbidden(body.origin())
.forbidden_remote_server_names
.contains(body.origin())
{ {
warn!( warn!(
"Server {} tried joining room ID {} through us who has a server name that is \ "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 let Some(server) = body.room_id.server_name() {
if services if services.moderation.is_remote_server_forbidden(server) {
.server
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
warn!( warn!(
"Server {} tried joining room ID {} through us which has a server name that is \ "Server {} tried joining room ID {} through us which has a server name that is \
globally forbidden. Rejecting.", globally forbidden. Rejecting.",
@ -318,21 +311,14 @@ pub(crate) async fn create_join_event_v2_route(
body: Ruma<create_join_event::v2::Request>, body: Ruma<create_join_event::v2::Request>,
) -> Result<create_join_event::v2::Response> { ) -> Result<create_join_event::v2::Response> {
if services if services
.server .moderation
.config .is_remote_server_forbidden(body.origin())
.forbidden_remote_server_names
.contains(body.origin())
{ {
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
if let Some(server) = body.room_id.server_name() { if let Some(server) = body.room_id.server_name() {
if services if services.moderation.is_remote_server_forbidden(server) {
.server
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
warn!( warn!(
"Server {} tried joining room ID {} through us which has a server name that is \ "Server {} tried joining room ID {} through us which has a server name that is \
globally forbidden. Rejecting.", globally forbidden. Rejecting.",

View file

@ -22,10 +22,8 @@ pub(crate) async fn create_knock_event_v1_route(
body: Ruma<send_knock::v1::Request>, body: Ruma<send_knock::v1::Request>,
) -> Result<send_knock::v1::Response> { ) -> Result<send_knock::v1::Response> {
if services if services
.server .moderation
.config .is_remote_server_forbidden(body.origin())
.forbidden_remote_server_names
.contains(body.origin())
{ {
warn!( warn!(
"Server {} tried knocking room ID {} who has a server name that is globally \ "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 let Some(server) = body.room_id.server_name() {
if services if services.moderation.is_remote_server_forbidden(server) {
.server
.config
.forbidden_remote_server_names
.contains(&server.to_owned())
{
warn!( warn!(
"Server {} tried knocking room ID {} which has a server name that is globally \ "Server {} tried knocking room ID {} which has a server name that is globally \
forbidden. Rejecting.", forbidden. Rejecting.",

View file

@ -1324,6 +1324,8 @@ pub struct Config {
/// Vector list of servers that conduwuit will refuse to download remote /// Vector list of servers that conduwuit will refuse to download remote
/// media from. /// media from.
/// ///
/// This is in addition to `forbidden_remote_server_names`.
///
/// default: [] /// default: []
#[serde(default)] #[serde(default)]
pub prevent_media_downloads_from: HashSet<OwnedServerName>, pub prevent_media_downloads_from: HashSet<OwnedServerName>,
@ -1335,16 +1337,28 @@ pub struct Config {
/// sender user's server name, inbound federation X-Matrix origin, and /// sender user's server name, inbound federation X-Matrix origin, and
/// outbound federation handler. /// outbound federation handler.
/// ///
/// Additionally, it will hide messages from these servers for all users
/// on this server.
///
/// Basically "global" ACLs. /// Basically "global" ACLs.
/// ///
/// default: [] /// default: []
#[serde(default)] #[serde(default)]
pub forbidden_remote_server_names: HashSet<OwnedServerName>, pub forbidden_remote_server_names: HashSet<OwnedServerName>,
/// The inverse of `forbidden_remote_server_names`. By default, allows all
/// servers. `forbidden_remote_server_names` takes precedence.
///
/// default: []
#[serde(default)]
pub allowed_remote_server_names: HashSet<OwnedServerName>,
/// List of forbidden server names that we will block all outgoing federated /// List of forbidden server names that we will block all outgoing federated
/// room directory requests for. Useful for preventing our users from /// room directory requests for. Useful for preventing our users from
/// wandering into bad servers or spaces. /// wandering into bad servers or spaces.
/// ///
/// This is in addition to `forbidden_remote_server_names`.
///
/// default: [] /// default: []
#[serde(default = "HashSet::new")] #[serde(default = "HashSet::new")]
pub forbidden_remote_room_directory_server_names: HashSet<OwnedServerName>, pub forbidden_remote_room_directory_server_names: HashSet<OwnedServerName>,
@ -1871,7 +1885,7 @@ impl Config {
let mut addrs = Vec::with_capacity( let mut addrs = Vec::with_capacity(
self.get_bind_hosts() self.get_bind_hosts()
.len() .len()
.saturating_add(self.get_bind_ports().len()), .saturating_mul(self.get_bind_ports().len()),
); );
for host in &self.get_bind_hosts() { for host in &self.get_bind_hosts() {
for port in &self.get_bind_ports() { for port in &self.get_bind_ports() {

View file

@ -65,13 +65,7 @@ where
return Err!(Config("allow_federation", "Federation is disabled.")); return Err!(Config("allow_federation", "Federation is disabled."));
} }
if self if self.services.moderation.is_remote_server_forbidden(dest) {
.services
.server
.config
.forbidden_remote_server_names
.contains(dest)
{
return Err!(Request(Forbidden(debug_warn!("Federation with {dest} is not allowed.")))); return Err!(Request(Forbidden(debug_warn!("Federation with {dest} is not allowed."))));
} }

View file

@ -4,7 +4,7 @@ use std::sync::Arc;
use conduwuit::{Result, Server}; use conduwuit::{Result, Server};
use crate::{client, resolver, server_keys, Dep}; use crate::{client, moderation, resolver, server_keys, Dep};
pub struct Service { pub struct Service {
services: Services, services: Services,
@ -15,6 +15,7 @@ struct Services {
client: Dep<client::Service>, client: Dep<client::Service>,
resolver: Dep<resolver::Service>, resolver: Dep<resolver::Service>,
server_keys: Dep<server_keys::Service>, server_keys: Dep<server_keys::Service>,
moderation: Dep<moderation::Service>,
} }
impl crate::Service for Service { impl crate::Service for Service {
@ -25,6 +26,7 @@ impl crate::Service for Service {
client: args.depend::<client::Service>("client"), client: args.depend::<client::Service>("client"),
resolver: args.depend::<resolver::Service>("resolver"), resolver: args.depend::<resolver::Service>("resolver"),
server_keys: args.depend::<server_keys::Service>("server_keys"), server_keys: args.depend::<server_keys::Service>("server_keys"),
moderation: args.depend::<moderation::Service>("moderation"),
}, },
})) }))
} }

View file

@ -22,7 +22,7 @@ use tokio::{
use self::data::{Data, Metadata}; use self::data::{Data, Metadata};
pub use self::thumbnail::Dim; pub use self::thumbnail::Dim;
use crate::{client, globals, sending, Dep}; use crate::{client, globals, moderation, sending, Dep};
#[derive(Debug)] #[derive(Debug)]
pub struct FileMeta { pub struct FileMeta {
@ -42,6 +42,7 @@ struct Services {
client: Dep<client::Service>, client: Dep<client::Service>,
globals: Dep<globals::Service>, globals: Dep<globals::Service>,
sending: Dep<sending::Service>, sending: Dep<sending::Service>,
moderation: Dep<moderation::Service>,
} }
/// generated MXC ID (`media-id`) length /// generated MXC ID (`media-id`) length
@ -64,6 +65,7 @@ impl crate::Service for Service {
client: args.depend::<client::Service>("client"), client: args.depend::<client::Service>("client"),
globals: args.depend::<globals::Service>("globals"), globals: args.depend::<globals::Service>("globals"),
sending: args.depend::<sending::Service>("sending"), sending: args.depend::<sending::Service>("sending"),
moderation: args.depend::<moderation::Service>("moderation"),
}, },
})) }))
} }

View file

@ -427,6 +427,10 @@ fn check_fetch_authorized(&self, mxc: &Mxc<'_>) -> Result<()> {
.config .config
.prevent_media_downloads_from .prevent_media_downloads_from
.contains(mxc.server_name) .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 // 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. // log. the client has no way of telling anyways so this is a security bonus.

View file

@ -15,6 +15,7 @@ pub mod federation;
pub mod globals; pub mod globals;
pub mod key_backups; pub mod key_backups;
pub mod media; pub mod media;
pub mod moderation;
pub mod presence; pub mod presence;
pub mod pusher; pub mod pusher;
pub mod resolver; pub mod resolver;

View file

@ -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<Server>,
pub globals: Dep<globals::Service>,
}
impl crate::Service for Service {
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
Ok(Arc::new(Self {
services: Services {
server: args.server.clone(),
globals: args.depend::<globals::Service>("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)
}

View file

@ -30,8 +30,9 @@ pub use self::{
sender::{EDU_LIMIT, PDU_LIMIT}, sender::{EDU_LIMIT, PDU_LIMIT},
}; };
use crate::{ use crate::{
account_data, client, federation, globals, presence, pusher, rooms, account_data, client, federation, globals, presence, pusher,
rooms::timeline::RawPduId, users, Dep, rooms::{self, timeline::RawPduId},
users, Dep,
}; };
pub struct Service { pub struct Service {

View file

@ -12,8 +12,8 @@ use tokio::sync::Mutex;
use crate::{ use crate::{
account_data, admin, appservice, client, config, emergency, federation, globals, key_backups, account_data, admin, appservice, client, config, emergency, federation, globals, key_backups,
manager::Manager, manager::Manager,
media, presence, pusher, resolver, rooms, sending, server_keys, service, media, moderation, presence, pusher, resolver, rooms, sending, server_keys,
service::{Args, Map, Service}, service::{self, Args, Map, Service},
sync, transaction_ids, uiaa, updates, users, sync, transaction_ids, uiaa, updates, users,
}; };
@ -31,6 +31,7 @@ pub struct Services {
pub pusher: Arc<pusher::Service>, pub pusher: Arc<pusher::Service>,
pub resolver: Arc<resolver::Service>, pub resolver: Arc<resolver::Service>,
pub rooms: rooms::Service, pub rooms: rooms::Service,
pub moderation: Arc<moderation::Service>,
pub federation: Arc<federation::Service>, pub federation: Arc<federation::Service>,
pub sending: Arc<sending::Service>, pub sending: Arc<sending::Service>,
pub server_keys: Arc<server_keys::Service>, pub server_keys: Arc<server_keys::Service>,
@ -98,6 +99,7 @@ impl Services {
typing: build!(rooms::typing::Service), typing: build!(rooms::typing::Service),
user: build!(rooms::user::Service), user: build!(rooms::user::Service),
}, },
moderation: build!(moderation::Service),
federation: build!(federation::Service), federation: build!(federation::Service),
sending: build!(sending::Service), sending: build!(sending::Service),
server_keys: build!(server_keys::Service), server_keys: build!(server_keys::Service),