From 95d1ab948f6d1dcafb142ada026793336b13be2f Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 29 Dec 2024 03:25:10 +0100 Subject: [PATCH] feat: simple federation policies basically can now fediblock instances, yayy, but more interestingly just refuse to mirror media from problematic instances or refuse deliveries for places we want to read but who shouldn't read us --- upub/core/src/config.rs | 16 ++++++++++++++++ upub/routes/src/activitypub/application.rs | 5 +++++ upub/routes/src/activitypub/inbox.rs | 6 +++++- upub/worker/src/outbound.rs | 12 ++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/upub/core/src/config.rs b/upub/core/src/config.rs index 7036995..c51477b 100644 --- a/upub/core/src/config.rs +++ b/upub/core/src/config.rs @@ -18,6 +18,9 @@ pub struct Config { #[serde(default)] pub files: FileStorageConfig, + #[serde(default)] + pub reject: RejectConfig, + // TODO should i move app keys here? } @@ -122,6 +125,19 @@ pub struct FileStorageConfig { pub path: String, } +#[serde_inline_default::serde_inline_default] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, serde_default::DefaultFromSerde)] +pub struct RejectConfig { + #[serde(default)] + pub everything: Vec, + + #[serde(default)] + pub media: Vec, + + #[serde(default)] + pub delivery: Vec, +} + impl Config { pub fn load(path: Option<&std::path::PathBuf>) -> Self { let Some(cfg_path) = path else { return Config::default() }; diff --git a/upub/routes/src/activitypub/application.rs b/upub/routes/src/activitypub/application.rs index 266df32..f15790b 100644 --- a/upub/routes/src/activitypub/application.rs +++ b/upub/routes/src/activitypub/application.rs @@ -120,6 +120,11 @@ pub async fn cloak_proxy( let uri = ctx.uncloak(&hmac, &uri) .ok_or_else(ApiError::unauthorized)?; + let stripped = uri.replace("https://", "").replace("http://", ""); + if ctx.cfg().reject.media.iter().any(|x| stripped.starts_with(x)) { + return Err(ApiError::Status(axum::http::StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS)); + } + let resp = Context::client(ctx.domain()) .get(uri) .send() diff --git a/upub/routes/src/activitypub/inbox.rs b/upub/routes/src/activitypub/inbox.rs index 4711257..e5292ce 100644 --- a/upub/routes/src/activitypub/inbox.rs +++ b/upub/routes/src/activitypub/inbox.rs @@ -43,7 +43,7 @@ pub async fn post( AuthIdentity(auth): AuthIdentity, Json(activity): Json ) -> crate::ApiResult { - let Identity::Remote { domain: _server, user: uid, .. } = auth else { + let Identity::Remote { domain, user: uid, .. } = auth else { if matches!(activity.activity_type(), Ok(ActivityType::Delete)) { // this is spammy af, ignore them! // we basically received a delete for a user we can't fetch and verify, meaning remote @@ -64,6 +64,10 @@ pub async fn post( } }; + if ctx.cfg().reject.everything.contains(&domain) { + return Err(crate::ApiError::Status(StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS)); + } + let aid = activity.id()?.to_string(); let server = upub::Context::server(&aid); diff --git a/upub/worker/src/outbound.rs b/upub/worker/src/outbound.rs index 13a0220..bab2a0d 100644 --- a/upub/worker/src/outbound.rs +++ b/upub/worker/src/outbound.rs @@ -185,6 +185,18 @@ pub async fn process(ctx: Context, job: &model::job::Model) -> crate::JobResult< targets.push(relay); } } + + targets + .retain(|target| { + let stripped = target.replace("https://", "").replace("http://", ""); + if ctx.cfg().reject.delivery.iter().any(|x| stripped.starts_with(x)) { + tracing::warn!("rejecting delivery of {} to {target}", job.activity); + false + } else { + true + } + }); + ctx.deliver(targets, &job.activity, &job.actor, &tx).await?; tx.commit().await?;