Add federation.onion_proxy_url configuration parameter

This commit is contained in:
silverpill 2023-03-06 14:30:34 +00:00
parent 94a5f3a3cd
commit 50f31e96fc
7 changed files with 55 additions and 14 deletions

View file

@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Support audio attachments. - Support audio attachments.
- Added CLI command for viewing unreachable actors. - Added CLI command for viewing unreachable actors.
- Implemented NodeInfo 2.1. - Implemented NodeInfo 2.1.
- Added `federation.onion_proxy_url` configuration parameter (enables proxy for requests to `.onion` domains).
### Changed ### Changed

View file

@ -47,6 +47,8 @@ retention:
#federation: #federation:
# # Proxy for outgoing requests # # Proxy for outgoing requests
# #proxy_url: 'socks5h://127.0.0.1:9050' # #proxy_url: 'socks5h://127.0.0.1:9050'
# # Proxy for outgoing requests to .onion targets
# #onion_proxy_url: 'socks5h://127.0.0.1:9050'
# List of blocked domains # List of blocked domains
#blocked_instances: [] #blocked_instances: []

View file

@ -99,6 +99,7 @@ impl Config {
_url: self.try_instance_url().unwrap(), _url: self.try_instance_url().unwrap(),
actor_key: self.instance_rsa_key.clone().unwrap(), actor_key: self.instance_rsa_key.clone().unwrap(),
proxy_url: self.federation.proxy_url.clone(), proxy_url: self.federation.proxy_url.clone(),
onion_proxy_url: self.federation.onion_proxy_url.clone(),
is_private: matches!(self.environment, Environment::Development), is_private: matches!(self.environment, Environment::Development),
} }
} }
@ -131,6 +132,7 @@ pub struct Instance {
pub actor_key: RsaPrivateKey, pub actor_key: RsaPrivateKey,
// Proxy for outgoing requests // Proxy for outgoing requests
pub proxy_url: Option<String>, pub proxy_url: Option<String>,
pub onion_proxy_url: Option<String>,
// Private instance won't send signed HTTP requests // Private instance won't send signed HTTP requests
pub is_private: bool, pub is_private: bool,
} }
@ -161,6 +163,7 @@ impl Instance {
_url: Url::parse(url).unwrap(), _url: Url::parse(url).unwrap(),
actor_key: generate_weak_rsa_key().unwrap(), actor_key: generate_weak_rsa_key().unwrap(),
proxy_url: None, proxy_url: None,
onion_proxy_url: None,
is_private: true, is_private: true,
} }
} }
@ -179,6 +182,7 @@ mod tests {
_url: instance_url, _url: instance_url,
actor_key: instance_rsa_key, actor_key: instance_rsa_key,
proxy_url: None, proxy_url: None,
onion_proxy_url: None,
is_private: true, is_private: true,
}; };
@ -198,6 +202,7 @@ mod tests {
_url: instance_url, _url: instance_url,
actor_key: instance_rsa_key, actor_key: instance_rsa_key,
proxy_url: None, proxy_url: None,
onion_proxy_url: None,
is_private: true, is_private: true,
}; };

View file

@ -3,4 +3,5 @@ use serde::Deserialize;
#[derive(Clone, Default, Deserialize)] #[derive(Clone, Default, Deserialize)]
pub struct FederationConfig { pub struct FederationConfig {
pub proxy_url: Option<String>, pub proxy_url: Option<String>,
pub onion_proxy_url: Option<String>,
} }

View file

@ -7,7 +7,10 @@ use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use mitra_config::Instance; use mitra_config::Instance;
use mitra_utils::crypto_rsa::deserialize_private_key; use mitra_utils::{
crypto_rsa::deserialize_private_key,
urls::get_hostname,
};
use crate::database::{ use crate::database::{
DatabaseClient, DatabaseClient,
@ -47,18 +50,28 @@ pub enum DelivererError {
#[error("activity serialization error")] #[error("activity serialization error")]
SerializationError(#[from] serde_json::Error), SerializationError(#[from] serde_json::Error),
#[error("inavlid URL")]
UrlError(#[from] url::ParseError),
#[error(transparent)] #[error(transparent)]
RequestError(#[from] reqwest::Error), RequestError(#[from] reqwest::Error),
#[error("http error {0:?}")] #[error("http error {0:?}")]
HttpError(reqwest::StatusCode), HttpError(reqwest::StatusCode),
#[error(transparent)]
DatabaseError(#[from] DatabaseError),
} }
fn build_client(instance: &Instance) -> reqwest::Result<Client> { fn build_client(
build_federation_client(instance, DELIVERER_TIMEOUT) instance: &Instance,
request_uri: &str,
) -> Result<Client, DelivererError> {
let hostname = get_hostname(request_uri)?;
let is_onion = hostname.ends_with(".onion");
let client = build_federation_client(
instance,
is_onion,
DELIVERER_TIMEOUT,
)?;
Ok(client)
} }
async fn send_activity( async fn send_activity(
@ -76,7 +89,7 @@ async fn send_activity(
actor_key_id, actor_key_id,
)?; )?;
let client = build_client(instance)?; let client = build_client(instance, inbox_url)?;
let request = client.post(inbox_url) let request = client.post(inbox_url)
.header("Host", headers.host) .header("Host", headers.host)
.header("Date", headers.date) .header("Date", headers.date)

View file

@ -6,7 +6,7 @@ use serde_json::Value;
use mitra_config::Instance; use mitra_config::Instance;
use mitra_utils::{ use mitra_utils::{
files::sniff_media_type, files::sniff_media_type,
urls::guess_protocol, urls::{get_hostname, guess_protocol},
}; };
use crate::activitypub::{ use crate::activitypub::{
@ -31,6 +31,9 @@ pub enum FetchError {
#[error(transparent)] #[error(transparent)]
SignatureError(#[from] HttpSignatureError), SignatureError(#[from] HttpSignatureError),
#[error("inavlid URL")]
UrlError(#[from] url::ParseError),
#[error(transparent)] #[error(transparent)]
RequestError(#[from] reqwest::Error), RequestError(#[from] reqwest::Error),
@ -50,8 +53,18 @@ pub enum FetchError {
OtherError(&'static str), OtherError(&'static str),
} }
fn build_client(instance: &Instance) -> reqwest::Result<Client> { fn build_client(
build_federation_client(instance, FETCHER_TIMEOUT) instance: &Instance,
request_uri: &str,
) -> Result<Client, FetchError> {
let hostname = get_hostname(request_uri)?;
let is_onion = hostname.ends_with(".onion");
let client = build_federation_client(
instance,
is_onion,
FETCHER_TIMEOUT,
)?;
Ok(client)
} }
fn build_request( fn build_request(
@ -75,7 +88,7 @@ async fn send_request(
url: &str, url: &str,
query_params: &[(&str, &str)], query_params: &[(&str, &str)],
) -> Result<String, FetchError> { ) -> Result<String, FetchError> {
let client = build_client(instance)?; let client = build_client(instance, url)?;
let mut request_builder = build_request(instance, client, Method::GET, url) let mut request_builder = build_request(instance, client, Method::GET, url)
.header(reqwest::header::ACCEPT, AP_MEDIA_TYPE); .header(reqwest::header::ACCEPT, AP_MEDIA_TYPE);
@ -113,7 +126,7 @@ pub async fn fetch_file(
file_max_size: usize, file_max_size: usize,
output_dir: &Path, output_dir: &Path,
) -> Result<(String, usize, Option<String>), FetchError> { ) -> Result<(String, usize, Option<String>), FetchError> {
let client = build_client(instance)?; let client = build_client(instance, url)?;
let request_builder = let request_builder =
build_request(instance, client, Method::GET, url); build_request(instance, client, Method::GET, url);
let response = request_builder.send().await?.error_for_status()?; let response = request_builder.send().await?.error_for_status()?;
@ -164,7 +177,7 @@ pub async fn perform_webfinger_query(
guess_protocol(&actor_address.hostname), guess_protocol(&actor_address.hostname),
actor_address.hostname, actor_address.hostname,
); );
let client = build_client(instance)?; let client = build_client(instance, &webfinger_url)?;
let request_builder = let request_builder =
build_request(instance, client, Method::GET, &webfinger_url); build_request(instance, client, Method::GET, &webfinger_url);
let webfinger_data = request_builder let webfinger_data = request_builder

View file

@ -9,10 +9,16 @@ const CONNECTION_TIMEOUT: u64 = 30;
pub fn build_federation_client( pub fn build_federation_client(
instance: &Instance, instance: &Instance,
is_onion: bool,
timeout: u64, timeout: u64,
) -> reqwest::Result<Client> { ) -> reqwest::Result<Client> {
let mut client_builder = Client::builder(); let mut client_builder = Client::builder();
if let Some(ref proxy_url) = instance.proxy_url { let mut maybe_proxy_url = instance.proxy_url.as_ref();
if is_onion {
maybe_proxy_url = maybe_proxy_url
.or(instance.onion_proxy_url.as_ref());
};
if let Some(proxy_url) = maybe_proxy_url {
let proxy = Proxy::all(proxy_url)?; let proxy = Proxy::all(proxy_url)?;
client_builder = client_builder.proxy(proxy); client_builder = client_builder.proxy(proxy);
}; };