Async verify (#4)
* Add date header when sending activity * Version 0.2.2 * Make verify url function async
This commit is contained in:
parent
356b98bb97
commit
472d55a69e
7 changed files with 66 additions and 11 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -4,7 +4,7 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "activitypub_federation"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"activitypub_federation_derive",
|
||||
"activitystreams-kinds",
|
||||
|
@ -16,6 +16,7 @@ dependencies = [
|
|||
"base64",
|
||||
"chrono",
|
||||
"derive_builder",
|
||||
"dyn-clone",
|
||||
"env_logger",
|
||||
"http",
|
||||
"http-signature-normalization-actix",
|
||||
|
@ -530,6 +531,12 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c97b9233581d84b8e1e689cdd3a47b6f69770084fc246e86a7f78b0d9c1d4a5"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "activitypub_federation"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
description = "High-level Activitypub framework"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -33,6 +33,7 @@ background-jobs = "0.13.0"
|
|||
thiserror = "1.0.37"
|
||||
derive_builder = "0.11.2"
|
||||
itertools = "0.10.5"
|
||||
dyn-clone = "1.0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
activitystreams-kinds = "0.2.1"
|
||||
|
|
|
@ -13,9 +13,11 @@ use activitypub_federation::{
|
|||
traits::ApubObject,
|
||||
InstanceSettings,
|
||||
LocalInstance,
|
||||
UrlVerifier,
|
||||
APUB_JSON_CONTENT_TYPE,
|
||||
};
|
||||
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
||||
use async_trait::async_trait;
|
||||
use http_signature_normalization_actix::prelude::VerifyDigest;
|
||||
use reqwest::Client;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
@ -37,9 +39,27 @@ pub struct Instance {
|
|||
pub posts: Mutex<Vec<MyPost>>,
|
||||
}
|
||||
|
||||
/// Use this to store your federation blocklist, or a database connection needed to retrieve it.
|
||||
#[derive(Clone)]
|
||||
struct MyUrlVerifier();
|
||||
|
||||
#[async_trait]
|
||||
impl UrlVerifier for MyUrlVerifier {
|
||||
async fn verify(&self, url: &Url) -> Result<(), &'static str> {
|
||||
if url.domain() == Some("malicious.com") {
|
||||
Err("malicious domain")
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub fn new(hostname: String) -> Result<InstanceHandle, Error> {
|
||||
let settings = InstanceSettings::builder().debug(true).build()?;
|
||||
let settings = InstanceSettings::builder()
|
||||
.debug(true)
|
||||
.url_verifier(Box::new(MyUrlVerifier()))
|
||||
.build()?;
|
||||
let local_instance =
|
||||
LocalInstance::new(hostname.clone(), Client::default().into(), settings);
|
||||
let local_user = MyUser::new(generate_object_id(&hostname)?, generate_actor_keypair()?);
|
||||
|
|
|
@ -16,6 +16,7 @@ use background_jobs::{
|
|||
MaxRetries,
|
||||
WorkerConfig,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use http::{header::HeaderName, HeaderMap, HeaderValue};
|
||||
use itertools::Itertools;
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
|
@ -50,12 +51,11 @@ where
|
|||
.into_iter()
|
||||
.unique()
|
||||
.filter(|i| !instance.is_local_url(i))
|
||||
.filter(|i| verify_url_valid(i, &instance.settings).is_ok())
|
||||
.collect();
|
||||
|
||||
let activity_queue = &instance.activity_queue;
|
||||
for inbox in inboxes {
|
||||
if verify_url_valid(&inbox, &instance.settings).is_err() {
|
||||
if verify_url_valid(&inbox, &instance.settings).await.is_err() {
|
||||
continue;
|
||||
}
|
||||
let message = SendActivityTask {
|
||||
|
@ -187,6 +187,10 @@ fn generate_request_headers(inbox_url: &Url) -> HeaderMap {
|
|||
HeaderName::from_static("host"),
|
||||
HeaderValue::from_str(&host).expect("Hostname is valid"),
|
||||
);
|
||||
headers.insert(
|
||||
"date",
|
||||
HeaderValue::from_str(&Utc::now().to_rfc2822()).expect("Hostname is valid"),
|
||||
);
|
||||
headers
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ where
|
|||
<ActorT as ApubObject>::Error: From<Error> + From<anyhow::Error>,
|
||||
{
|
||||
verify_domains_match(activity.id(), activity.actor())?;
|
||||
verify_url_valid(activity.id(), &local_instance.settings)?;
|
||||
verify_url_valid(activity.id(), &local_instance.settings).await?;
|
||||
if local_instance.is_local_url(activity.id()) {
|
||||
return Err(Error::UrlVerificationError("Activity was sent from local instance").into());
|
||||
}
|
||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -1,6 +1,8 @@
|
|||
use crate::core::activity_queue::create_activity_queue;
|
||||
use async_trait::async_trait;
|
||||
use background_jobs::Manager;
|
||||
use derive_builder::Builder;
|
||||
use dyn_clone::{clone_trait_object, DynClone};
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
@ -23,6 +25,12 @@ pub struct LocalInstance {
|
|||
settings: InstanceSettings,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UrlVerifier: DynClone + Send {
|
||||
async fn verify(&self, url: &Url) -> Result<(), &'static str>;
|
||||
}
|
||||
clone_trait_object!(UrlVerifier);
|
||||
|
||||
// Use InstanceSettingsBuilder to initialize this
|
||||
#[derive(Builder)]
|
||||
pub struct InstanceSettings {
|
||||
|
@ -45,12 +53,13 @@ pub struct InstanceSettings {
|
|||
/// Function used to verify that urls are valid, used when receiving activities or fetching remote
|
||||
/// objects. Use this to implement functionality like federation blocklists. In case verification
|
||||
/// fails, it should return an error message.
|
||||
#[builder(default = "|_| { Ok(()) }")]
|
||||
verify_url_function: fn(&Url) -> Result<(), &'static str>,
|
||||
#[builder(default = "Box::new(DefaultUrlVerifier())")]
|
||||
url_verifier: Box<dyn UrlVerifier + Sync>,
|
||||
/// Enable to sign HTTP signatures according to draft 10, which does not include (created) and
|
||||
/// (expires) fields. This is required for compatibility with some software like Pleroma.
|
||||
/// https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-10
|
||||
/// https://git.pleroma.social/pleroma/pleroma/-/issues/2939
|
||||
#[builder(default = "false")]
|
||||
http_signature_compat: bool,
|
||||
}
|
||||
|
||||
|
@ -61,6 +70,16 @@ impl InstanceSettings {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DefaultUrlVerifier();
|
||||
|
||||
#[async_trait]
|
||||
impl UrlVerifier for DefaultUrlVerifier {
|
||||
async fn verify(&self, _url: &Url) -> Result<(), &'static str> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalInstance {
|
||||
pub fn new(domain: String, client: ClientWithMiddleware, settings: InstanceSettings) -> Self {
|
||||
let activity_queue = create_activity_queue(client.clone(), &settings);
|
||||
|
|
10
src/utils.rs
10
src/utils.rs
|
@ -11,7 +11,7 @@ pub async fn fetch_object_http<Kind: DeserializeOwned>(
|
|||
) -> Result<Kind, Error> {
|
||||
// dont fetch local objects this way
|
||||
debug_assert!(url.domain() != Some(&instance.hostname));
|
||||
verify_url_valid(url, &instance.settings)?;
|
||||
verify_url_valid(url, &instance.settings).await?;
|
||||
info!("Fetching remote object {}", url.to_string());
|
||||
|
||||
*request_counter += 1;
|
||||
|
@ -55,7 +55,7 @@ pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), Error> {
|
|||
/// [`InstanceSettings.verify_url_function`].
|
||||
///
|
||||
/// https://www.w3.org/TR/activitypub/#security-considerations
|
||||
pub fn verify_url_valid(url: &Url, settings: &InstanceSettings) -> Result<(), Error> {
|
||||
pub async fn verify_url_valid(url: &Url, settings: &InstanceSettings) -> Result<(), Error> {
|
||||
match url.scheme() {
|
||||
"https" => {}
|
||||
"http" => {
|
||||
|
@ -78,7 +78,11 @@ pub fn verify_url_valid(url: &Url, settings: &InstanceSettings) -> Result<(), Er
|
|||
));
|
||||
}
|
||||
|
||||
(settings.verify_url_function)(url).map_err(Error::UrlVerificationError)?;
|
||||
settings
|
||||
.url_verifier
|
||||
.verify(url)
|
||||
.await
|
||||
.map_err(Error::UrlVerificationError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue