extend x-platform support for binding URL previews to interfaces via address
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
52cee65748
commit
f0a1aaf7bc
6 changed files with 74 additions and 28 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -824,6 +824,7 @@ dependencies = [
|
||||||
"conduit_core",
|
"conduit_core",
|
||||||
"conduit_database",
|
"conduit_database",
|
||||||
"const-str",
|
"const-str",
|
||||||
|
"either",
|
||||||
"futures",
|
"futures",
|
||||||
"hickory-resolver",
|
"hickory-resolver",
|
||||||
"http",
|
"http",
|
||||||
|
|
|
@ -1117,13 +1117,15 @@
|
||||||
#
|
#
|
||||||
#ip_range_denylist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12",
|
#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.
|
# Optional IP address or network interface-name to bind as the source of
|
||||||
# If not set, it will not bind to a specific interface.
|
# URL preview requests. If not set, it will not bind to a specific
|
||||||
# This uses [`reqwest::ClientBuilder::interface`] under the hood.
|
# address or interface.
|
||||||
#
|
#
|
||||||
# To list the interfaces on your system, use the command `ip link show`
|
# Interface names only supported on Linux, Android, and Fuchsia platforms;
|
||||||
|
# all other platforms can specify the IP address. To list the interfaces
|
||||||
|
# on your system, use the command `ip link show`.
|
||||||
#
|
#
|
||||||
# Example: `"eth0"`
|
# example: `"eth0"` or `"1.2.3.4"`
|
||||||
#
|
#
|
||||||
#url_preview_bound_interface =
|
#url_preview_bound_interface =
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use std::env::consts::OS;
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
use figment::Figment;
|
use figment::Figment;
|
||||||
|
|
||||||
use super::DEPRECATED_KEYS;
|
use super::DEPRECATED_KEYS;
|
||||||
|
@ -191,6 +194,15 @@ For security and safety reasons, conduwuit will shut down. If you are extra sure
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(Either::Right(_)) = config.url_preview_bound_interface.as_ref() {
|
||||||
|
if !matches!(OS, "android" | "fuchsia" | "linux") {
|
||||||
|
return Err!(Config(
|
||||||
|
"url_preview_bound_interface",
|
||||||
|
"Not a valid IP address. Interface names not supported on {OS}."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1250,15 +1250,19 @@ pub struct Config {
|
||||||
#[serde(default = "default_ip_range_denylist")]
|
#[serde(default = "default_ip_range_denylist")]
|
||||||
pub ip_range_denylist: Vec<String>,
|
pub ip_range_denylist: Vec<String>,
|
||||||
|
|
||||||
/// Optional interface to bind to with SO_BINDTODEVICE for URL previews.
|
/// Optional IP address or network interface-name to bind as the source of
|
||||||
/// If not set, it will not bind to a specific interface.
|
/// URL preview requests. If not set, it will not bind to a specific
|
||||||
/// This uses [`reqwest::ClientBuilder::interface`] under the hood.
|
/// address or interface.
|
||||||
///
|
///
|
||||||
/// To list the interfaces on your system, use the command `ip link show`
|
/// Interface names only supported on Linux, Android, and Fuchsia platforms;
|
||||||
|
/// all other platforms can specify the IP address. To list the interfaces
|
||||||
|
/// on your system, use the command `ip link show`.
|
||||||
///
|
///
|
||||||
/// Example: `"eth0"`
|
/// example: `"eth0"` or `"1.2.3.4"`
|
||||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
///
|
||||||
pub url_preview_bound_interface: Option<String>,
|
/// default:
|
||||||
|
#[serde(default, with = "either::serde_untagged_optional")]
|
||||||
|
pub url_preview_bound_interface: Option<Either<IpAddr, String>>,
|
||||||
|
|
||||||
/// Vector list of domains allowed to send requests to for URL previews.
|
/// Vector list of domains allowed to send requests to for URL previews.
|
||||||
/// Defaults to none. Note: this is a *contains* match, not an explicit
|
/// Defaults to none. Note: this is a *contains* match, not an explicit
|
||||||
|
@ -1970,14 +1974,15 @@ impl fmt::Display for Config {
|
||||||
line("Forbidden room aliases", {
|
line("Forbidden room aliases", {
|
||||||
&self.forbidden_alias_names.patterns().iter().join(", ")
|
&self.forbidden_alias_names.patterns().iter().join(", ")
|
||||||
});
|
});
|
||||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
||||||
line(
|
line(
|
||||||
"URL preview bound interface",
|
"URL preview bound interface",
|
||||||
if let Some(interface) = &self.url_preview_bound_interface {
|
self.url_preview_bound_interface
|
||||||
interface
|
.as_ref()
|
||||||
} else {
|
.map(Either::as_ref)
|
||||||
"not set"
|
.map(|either| either.map_left(ToString::to_string))
|
||||||
},
|
.map(Either::either_into::<String>)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_str(),
|
||||||
);
|
);
|
||||||
line(
|
line(
|
||||||
"URL preview domain contains allowlist",
|
"URL preview domain contains allowlist",
|
||||||
|
|
|
@ -47,6 +47,7 @@ bytes.workspace = true
|
||||||
conduit-core.workspace = true
|
conduit-core.workspace = true
|
||||||
conduit-database.workspace = true
|
conduit-database.workspace = true
|
||||||
const-str.workspace = true
|
const-str.workspace = true
|
||||||
|
either.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
hickory-resolver.workspace = true
|
hickory-resolver.workspace = true
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
use conduit::{err, implement, trace, Config, Result};
|
use conduit::{err, implement, trace, Config, Result};
|
||||||
|
use either::Either;
|
||||||
use ipaddress::IPAddress;
|
use ipaddress::IPAddress;
|
||||||
use reqwest::redirect;
|
use reqwest::redirect;
|
||||||
|
|
||||||
|
@ -25,23 +26,27 @@ impl crate::Service for Service {
|
||||||
let config = &args.server.config;
|
let config = &args.server.config;
|
||||||
let resolver = args.require::<resolver::Service>("resolver");
|
let resolver = args.require::<resolver::Service>("resolver");
|
||||||
|
|
||||||
let url_preview_builder = base(config)?
|
let url_preview_bind_addr = config
|
||||||
.dns_resolver(resolver.resolver.clone())
|
.url_preview_bound_interface
|
||||||
.redirect(redirect::Policy::limited(3));
|
.clone()
|
||||||
|
.and_then(Either::left);
|
||||||
|
|
||||||
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
let url_preview_bind_iface = config
|
||||||
let url_preview_builder = if let Some(interface) = &config.url_preview_bound_interface {
|
.url_preview_bound_interface
|
||||||
url_preview_builder.interface(interface)
|
.clone()
|
||||||
} else {
|
.and_then(Either::right);
|
||||||
url_preview_builder
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Arc::new(Self {
|
Ok(Arc::new(Self {
|
||||||
default: base(config)?
|
default: base(config)?
|
||||||
.dns_resolver(resolver.resolver.clone())
|
.dns_resolver(resolver.resolver.clone())
|
||||||
.build()?,
|
.build()?,
|
||||||
|
|
||||||
url_preview: url_preview_builder.build()?,
|
url_preview: base(config)
|
||||||
|
.and_then(|builder| builder_interface(builder, url_preview_bind_iface.as_deref()))?
|
||||||
|
.local_address(url_preview_bind_addr)
|
||||||
|
.dns_resolver(resolver.resolver.clone())
|
||||||
|
.redirect(redirect::Policy::limited(3))
|
||||||
|
.build()?,
|
||||||
|
|
||||||
extern_media: base(config)?
|
extern_media: base(config)?
|
||||||
.dns_resolver(resolver.resolver.clone())
|
.dns_resolver(resolver.resolver.clone())
|
||||||
|
@ -172,6 +177,26 @@ fn base(config: &Config) -> Result<reqwest::ClientBuilder> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
||||||
|
fn builder_interface(builder: reqwest::ClientBuilder, config: Option<&str>) -> Result<reqwest::ClientBuilder> {
|
||||||
|
if let Some(iface) = config {
|
||||||
|
Ok(builder.interface(iface))
|
||||||
|
} else {
|
||||||
|
Ok(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
|
||||||
|
fn builder_interface(builder: reqwest::ClientBuilder, config: Option<&str>) -> Result<reqwest::ClientBuilder> {
|
||||||
|
use conduit::Err;
|
||||||
|
|
||||||
|
if let Some(iface) = config {
|
||||||
|
Err!("Binding to network-interface {iface:?} by name is not supported on this platform.")
|
||||||
|
} else {
|
||||||
|
Ok(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[implement(Service)]
|
#[implement(Service)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue