Compare commits

...

5 commits

Author SHA1 Message Date
June Clementine Strawberry
99868b1661
update new complement flakes
Signed-off-by: June Clementine Strawberry <june@3.dog>
2025-04-06 16:11:35 -04:00
June Clementine Strawberry
d5ad973464
change forbidden_server_names and etc to allow regex patterns for wildcards
Signed-off-by: June Clementine Strawberry <june@3.dog>
2025-04-06 15:25:19 -04:00
June Clementine Strawberry
ff276a42a3
drop unnecessary info log to debug
Signed-off-by: June Clementine Strawberry <june@3.dog>
2025-04-06 13:19:09 -04:00
June Clementine Strawberry
5f8c68ab84
add trace logging for room summaries, use server_in_room instead of exists
Signed-off-by: June Clementine Strawberry <june@3.dog>
2025-04-06 13:17:13 -04:00
June Clementine Strawberry
6578b83bce
parallelise IO of user searching, improve perf, raise max limit to 500
Signed-off-by: June Clementine Strawberry <june@3.dog>
2025-04-05 20:09:22 -04:00
17 changed files with 162 additions and 157 deletions

View file

@ -594,7 +594,7 @@
# Currently, conduwuit doesn't support inbound batched key requests, so # Currently, conduwuit doesn't support inbound batched key requests, so
# this list should only contain other Synapse servers. # this list should only contain other Synapse servers.
# #
# example: ["matrix.org", "envs.net", "tchncs.de"] # example: ["matrix.org", "tchncs.de"]
# #
#trusted_servers = ["matrix.org"] #trusted_servers = ["matrix.org"]
@ -1186,13 +1186,16 @@
# #
#prune_missing_media = false #prune_missing_media = false
# Vector list of servers that conduwuit will refuse to download remote # Vector list of regex patterns of server names that conduwuit will refuse
# media from. # to download remote media from.
#
# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
# #
#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 via regex patterns that we will block
# federation with, and block client room joins / remote user invites. # incoming AND outgoing federation with, and block client room joins /
# remote user invites.
# #
# This check is applied on the room ID, room alias, sender server name, # This check is applied on the room ID, room alias, sender server name,
# sender user's server name, inbound federation X-Matrix origin, and # sender user's server name, inbound federation X-Matrix origin, and
@ -1200,11 +1203,15 @@
# #
# Basically "global" ACLs. # Basically "global" ACLs.
# #
# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
#
#forbidden_remote_server_names = [] #forbidden_remote_server_names = []
# List of forbidden server names that we will block all outgoing federated # List of forbidden server names via regex patterns that we will block all
# room directory requests for. Useful for preventing our users from # outgoing federated room directory requests for. Useful for preventing
# wandering into bad servers or spaces. # our users from wandering into bad servers or spaces.
#
# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
# #
#forbidden_remote_room_directory_server_names = [] #forbidden_remote_room_directory_server_names = []
@ -1315,7 +1322,7 @@
# used, and startup as warnings if any room aliases in your database have # used, and startup as warnings if any room aliases in your database have
# a forbidden room alias/ID. # a forbidden room alias/ID.
# #
# example: ["19dollarfortnitecards", "b[4a]droom"] # example: ["19dollarfortnitecards", "b[4a]droom", "badphrase"]
# #
#forbidden_alias_names = [] #forbidden_alias_names = []
@ -1328,7 +1335,7 @@
# startup as warnings if any local users in your database have a forbidden # startup as warnings if any local users in your database have a forbidden
# username. # username.
# #
# example: ["administrator", "b[a4]dusernam[3e]"] # example: ["administrator", "b[a4]dusernam[3e]", "badphrase"]
# #
#forbidden_usernames = [] #forbidden_usernames = []

View file

@ -52,10 +52,13 @@ pub(crate) async fn get_public_rooms_filtered_route(
) -> Result<get_public_rooms_filtered::v3::Response> { ) -> Result<get_public_rooms_filtered::v3::Response> {
if let Some(server) = &body.server { if let Some(server) = &body.server {
if services if services
.server
.config .config
.forbidden_remote_room_directory_server_names .forbidden_remote_room_directory_server_names
.contains(server) .is_match(server.host())
|| services
.config
.forbidden_remote_server_names
.is_match(server.host())
{ {
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }
@ -90,10 +93,13 @@ pub(crate) async fn get_public_rooms_route(
) -> Result<get_public_rooms::v3::Response> { ) -> Result<get_public_rooms::v3::Response> {
if let Some(server) = &body.server { if let Some(server) = &body.server {
if services if services
.server
.config .config
.forbidden_remote_room_directory_server_names .forbidden_remote_room_directory_server_names
.contains(server) .is_match(server.host())
|| services
.config
.forbidden_remote_server_names
.is_match(server.host())
{ {
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }

View file

@ -1,7 +1,7 @@
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
use axum::extract::State; use axum::extract::State;
use conduwuit::{Err, Error, Result, debug, debug_warn, err, info, result::NotFound, utils}; use conduwuit::{Err, Error, Result, debug, debug_warn, err, result::NotFound, utils};
use conduwuit_service::{Services, users::parse_master_key}; use conduwuit_service::{Services, users::parse_master_key};
use futures::{StreamExt, stream::FuturesUnordered}; use futures::{StreamExt, stream::FuturesUnordered};
use ruma::{ use ruma::{
@ -177,7 +177,7 @@ pub(crate) async fn upload_signing_keys_route(
body.master_key.as_ref(), body.master_key.as_ref(),
) )
.await .await
.inspect_err(|e| info!(?e)) .inspect_err(|e| debug!(?e))
{ {
| Ok(exists) => { | Ok(exists) => {
if let Some(result) = exists { if let Some(result) = exists {

View file

@ -79,10 +79,9 @@ 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
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&room_id.server_name().unwrap().to_owned()) .is_match(room_id.server_name().unwrap().host())
{ {
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 \
@ -120,10 +119,9 @@ async fn banned_room_check(
} }
} else if let Some(server_name) = server_name { } else if let Some(server_name) = server_name {
if services if services
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&server_name.to_owned()) .is_match(server_name.host())
{ {
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 \

View file

@ -261,10 +261,9 @@ pub(crate) async fn is_ignored_pdu(
let ignored_type = IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok(); let ignored_type = IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok();
let ignored_server = services let ignored_server = services
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(pdu.sender().server_name()); .is_match(pdu.sender().server_name().host());
if ignored_type if ignored_type
&& (ignored_server || services.users.user_is_ignored(&pdu.sender, user_id).await) && (ignored_server || services.users.user_is_ignored(&pdu.sender, user_id).await)

View file

@ -1,7 +1,7 @@
use axum::extract::State; use axum::extract::State;
use axum_client_ip::InsecureClientIp; use axum_client_ip::InsecureClientIp;
use conduwuit::{ use conduwuit::{
Err, Result, debug_warn, Err, Result, debug_warn, trace,
utils::{IterStream, future::TryExtExt}, utils::{IterStream, future::TryExtExt},
}; };
use futures::{ use futures::{
@ -74,7 +74,12 @@ async fn room_summary_response(
servers: &[OwnedServerName], servers: &[OwnedServerName],
sender_user: Option<&UserId>, sender_user: Option<&UserId>,
) -> Result<get_summary::msc3266::Response> { ) -> Result<get_summary::msc3266::Response> {
if services.rooms.metadata.exists(room_id).await { if services
.rooms
.state_cache
.server_in_room(services.globals.server_name(), room_id)
.await
{
return local_room_summary_response(services, room_id, sender_user) return local_room_summary_response(services, room_id, sender_user)
.boxed() .boxed()
.await; .await;
@ -106,14 +111,14 @@ async fn local_room_summary_response(
room_id: &RoomId, room_id: &RoomId,
sender_user: Option<&UserId>, sender_user: Option<&UserId>,
) -> Result<get_summary::msc3266::Response> { ) -> Result<get_summary::msc3266::Response> {
trace!(?sender_user, "Sending local room summary response for {room_id:?}");
let join_rule = services.rooms.state_accessor.get_join_rules(room_id); let join_rule = services.rooms.state_accessor.get_join_rules(room_id);
let world_readable = services.rooms.state_accessor.is_world_readable(room_id); let world_readable = services.rooms.state_accessor.is_world_readable(room_id);
let guest_can_join = services.rooms.state_accessor.guest_can_join(room_id); let guest_can_join = services.rooms.state_accessor.guest_can_join(room_id);
let (join_rule, world_readable, guest_can_join) = let (join_rule, world_readable, guest_can_join) =
join3(join_rule, world_readable, guest_can_join).await; join3(join_rule, world_readable, guest_can_join).await;
trace!("{join_rule:?}, {world_readable:?}, {guest_can_join:?}");
user_can_see_summary( user_can_see_summary(
services, services,
@ -215,6 +220,7 @@ async fn remote_room_summary_hierarchy_response(
servers: &[OwnedServerName], servers: &[OwnedServerName],
sender_user: Option<&UserId>, sender_user: Option<&UserId>,
) -> Result<SpaceHierarchyParentSummary> { ) -> Result<SpaceHierarchyParentSummary> {
trace!(?sender_user, ?servers, "Sending remote room summary response for {room_id:?}");
if !services.config.allow_federation { if !services.config.allow_federation {
return Err!(Request(Forbidden("Federation is disabled."))); return Err!(Request(Forbidden("Federation is disabled.")));
} }
@ -237,6 +243,7 @@ async fn remote_room_summary_hierarchy_response(
.collect(); .collect();
while let Some(Ok(response)) = requests.next().await { while let Some(Ok(response)) = requests.next().await {
trace!("{response:?}");
let room = response.room.clone(); let room = response.room.clone();
if room.room_id != room_id { if room.room_id != room_id {
debug_warn!( debug_warn!(
@ -278,6 +285,7 @@ async fn user_can_see_summary<'a, I>(
where where
I: Iterator<Item = &'a RoomId> + Send, I: Iterator<Item = &'a RoomId> + Send,
{ {
let is_public_room = matches!(join_rule, Public | Knock | KnockRestricted);
match sender_user { match sender_user {
| Some(sender_user) => { | Some(sender_user) => {
let user_can_see_state_events = services let user_can_see_state_events = services
@ -296,7 +304,7 @@ where
if user_can_see_state_events if user_can_see_state_events
|| (is_guest && guest_can_join) || (is_guest && guest_can_join)
|| matches!(&join_rule, &Public | &Knock | &KnockRestricted) || is_public_room
|| user_in_allowed_restricted_room || user_in_allowed_restricted_room
{ {
return Ok(()); return Ok(());
@ -309,7 +317,7 @@ where
))) )))
}, },
| None => { | None => {
if matches!(join_rule, Public | Knock | KnockRestricted) || world_readable { if is_public_room || world_readable {
return Ok(()); return Ok(());
} }

View file

@ -1,16 +1,20 @@
use axum::extract::State; use axum::extract::State;
use conduwuit::{Result, utils::TryFutureExtExt}; use conduwuit::{
use futures::{StreamExt, pin_mut}; Result,
utils::{future::BoolExt, stream::BroadbandExt},
};
use futures::{FutureExt, StreamExt, pin_mut};
use ruma::{ use ruma::{
api::client::user_directory::search_users, api::client::user_directory::search_users::{self},
events::{ events::room::join_rules::JoinRule,
StateEventType,
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
},
}; };
use crate::Ruma; use crate::Ruma;
// conduwuit can handle a lot more results than synapse
const LIMIT_MAX: usize = 500;
const LIMIT_DEFAULT: usize = 10;
/// # `POST /_matrix/client/r0/user_directory/search` /// # `POST /_matrix/client/r0/user_directory/search`
/// ///
/// Searches all known users for a match. /// Searches all known users for a match.
@ -21,78 +25,63 @@ pub(crate) async fn search_users_route(
State(services): State<crate::State>, State(services): State<crate::State>,
body: Ruma<search_users::v3::Request>, body: Ruma<search_users::v3::Request>,
) -> Result<search_users::v3::Response> { ) -> Result<search_users::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated"); let sender_user = body.sender_user();
let limit = usize::try_from(body.limit).map_or(10, usize::from).min(100); // default limit is 10 let limit = usize::try_from(body.limit)
.map_or(LIMIT_DEFAULT, usize::from)
.min(LIMIT_MAX);
let users = services.users.stream().filter_map(|user_id| async { let mut users = services
// Filter out buggy users (they should not exist, but you never know...) .users
let user = search_users::v3::User { .stream()
user_id: user_id.to_owned(), .map(ToOwned::to_owned)
display_name: services.users.displayname(user_id).await.ok(), .broad_filter_map(async |user_id| {
avatar_url: services.users.avatar_url(user_id).await.ok(), let user = search_users::v3::User {
}; user_id: user_id.clone(),
display_name: services.users.displayname(&user_id).await.ok(),
avatar_url: services.users.avatar_url(&user_id).await.ok(),
};
let user_id_matches = user let user_id_matches = user
.user_id .user_id
.to_string() .as_str()
.to_lowercase() .to_lowercase()
.contains(&body.search_term.to_lowercase()); .contains(&body.search_term.to_lowercase());
let user_displayname_matches = user let user_displayname_matches = user.display_name.as_ref().is_some_and(|name| {
.display_name
.as_ref()
.filter(|name| {
name.to_lowercase() name.to_lowercase()
.contains(&body.search_term.to_lowercase()) .contains(&body.search_term.to_lowercase())
}) });
.is_some();
if !user_id_matches && !user_displayname_matches { if !user_id_matches && !user_displayname_matches {
return None; return None;
} }
// It's a matching user, but is the sender allowed to see them? let user_in_public_room = services
let mut user_visible = false;
let user_is_in_public_rooms = services
.rooms
.state_cache
.rooms_joined(&user.user_id)
.any(|room| {
services
.rooms
.state_accessor
.room_state_get_content::<RoomJoinRulesEventContent>(
room,
&StateEventType::RoomJoinRules,
"",
)
.map_ok_or(false, |content| content.join_rule == JoinRule::Public)
})
.await;
if user_is_in_public_rooms {
user_visible = true;
} else {
let user_is_in_shared_rooms = services
.rooms .rooms
.state_cache .state_cache
.user_sees_user(sender_user, &user.user_id) .rooms_joined(&user_id)
.await; .map(ToOwned::to_owned)
.any(|room| async move {
services
.rooms
.state_accessor
.get_join_rules(&room)
.map(|rule| matches!(rule, JoinRule::Public))
.await
});
if user_is_in_shared_rooms { let user_sees_user = services
user_visible = true; .rooms
} .state_cache
} .user_sees_user(sender_user, &user_id);
user_visible.then_some(user) pin_mut!(user_in_public_room, user_sees_user);
});
pin_mut!(users); user_in_public_room.or(user_sees_user).await.then_some(user)
});
let limited = users.by_ref().next().await.is_some(); let results = users.by_ref().take(limit).collect().await;
let limited = users.next().await.is_some();
let results = users.take(limit).collect().await;
Ok(search_users::v3::Response { results, limited }) Ok(search_users::v3::Response { results, limited })
} }

View file

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

View file

@ -38,20 +38,18 @@ 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
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&server.to_owned()) .is_match(server.host())
{ {
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
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(body.origin()) .is_match(body.origin().host())
{ {
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,9 @@ pub(crate) async fn create_join_event_template_route(
.await?; .await?;
if services if services
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(body.origin()) .is_match(body.origin().host())
{ {
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 \
@ -59,10 +58,9 @@ 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
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&server.to_owned()) .is_match(server.host())
{ {
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

@ -33,10 +33,9 @@ pub(crate) async fn create_knock_event_template_route(
.await?; .await?;
if services if services
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(body.origin()) .is_match(body.origin().host())
{ {
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,10 +49,9 @@ 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
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&server.to_owned()) .is_match(server.host())
{ {
return Err!(Request(Forbidden("Server is banned on this homeserver."))); return Err!(Request(Forbidden("Server is banned on this homeserver.")));
} }

View file

@ -268,10 +268,9 @@ 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
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(body.origin()) .is_match(body.origin().host())
{ {
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 \
@ -284,10 +283,9 @@ 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
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&server.to_owned()) .is_match(server.host())
{ {
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 \
@ -316,20 +314,18 @@ 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
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(body.origin()) .is_match(body.origin().host())
{ {
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
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&server.to_owned()) .is_match(server.host())
{ {
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 \

View file

@ -26,10 +26,9 @@ 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
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(body.origin()) .is_match(body.origin().host())
{ {
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 \
@ -42,10 +41,9 @@ 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
.server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(&server.to_owned()) .is_match(server.host())
{ {
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 \

View file

@ -3,7 +3,7 @@ pub mod manager;
pub mod proxy; pub mod proxy;
use std::{ use std::{
collections::{BTreeMap, BTreeSet, HashSet}, collections::{BTreeMap, BTreeSet},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -715,7 +715,7 @@ pub struct Config {
/// Currently, conduwuit doesn't support inbound batched key requests, so /// Currently, conduwuit doesn't support inbound batched key requests, so
/// this list should only contain other Synapse servers. /// this list should only contain other Synapse servers.
/// ///
/// example: ["matrix.org", "envs.net", "tchncs.de"] /// example: ["matrix.org", "tchncs.de"]
/// ///
/// default: ["matrix.org"] /// default: ["matrix.org"]
#[serde(default = "default_trusted_servers")] #[serde(default = "default_trusted_servers")]
@ -1361,15 +1361,18 @@ pub struct Config {
#[serde(default)] #[serde(default)]
pub prune_missing_media: bool, pub prune_missing_media: bool,
/// Vector list of servers that conduwuit will refuse to download remote /// Vector list of regex patterns of server names that conduwuit will refuse
/// media from. /// to download remote media from.
///
/// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
/// ///
/// default: [] /// default: []
#[serde(default)] #[serde(default, with = "serde_regex")]
pub prevent_media_downloads_from: HashSet<OwnedServerName>, pub prevent_media_downloads_from: RegexSet,
/// List of forbidden server names that we will block incoming AND outgoing /// List of forbidden server names via regex patterns that we will block
/// federation with, and block client room joins / remote user invites. /// incoming AND outgoing federation with, and block client room joins /
/// remote user invites.
/// ///
/// This check is applied on the room ID, room alias, sender server name, /// This check is applied on the room ID, room alias, sender server name,
/// sender user's server name, inbound federation X-Matrix origin, and /// sender user's server name, inbound federation X-Matrix origin, and
@ -1377,17 +1380,21 @@ pub struct Config {
/// ///
/// Basically "global" ACLs. /// Basically "global" ACLs.
/// ///
/// default: [] /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
#[serde(default)]
pub forbidden_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.
/// ///
/// default: [] /// default: []
#[serde(default = "HashSet::new")] #[serde(default, with = "serde_regex")]
pub forbidden_remote_room_directory_server_names: HashSet<OwnedServerName>, pub forbidden_remote_server_names: RegexSet,
/// List of forbidden server names via regex patterns that we will block all
/// outgoing federated room directory requests for. Useful for preventing
/// our users from wandering into bad servers or spaces.
///
/// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
///
/// default: []
#[serde(default, with = "serde_regex")]
pub forbidden_remote_room_directory_server_names: RegexSet,
/// 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
/// do not want conduwuit to send outbound requests to. Defaults to /// do not want conduwuit to send outbound requests to. Defaults to
@ -1508,11 +1515,10 @@ pub struct Config {
/// used, and startup as warnings if any room aliases in your database have /// used, and startup as warnings if any room aliases in your database have
/// a forbidden room alias/ID. /// a forbidden room alias/ID.
/// ///
/// example: ["19dollarfortnitecards", "b[4a]droom"] /// example: ["19dollarfortnitecards", "b[4a]droom", "badphrase"]
/// ///
/// default: [] /// default: []
#[serde(default)] #[serde(default, with = "serde_regex")]
#[serde(with = "serde_regex")]
pub forbidden_alias_names: RegexSet, pub forbidden_alias_names: RegexSet,
/// List of forbidden username patterns/strings. /// List of forbidden username patterns/strings.
@ -1524,11 +1530,10 @@ pub struct Config {
/// startup as warnings if any local users in your database have a forbidden /// startup as warnings if any local users in your database have a forbidden
/// username. /// username.
/// ///
/// example: ["administrator", "b[a4]dusernam[3e]"] /// example: ["administrator", "b[a4]dusernam[3e]", "badphrase"]
/// ///
/// default: [] /// default: []
#[serde(default)] #[serde(default, with = "serde_regex")]
#[serde(with = "serde_regex")]
pub forbidden_usernames: RegexSet, pub forbidden_usernames: RegexSet,
/// Retry failed and incomplete messages to remote servers immediately upon /// Retry failed and incomplete messages to remote servers immediately upon

View file

@ -69,7 +69,7 @@ where
.server .server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(dest) .is_match(dest.host())
{ {
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

@ -426,7 +426,13 @@ fn check_fetch_authorized(&self, mxc: &Mxc<'_>) -> Result<()> {
.server .server
.config .config
.prevent_media_downloads_from .prevent_media_downloads_from
.contains(mxc.server_name) .is_match(mxc.server_name.host())
|| self
.services
.server
.config
.forbidden_remote_server_names
.is_match(mxc.server_name.host())
{ {
// 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

@ -491,7 +491,7 @@
{"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself"} {"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself"}
{"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel"} {"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel"}
{"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Joining_room_twice_is_idempotent"} {"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Joining_room_twice_is_idempotent"}
{"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Room_creation_reports_m.room.create_to_myself"} {"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel/Room_creation_reports_m.room.create_to_myself"}
{"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Room_creation_reports_m.room.member_to_myself"} {"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Room_creation_reports_m.room.member_to_myself"}
{"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Setting_room_topic_reports_m.room.topic_to_myself"} {"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Setting_room_topic_reports_m.room.topic_to_myself"}
{"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel/Setting_state_twice_is_idempotent"} {"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel/Setting_state_twice_is_idempotent"}
@ -527,17 +527,17 @@
{"Action":"pass","Test":"TestRoomMessagesLazyLoadingLocalUser"} {"Action":"pass","Test":"TestRoomMessagesLazyLoadingLocalUser"}
{"Action":"pass","Test":"TestRoomReadMarkers"} {"Action":"pass","Test":"TestRoomReadMarkers"}
{"Action":"pass","Test":"TestRoomReceipts"} {"Action":"pass","Test":"TestRoomReceipts"}
{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin"} {"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin"}
{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_mxid"} {"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_mxid"}
{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_profile_display_name"} {"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_profile_display_name"}
{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_mxid"} {"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_mxid"}
{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_profile_display_name"} {"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_profile_display_name"}
{"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Eve_cannot_find_Alice_by_room-specific_name_that_Eve_is_not_privy_to"} {"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Eve_cannot_find_Alice_by_room-specific_name_that_Eve_is_not_privy_to"}
{"Action":"fail","Test":"TestRoomSpecificUsernameChange"} {"Action":"pass","Test":"TestRoomSpecificUsernameChange"}
{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_mxid"} {"Action":"pass","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_mxid"}
{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_profile_display_name"} {"Action":"pass","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_profile_display_name"}
{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_mxid"} {"Action":"pass","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_mxid"}
{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_profile_display_name"} {"Action":"pass","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_profile_display_name"}
{"Action":"pass","Test":"TestRoomSpecificUsernameChange/Eve_cannot_find_Alice_by_room-specific_name_that_Eve_is_not_privy_to"} {"Action":"pass","Test":"TestRoomSpecificUsernameChange/Eve_cannot_find_Alice_by_room-specific_name_that_Eve_is_not_privy_to"}
{"Action":"fail","Test":"TestRoomState"} {"Action":"fail","Test":"TestRoomState"}
{"Action":"fail","Test":"TestRoomState/Parallel"} {"Action":"fail","Test":"TestRoomState/Parallel"}
@ -589,7 +589,7 @@
{"Action":"fail","Test":"TestSync/parallel/Newly_joined_room_has_correct_timeline_in_incremental_sync"} {"Action":"fail","Test":"TestSync/parallel/Newly_joined_room_has_correct_timeline_in_incremental_sync"}
{"Action":"fail","Test":"TestSync/parallel/Newly_joined_room_includes_presence_in_incremental_sync"} {"Action":"fail","Test":"TestSync/parallel/Newly_joined_room_includes_presence_in_incremental_sync"}
{"Action":"pass","Test":"TestSync/parallel/Newly_joined_room_is_included_in_an_incremental_sync"} {"Action":"pass","Test":"TestSync/parallel/Newly_joined_room_is_included_in_an_incremental_sync"}
{"Action":"fail","Test":"TestSync/parallel/sync_should_succeed_even_if_the_sync_token_points_to_a_redaction_of_an_unknown_event"} {"Action":"pass","Test":"TestSync/parallel/sync_should_succeed_even_if_the_sync_token_points_to_a_redaction_of_an_unknown_event"}
{"Action":"pass","Test":"TestSyncFilter"} {"Action":"pass","Test":"TestSyncFilter"}
{"Action":"pass","Test":"TestSyncFilter/Can_create_filter"} {"Action":"pass","Test":"TestSyncFilter/Can_create_filter"}
{"Action":"pass","Test":"TestSyncFilter/Can_download_filter"} {"Action":"pass","Test":"TestSyncFilter/Can_download_filter"}