mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-06-27 16:35:59 +00:00
Change URL preview setting from bool to a mode, and add support for an allowlist
This commit is contained in:
parent
bb4cade9fd
commit
61fd9166f6
5 changed files with 90 additions and 26 deletions
|
@ -47,9 +47,6 @@ registration_token = ""
|
|||
allow_check_for_updates = true
|
||||
allow_federation = true
|
||||
|
||||
# Allows clients to request a URL preview
|
||||
allow_url_preview = false
|
||||
|
||||
# Enable the display name lightning bolt on registration.
|
||||
enable_lightning_bolt = true
|
||||
|
||||
|
@ -69,6 +66,13 @@ trusted_servers = ["matrix.org"]
|
|||
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
|
||||
#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it.
|
||||
|
||||
# possible URL preview modes:
|
||||
# None: previews disabled
|
||||
# All: previews for any URL allowed
|
||||
# Allowlist: only domains in `url_preview_allowlist` are allowed
|
||||
url_preview_mode = "None"
|
||||
url_preview_allowlist = ["google.com", "youtube.com", "www.youtube.com"]
|
||||
|
||||
[global.well_known]
|
||||
# Conduit handles the /.well-known/matrix/* endpoints, making both clients and servers try to access conduit with the host
|
||||
# server_name and port 443 by default.
|
||||
|
|
10
debian/postinst
vendored
10
debian/postinst
vendored
|
@ -84,9 +84,6 @@ allow_check_for_updates = true
|
|||
# Enable the display name lightning bolt on registration.
|
||||
enable_lightning_bolt = true
|
||||
|
||||
# Allows clients to request a URL preview
|
||||
allow_url_preview = false
|
||||
|
||||
# Servers listed here will be used to gather public keys of other servers.
|
||||
# Generally, copying this exactly should be enough. (Currently, Conduit doesn't
|
||||
# support batched key requests, so this list should only contain Synapse
|
||||
|
@ -99,6 +96,13 @@ trusted_servers = ["matrix.org"]
|
|||
#
|
||||
# [0]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
|
||||
#log = "..."
|
||||
|
||||
# possible URL preview modes:
|
||||
# None: previews disabled
|
||||
# All: previews for any URL allowed
|
||||
# Allowlist: only domains in \`url_preview_allowlist\` are allowed
|
||||
url_preview_mode = "None"
|
||||
url_preview_allowlist = ["google.com", "youtube.com", "www.youtube.com"]
|
||||
EOF
|
||||
fi
|
||||
;;
|
||||
|
|
|
@ -11,9 +11,11 @@ use ruma::api::client::{
|
|||
|
||||
#[cfg(feature = "url_preview")]
|
||||
use {
|
||||
crate::config::UrlPreviewMode,
|
||||
crate::service::media::UrlPreviewData,
|
||||
webpage::HTML,
|
||||
std::{io::Cursor, net::IpAddr, sync::Arc, time::Duration},
|
||||
reqwest::Url,
|
||||
std::{io::Cursor, net::IpAddr, sync::Arc},
|
||||
tokio::sync::Notify,
|
||||
image::io::Reader as ImgReader,
|
||||
};
|
||||
|
@ -123,9 +125,9 @@ fn url_request_allowed(addr: &IpAddr) -> bool {
|
|||
}
|
||||
|
||||
#[cfg(feature = "url_preview")]
|
||||
async fn request_url_preview(url: String) -> Result<UrlPreviewData> {
|
||||
async fn request_url_preview(url: &str) -> Result<UrlPreviewData> {
|
||||
let client = services().globals.default_client();
|
||||
let response = client.head(&url).send().await?;
|
||||
let response = client.head(url).send().await?;
|
||||
|
||||
if !response
|
||||
.remote_addr()
|
||||
|
@ -151,8 +153,8 @@ async fn request_url_preview(url: String) -> Result<UrlPreviewData> {
|
|||
}
|
||||
};
|
||||
let data = match content_type {
|
||||
html if html.starts_with("text/html") => download_html(&client, &url).await?,
|
||||
img if img.starts_with("image/") => download_image(&client, &url).await?,
|
||||
html if html.starts_with("text/html") => download_html(&client, url).await?,
|
||||
img if img.starts_with("image/") => download_image(&client, url).await?,
|
||||
_ => {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Unknown,
|
||||
|
@ -161,14 +163,14 @@ async fn request_url_preview(url: String) -> Result<UrlPreviewData> {
|
|||
}
|
||||
};
|
||||
|
||||
services().media.set_url_preview(&url, &data).await?;
|
||||
services().media.set_url_preview(url, &data).await?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "url_preview")]
|
||||
async fn get_url_preview(url: String) -> Result<UrlPreviewData> {
|
||||
if let Some(preview) = services().media.get_url_preview(&url).await {
|
||||
async fn get_url_preview(url: &str) -> Result<UrlPreviewData> {
|
||||
if let Some(preview) = services().media.get_url_preview(url).await {
|
||||
return Ok(preview);
|
||||
}
|
||||
|
||||
|
@ -177,7 +179,7 @@ async fn get_url_preview(url: String) -> Result<UrlPreviewData> {
|
|||
.url_preview_requests
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&url)
|
||||
.get(url)
|
||||
.cloned();
|
||||
|
||||
match notif_opt {
|
||||
|
@ -188,15 +190,15 @@ async fn get_url_preview(url: String) -> Result<UrlPreviewData> {
|
|||
.url_preview_requests
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(url.clone(), notifier.clone());
|
||||
.insert(url.to_string(), notifier.clone());
|
||||
}
|
||||
|
||||
let data = request_url_preview(url.clone()).await;
|
||||
let data = request_url_preview(url).await;
|
||||
|
||||
notifier.notify_waiters();
|
||||
|
||||
{
|
||||
services().media.url_preview_requests.write().unwrap().remove(&url);
|
||||
services().media.url_preview_requests.write().unwrap().remove(url);
|
||||
}
|
||||
|
||||
data
|
||||
|
@ -208,7 +210,7 @@ async fn get_url_preview(url: String) -> Result<UrlPreviewData> {
|
|||
notifier.await;
|
||||
|
||||
services().media
|
||||
.get_url_preview(&url)
|
||||
.get_url_preview(url)
|
||||
.await
|
||||
.ok_or(Error::BadRequest(
|
||||
ErrorKind::Unknown,
|
||||
|
@ -218,6 +220,29 @@ async fn get_url_preview(url: String) -> Result<UrlPreviewData> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "url_preview")]
|
||||
fn url_preview_allowed(url_str: &str) -> bool {
|
||||
let url = match Url::parse(url_str) {
|
||||
Ok(u) => u,
|
||||
Err(_) => return false,
|
||||
};
|
||||
if ["http", "https"].iter().all(|&scheme| scheme != url.scheme().to_lowercase()) {
|
||||
return false;
|
||||
}
|
||||
match services().globals.url_preview_mode() {
|
||||
UrlPreviewMode::All => true,
|
||||
UrlPreviewMode::None => false,
|
||||
UrlPreviewMode::Allowlist => {
|
||||
match url.host_str() {
|
||||
None => false,
|
||||
Some(host) => {
|
||||
services().globals.url_preview_allowlist().contains(&host.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/r0/preview_url`
|
||||
///
|
||||
/// Returns URL preview.
|
||||
|
@ -225,14 +250,15 @@ async fn get_url_preview(url: String) -> Result<UrlPreviewData> {
|
|||
pub async fn get_media_preview_route(
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
if !services().globals.allow_url_preview() {
|
||||
let url = &body.url;
|
||||
if !url_preview_allowed(url) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Unknown,
|
||||
"Previewing URL not allowed",
|
||||
));
|
||||
}
|
||||
|
||||
if let Ok(preview) = get_url_preview(body.url.clone()).await {
|
||||
if let Ok(preview) = get_url_preview(url).await {
|
||||
let res = serde_json::value::to_raw_value(&preview).expect("Converting to JSON failed");
|
||||
return Ok(get_media_preview::v3::Response::from_raw_value(res));
|
||||
}
|
||||
|
|
|
@ -13,6 +13,23 @@ mod proxy;
|
|||
|
||||
use self::proxy::ProxyConfig;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
pub enum UrlPreviewMode {
|
||||
All,
|
||||
None,
|
||||
Allowlist,
|
||||
}
|
||||
|
||||
impl ToString for UrlPreviewMode {
|
||||
fn to_string(&self) -> String {
|
||||
match *self {
|
||||
UrlPreviewMode::All => "All".to_string(),
|
||||
UrlPreviewMode::None => "None".to_string(),
|
||||
UrlPreviewMode::Allowlist => "Allowlist".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
#[serde(default = "default_address")]
|
||||
|
@ -53,8 +70,6 @@ pub struct Config {
|
|||
pub allow_encryption: bool,
|
||||
#[serde(default = "false_fn")]
|
||||
pub allow_federation: bool,
|
||||
#[serde(default = "false_fn")]
|
||||
pub allow_url_preview: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub allow_room_creation: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
|
@ -87,6 +102,11 @@ pub struct Config {
|
|||
|
||||
pub emergency_password: Option<String>,
|
||||
|
||||
#[serde(default = "default_url_preview_mode")]
|
||||
pub url_preview_mode: UrlPreviewMode,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub url_preview_allowlist: Vec<String>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub catchall: BTreeMap<String, IgnoredAny>,
|
||||
}
|
||||
|
@ -186,7 +206,6 @@ impl fmt::Display for Config {
|
|||
),
|
||||
("Allow encryption", &self.allow_encryption.to_string()),
|
||||
("Allow federation", &self.allow_federation.to_string()),
|
||||
("Allow URL preview", &self.allow_url_preview.to_string()),
|
||||
("Allow room creation", &self.allow_room_creation.to_string()),
|
||||
(
|
||||
"JWT secret",
|
||||
|
@ -235,6 +254,8 @@ impl fmt::Display for Config {
|
|||
}),
|
||||
("Well-known server name", well_known_server.as_str()),
|
||||
("Well-known client URL", &self.well_known_client()),
|
||||
("URL preview mode", &self.url_preview_mode.to_string()),
|
||||
("URL preview allowlist", &self.url_preview_allowlist.join(", ")),
|
||||
];
|
||||
|
||||
let mut msg: String = "Active config values:\n\n".to_owned();
|
||||
|
@ -315,3 +336,7 @@ fn default_openid_token_ttl() -> u64 {
|
|||
pub fn default_default_room_version() -> RoomVersionId {
|
||||
RoomVersionId::V10
|
||||
}
|
||||
|
||||
pub fn default_url_preview_mode() -> UrlPreviewMode {
|
||||
UrlPreviewMode::None
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use futures_util::FutureExt;
|
|||
use hickory_resolver::TokioAsyncResolver;
|
||||
use hyper_util::client::legacy::connect::dns::{GaiResolver, Name as HyperName};
|
||||
use reqwest::dns::{Addrs, Name, Resolve, Resolving};
|
||||
use crate::config::UrlPreviewMode;
|
||||
use ruma::{
|
||||
api::{client::sync::sync_events, federation::discovery::ServerSigningKeys},
|
||||
DeviceId, RoomVersionId, ServerName, UserId,
|
||||
|
@ -324,8 +325,12 @@ impl Service {
|
|||
self.config.allow_federation
|
||||
}
|
||||
|
||||
pub fn allow_url_preview(&self) -> bool {
|
||||
self.config.allow_url_preview
|
||||
pub fn url_preview_mode(&self) -> UrlPreviewMode {
|
||||
self.config.url_preview_mode
|
||||
}
|
||||
|
||||
pub fn url_preview_allowlist(&self) -> &Vec<String> {
|
||||
&self.config.url_preview_allowlist
|
||||
}
|
||||
|
||||
pub fn allow_room_creation(&self) -> bool {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue