diff --git a/synapse/config/repository.py b/synapse/config/repository.py
index ecb3edbe3a..655f06505b 100644
--- a/synapse/config/repository.py
+++ b/synapse/config/repository.py
@@ -137,6 +137,10 @@ class ContentRepositoryConfig(Config):
self.max_image_pixels = self.parse_size(config.get("max_image_pixels", "32M"))
self.max_spider_size = self.parse_size(config.get("max_spider_size", "10M"))
+ self.prevent_media_downloads_from = config.get(
+ "prevent_media_downloads_from", []
+ )
+
self.media_store_path = self.ensure_directory(
config.get("media_store_path", "media_store")
)
diff --git a/synapse/media/media_repository.py b/synapse/media/media_repository.py
index b81e3c2b0c..e81c987b10 100644
--- a/synapse/media/media_repository.py
+++ b/synapse/media/media_repository.py
@@ -93,6 +93,7 @@ class MediaRepository:
self.federation_domain_whitelist = (
hs.config.federation.federation_domain_whitelist
)
+ self.prevent_media_downloads_from = hs.config.media.prevent_media_downloads_from
# List of StorageProviders where we should search for media and
# potentially upload to.
@@ -276,6 +277,14 @@ class MediaRepository:
):
raise FederationDeniedError(server_name)
+ # Don't let users download media from domains listed in the config, even
+ # if we might have the media to serve. This is Trust & Safety tooling to
+ # block some servers' media from being accessible to local users.
+ # See `prevent_media_downloads_from` config docs for more info.
+ if server_name in self.prevent_media_downloads_from:
+ respond_404(request)
+ return
+
self.mark_recently_accessed(server_name, media_id)
# We linearize here to ensure that we don't try and download remote
diff --git a/synapse/rest/media/thumbnail_resource.py b/synapse/rest/media/thumbnail_resource.py
index a6396fb05a..661e604b85 100644
--- a/synapse/rest/media/thumbnail_resource.py
+++ b/synapse/rest/media/thumbnail_resource.py
@@ -60,6 +60,7 @@ class ThumbnailResource(DirectServeJsonResource):
self.media_storage = media_storage
self.dynamic_thumbnails = hs.config.media.dynamic_thumbnails
self._is_mine_server_name = hs.is_mine_server_name
+ self.prevent_media_downloads_from = hs.config.media.prevent_media_downloads_from
async def _async_render_GET(self, request: SynapseRequest) -> None:
set_cors_headers(request)
@@ -82,6 +83,14 @@ class ThumbnailResource(DirectServeJsonResource):
)
self.media_repo.mark_recently_accessed(None, media_id)
else:
+ # Don't let users download media from configured domains, even if it
+ # is already downloaded. This is Trust & Safety tooling to make some
+ # media inaccessible to local users.
+ # See `prevent_media_downloads_from` config docs for more info.
+ if server_name in self.prevent_media_downloads_from:
+ respond_404(request)
+ return
+
if self.dynamic_thumbnails:
await self._select_or_generate_remote_thumbnail(
request, server_name, media_id, width, height, method, m_type
|