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
# 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

View file

@ -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.")));
}

View file

@ -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.",

View file

@ -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;
}

View file

@ -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."
))));

View file

@ -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.",

View file

@ -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."
))));

View file

@ -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.")));
}
}

View file

@ -270,10 +270,8 @@ pub(crate) async fn create_join_event_v1_route(
body: Ruma<create_join_event::v1::Request>,
) -> Result<create_join_event::v1::Response> {
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<create_join_event::v2::Request>,
) -> Result<create_join_event::v2::Response> {
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.",

View file

@ -22,10 +22,8 @@ pub(crate) async fn create_knock_event_v1_route(
body: Ruma<send_knock::v1::Request>,
) -> Result<send_knock::v1::Response> {
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.",

View file

@ -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<OwnedServerName>,
@ -1335,16 +1337,28 @@ 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<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
/// 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<OwnedServerName>,
@ -1871,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() {

View file

@ -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."))));
}

View file

@ -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<client::Service>,
resolver: Dep<resolver::Service>,
server_keys: Dep<server_keys::Service>,
moderation: Dep<moderation::Service>,
}
impl crate::Service for Service {
@ -25,6 +26,7 @@ impl crate::Service for Service {
client: args.depend::<client::Service>("client"),
resolver: args.depend::<resolver::Service>("resolver"),
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};
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<client::Service>,
globals: Dep<globals::Service>,
sending: Dep<sending::Service>,
moderation: Dep<moderation::Service>,
}
/// generated MXC ID (`media-id`) length
@ -64,6 +65,7 @@ impl crate::Service for Service {
client: args.depend::<client::Service>("client"),
globals: args.depend::<globals::Service>("globals"),
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
.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.

View file

@ -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;

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},
};
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 {

View file

@ -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<pusher::Service>,
pub resolver: Arc<resolver::Service>,
pub rooms: rooms::Service,
pub moderation: Arc<moderation::Service>,
pub federation: Arc<federation::Service>,
pub sending: Arc<sending::Service>,
pub server_keys: Arc<server_keys::Service>,
@ -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),