From 35ce50268a93d729ed8d95d189fe2e62219c0b35 Mon Sep 17 00:00:00 2001 From: Jade Ellis Date: Sat, 7 Dec 2024 23:11:04 +0000 Subject: [PATCH] add support for binding to a specific interface for url previews This is helpful to, for example, bind to an interface that can only access the public internet. The resulting setup is less maintenance-heavy / error-prone than manually maintaining a deny/ allowlist to protect internal resources. Signed-off-by: Jade Ellis --- conduwuit-example.toml | 10 ++++++++++ src/core/config/mod.rs | 19 +++++++++++++++++++ src/service/client/mod.rs | 14 ++++++++++---- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/conduwuit-example.toml b/conduwuit-example.toml index aee29f928..72345377e 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1117,6 +1117,16 @@ # #ip_range_denylist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", +# Optional interface to bind to with SO_BINDTODEVICE for URL previews. +# If not set, it will not bind to a specific interface. +# This uses [`reqwest::ClientBuilder::interface`] under the hood. +# +# To list the interfaces on your system, use the command `ip link show` +# +# Example: `"eth0"` +# +#url_preview_bound_interface = false + # Vector list of domains allowed to send requests to for URL previews. # Defaults to none. Note: this is a *contains* match, not an explicit # match. Putting "google.com" will match "https://google.com" and diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index cb8f3b876..c613c8197 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1250,6 +1250,16 @@ pub struct Config { #[serde(default = "default_ip_range_denylist")] pub ip_range_denylist: Vec, + /// Optional interface to bind to with SO_BINDTODEVICE for URL previews. + /// If not set, it will not bind to a specific interface. + /// This uses [`reqwest::ClientBuilder::interface`] under the hood. + /// + /// To list the interfaces on your system, use the command `ip link show` + /// + /// Example: `"eth0"` + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + pub url_preview_bound_interface: Option, + /// Vector list of domains allowed to send requests to for URL previews. /// Defaults to none. Note: this is a *contains* match, not an explicit /// match. Putting "google.com" will match "https://google.com" and @@ -1960,6 +1970,15 @@ impl fmt::Display for Config { line("Forbidden room aliases", { &self.forbidden_alias_names.patterns().iter().join(", ") }); + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + line( + "URL preview bound interface", + if let Some(interface) = &self.url_preview_bound_interface { + interface + } else { + "not set" + }, + ); line( "URL preview domain contains allowlist", &self.url_preview_domain_contains_allowlist.join(", "), diff --git a/src/service/client/mod.rs b/src/service/client/mod.rs index 2794efc1b..0b3bfd653 100644 --- a/src/service/client/mod.rs +++ b/src/service/client/mod.rs @@ -25,15 +25,21 @@ impl crate::Service for Service { let config = &args.server.config; let resolver = args.require::("resolver"); + let mut url_preview_builder = base(config)? + .dns_resolver(resolver.resolver.clone()) + .redirect(redirect::Policy::limited(3)); + + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + if let Some(interface) = &config.url_preview_bound_interface { + url_preview_builder = url_preview_builder.interface(&interface); + } + Ok(Arc::new(Self { default: base(config)? .dns_resolver(resolver.resolver.clone()) .build()?, - url_preview: base(config)? - .dns_resolver(resolver.resolver.clone()) - .redirect(redirect::Policy::limited(3)) - .build()?, + url_preview: url_preview_builder.build()?, extern_media: base(config)? .dns_resolver(resolver.resolver.clone())