diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index 61db932cc..b7aec47ba 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -25,6 +25,7 @@ services: - LEMMY_FRONT_END_DIR=/app/dist - LEMMY_FEDERATION__ENABLED=true - LEMMY_FEDERATION__TLS_ENABLED=false + - LEMMY_FEDERATION__INSTANCE_WHITELIST=lemmy_beta - LEMMY_PORT=8540 - LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha - LEMMY_SETUP__ADMIN_PASSWORD=lemmy @@ -58,6 +59,7 @@ services: - LEMMY_FRONT_END_DIR=/app/dist - LEMMY_FEDERATION__ENABLED=true - LEMMY_FEDERATION__TLS_ENABLED=false + - LEMMY_FEDERATION__INSTANCE_WHITELIST=lemmy_alpha - LEMMY_PORT=8550 - LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta - LEMMY_SETUP__ADMIN_PASSWORD=lemmy diff --git a/server/config/defaults.hjson b/server/config/defaults.hjson index 509bef47d..09db4c020 100644 --- a/server/config/defaults.hjson +++ b/server/config/defaults.hjson @@ -56,6 +56,8 @@ enabled: false # whether tls is required for activitypub. only disable this for debugging, never for producion. tls_enabled: true + # comma seperated list of instances with which federation is allowed + instance_whitelist: "" } # # email sending configuration # email: { diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs index f31be9db3..d49008064 100644 --- a/server/src/apub/activities.rs +++ b/server/src/apub/activities.rs @@ -1,3 +1,4 @@ +use crate::apub::is_apub_id_valid; use crate::db::community::Community; use crate::db::community_view::CommunityFollowerView; use crate::db::post::Post; @@ -36,9 +37,12 @@ where A: Serialize + Debug, { let json = serde_json::to_string(&activity)?; - debug!("Sending activitypub activity {}", json); + debug!("Sending activitypub activity {} to {:?}", json, to); for t in to { - debug!("Sending activity to: {}", t); + if is_apub_id_valid(&t) { + debug!("Not sending activity to {} (invalid or blacklisted)", t); + continue; + } let res = Request::post(t) .header("Content-Type", "application/json") .body(json.to_owned())? diff --git a/server/src/apub/community_inbox.rs b/server/src/apub/community_inbox.rs index ea0d91054..65d7bec1f 100644 --- a/server/src/apub/community_inbox.rs +++ b/server/src/apub/community_inbox.rs @@ -17,22 +17,18 @@ pub enum CommunityAcceptedObjects { Follow(Follow), } -#[derive(Deserialize)] -pub struct Params { - community_name: String, -} - /// Handler for all incoming activities to community inboxes. pub async fn community_inbox( input: web::Json, - params: web::Query, + path: web::Path, db: web::Data>>, ) -> Result { let input = input.into_inner(); let conn = &db.get().unwrap(); debug!( "Community {} received activity {:?}", - ¶ms.community_name, &input + &path.into_inner(), + &input ); match input { CommunityAcceptedObjects::Follow(f) => handle_follow(&f, conn), diff --git a/server/src/apub/fetcher.rs b/server/src/apub/fetcher.rs index 368aa4dc4..d44fdcb5f 100644 --- a/server/src/apub/fetcher.rs +++ b/server/src/apub/fetcher.rs @@ -8,7 +8,6 @@ use crate::db::user::{UserForm, User_}; use crate::db::user_view::UserView; use crate::db::{Crud, SearchType}; use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown}; -use crate::settings::Settings; use activitystreams::collection::OrderedCollection; use activitystreams::object::Page; use activitystreams::BaseBox; @@ -68,8 +67,8 @@ pub fn fetch_remote_object(url: &Url) -> Result where Response: for<'de> Deserialize<'de>, { - if Settings::get().federation.tls_enabled && url.scheme() != "https" { - return Err(format_err!("Activitypub uri is insecure: {}", url)); + if !is_apub_id_valid(&url.to_string()) { + return Err(format_err!("Activitypub uri invalid or blocked: {}", url)); } // TODO: this function should return a future let timeout = Duration::from_secs(60); @@ -86,7 +85,7 @@ where /// The types of ActivityPub objects that can be fetched directly by searching for their ID. #[serde(untagged)] -#[derive(serde::Deserialize)] +#[derive(serde::Deserialize, Debug)] pub enum SearchAcceptedObjects { Person(Box), Group(Box), diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index a7f0668a3..634f35101 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -88,3 +88,26 @@ pub fn gen_keypair_str() -> (String, String) { fn vec_bytes_to_str(bytes: Vec) -> String { String::from_utf8_lossy(&bytes).into_owned() } + +// Checks if the ID has a valid format, correct scheme, and is in the whitelist. +fn is_apub_id_valid(apub_id: &str) -> bool { + let url = match Url::parse(apub_id) { + Ok(u) => u, + Err(_) => return false, + }; + + if url.scheme() != get_apub_protocol_string() { + return false; + } + + let whitelist: Vec = Settings::get() + .federation + .instance_whitelist + .split(',') + .map(|d| d.to_string()) + .collect(); + match url.domain() { + Some(d) => whitelist.contains(&d.to_owned()), + None => false, + } +} diff --git a/server/src/apub/user_inbox.rs b/server/src/apub/user_inbox.rs index 7d1463083..75cd4e479 100644 --- a/server/src/apub/user_inbox.rs +++ b/server/src/apub/user_inbox.rs @@ -17,20 +17,19 @@ pub enum UserAcceptedObjects { Accept(Accept), } -#[derive(Deserialize)] -pub struct Params { - user_name: String, -} - /// Handler for all incoming activities to user inboxes. pub async fn user_inbox( input: web::Json, - params: web::Query, + path: web::Path, db: web::Data>>, ) -> Result { let input = input.into_inner(); let conn = &db.get().unwrap(); - debug!("User {} received activity: {:?}", ¶ms.user_name, &input); + debug!( + "User {} received activity: {:?}", + &path.into_inner(), + &input + ); match input { UserAcceptedObjects::Create(c) => handle_create(&c, conn), diff --git a/server/src/settings.rs b/server/src/settings.rs index a82e47860..c19cb7175 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -64,6 +64,7 @@ pub struct Database { pub struct Federation { pub enabled: bool, pub tls_enabled: bool, + pub instance_whitelist: String, } lazy_static! {