diff --git a/CHANGELOG.md b/CHANGELOG.md index 999bc9eb259793f692286753a8003cfc64c57831..e3f61bf099648161aea01066d117a904c6585863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +* Added a `federation.ignoredHosts` config option to block media from individual homeservers. + ### Removed * IPFS support has been removed due to maintenance burden. diff --git a/api/_responses/errors.go b/api/_responses/errors.go index deb93b29af320d492c13bdeb4234769c9c02e938..1cee50216ea8075e8b2f45b993130472226f421a 100644 --- a/api/_responses/errors.go +++ b/api/_responses/errors.go @@ -40,6 +40,10 @@ func AuthFailed() *ErrorResponse { return &ErrorResponse{common.ErrCodeUnknownToken, "Authentication Failed", common.ErrCodeUnknownToken} } +func MediaBlocked() *ErrorResponse { + return &ErrorResponse{common.ErrCodeNotFound, "Media blocked or not found", common.ErrCodeForbidden} +} + func GuestAuthFailed() *ErrorResponse { return &ErrorResponse{common.ErrCodeNoGuests, "Guests cannot use this endpoint", common.ErrCodeNoGuests} } diff --git a/api/r0/download.go b/api/r0/download.go index 5b988220de6df5399e445e0ebd471356bd9af2a1..83b9232a97c5d787c3a9b346cbe7680305378829 100644 --- a/api/r0/download.go +++ b/api/r0/download.go @@ -8,6 +8,7 @@ import ( "github.com/turt2live/matrix-media-repo/api/_apimeta" "github.com/turt2live/matrix-media-repo/api/_responses" "github.com/turt2live/matrix-media-repo/api/_routers" + "github.com/turt2live/matrix-media-repo/util" "github.com/sirupsen/logrus" "github.com/turt2live/matrix-media-repo/common" @@ -52,6 +53,11 @@ func DownloadMedia(r *http.Request, rctx rcontext.RequestContext, user _apimeta. "allowRemote": downloadRemote, }) + if !util.IsGlobalAdmin(user.UserId) && util.IsHostIgnored(server) { + rctx.Log.Warn("Request blocked due to domain being ignored.") + return _responses.MediaBlocked() + } + streamedMedia, err := download_controller.GetMedia(server, mediaId, downloadRemote, false, rctx) if err != nil { if err == common.ErrMediaNotFound { diff --git a/api/r0/thumbnail.go b/api/r0/thumbnail.go index 7e56131e9fccb8de2c4143c7bc2fb2db6fc04426..86694315dad38f2ac079191c71d52808081b62e2 100644 --- a/api/r0/thumbnail.go +++ b/api/r0/thumbnail.go @@ -5,6 +5,7 @@ import ( "github.com/turt2live/matrix-media-repo/api/_apimeta" "github.com/turt2live/matrix-media-repo/api/_responses" "github.com/turt2live/matrix-media-repo/api/_routers" + "github.com/turt2live/matrix-media-repo/util" "net/http" "strconv" @@ -39,6 +40,11 @@ func ThumbnailMedia(r *http.Request, rctx rcontext.RequestContext, user _apimeta "allowRemote": downloadRemote, }) + if !util.IsGlobalAdmin(user.UserId) && util.IsHostIgnored(server) { + rctx.Log.Warn("Request blocked due to domain being ignored.") + return _responses.MediaBlocked() + } + widthStr := r.URL.Query().Get("width") heightStr := r.URL.Query().Get("height") method := r.URL.Query().Get("method") diff --git a/api/unstable/info.go b/api/unstable/info.go index e47542e32b159b5f7074f01329e15fc9afdb4cba..3bdafb245ea1fc52c199886b36446109bbbe8572 100644 --- a/api/unstable/info.go +++ b/api/unstable/info.go @@ -12,6 +12,7 @@ import ( "github.com/turt2live/matrix-media-repo/api/_apimeta" "github.com/turt2live/matrix-media-repo/api/_responses" "github.com/turt2live/matrix-media-repo/api/_routers" + "github.com/turt2live/matrix-media-repo/util" "github.com/turt2live/matrix-media-repo/util/stream_util" "github.com/disintegration/imaging" @@ -73,6 +74,11 @@ func MediaInfo(r *http.Request, rctx rcontext.RequestContext, user _apimeta.User "allowRemote": downloadRemote, }) + if !util.IsGlobalAdmin(user.UserId) && util.IsHostIgnored(server) { + rctx.Log.Warn("Request blocked due to domain being ignored.") + return _responses.MediaBlocked() + } + streamedMedia, err := download_controller.GetMedia(server, mediaId, downloadRemote, true, rctx) if err != nil { if err == common.ErrMediaNotFound { diff --git a/api/unstable/local_copy.go b/api/unstable/local_copy.go index 74166e60af6778a7cf350aaeea6bbdc54d2d3b2e..fa5a1f03d8b383332ab8e6547e4280e8e765e38e 100644 --- a/api/unstable/local_copy.go +++ b/api/unstable/local_copy.go @@ -5,6 +5,7 @@ import ( "github.com/turt2live/matrix-media-repo/api/_apimeta" "github.com/turt2live/matrix-media-repo/api/_responses" "github.com/turt2live/matrix-media-repo/api/_routers" + "github.com/turt2live/matrix-media-repo/util" "github.com/turt2live/matrix-media-repo/util/stream_util" "net/http" @@ -42,6 +43,11 @@ func LocalCopy(r *http.Request, rctx rcontext.RequestContext, user _apimeta.User "allowRemote": downloadRemote, }) + if !util.IsGlobalAdmin(user.UserId) && util.IsHostIgnored(server) { + rctx.Log.Warn("Request blocked due to domain being ignored.") + return _responses.MediaBlocked() + } + // TODO: There's a lot of room for improvement here. Instead of re-uploading media, we should just update the DB. streamedMedia, err := download_controller.GetMedia(server, mediaId, downloadRemote, true, rctx) diff --git a/common/config/models_main.go b/common/config/models_main.go index bde7479dd82ab2a4b56954f7b4b8086e76b3e655..d8f5948aaa41de78d8c99f755b3b8243aedb268d 100644 --- a/common/config/models_main.go +++ b/common/config/models_main.go @@ -64,7 +64,8 @@ type SharedSecretConfig struct { } type FederationConfig struct { - BackoffAt int `yaml:"backoffAt"` + BackoffAt int `yaml:"backoffAt"` + IgnoredHosts []string `yaml:"ignoredHosts,flow"` } type PluginConfig struct { diff --git a/config.sample.yaml b/config.sample.yaml index 9837491e6ecb6540a82693c56b88feca75ab0bdd..e3570bb466fda47ce249774f9748f04a6ce54fbc 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -41,6 +41,18 @@ federation: # the remote server do not count towards this. backoffAt: 20 + # The domains the media repo should never serve media for. Existing media already stored from + # these domains will remain, however will not be downloadable without a data export. Media + # repo administrators will bypass this check. Admin APIs will still work for media on these + # domains. + # + # This will not prevent the listed domains from accessing media on this media repo - it only + # stops users on *this* media repo from accessing media originally uploaded to the listed domains. + # + # Note: Adding domains controlled by the media repo itself to this list is not advisable. + ignoredHosts: + - example.org + # The database configuration for the media repository # Do NOT put your homeserver's existing database credentials here. Create a new database and # user instead. Using the same server is fine, just not the same username and database. diff --git a/util/config.go b/util/config.go index 18bd40e1feedeaa8a1b67b56a20916a3c95a6ea5..306495da2906d569430cacc3225f9cf731473281 100644 --- a/util/config.go +++ b/util/config.go @@ -1,6 +1,8 @@ package util import ( + "strings" + "github.com/turt2live/matrix-media-repo/common/config" ) @@ -18,3 +20,13 @@ func IsGlobalAdmin(userId string) bool { return false } + +func IsHostIgnored(serverName string) bool { + serverName = strings.ToLower(serverName) + for _, host := range config.Get().Federation.IgnoredHosts { + if strings.ToLower(host) == serverName { + return true + } + } + return false +}