From f9e878ec6065aaf8a660a5e49a1f3a980c00f140 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Fri, 24 Jul 2020 22:00:10 -0600 Subject: [PATCH] Support enough to get Complement to run the tests --- Complement.Dockerfile | 3 +- cmd/complement_hs/main.go | 92 +++++++- docker/complement-run.sh | 6 +- docker/complement.yaml | 467 +------------------------------------- matrix/federation.go | 68 ++++-- 5 files changed, 149 insertions(+), 487 deletions(-) diff --git a/Complement.Dockerfile b/Complement.Dockerfile index f9b316fb..e525d8c8 100644 --- a/Complement.Dockerfile +++ b/Complement.Dockerfile @@ -23,6 +23,7 @@ COPY ./docker/complement.yaml /data/media-repo.yaml ENV REPO_CONFIG=/data/media-repo.yaml ENV SERVER_NAME=localhost ENV PGDATA=/data/pgdata +ENV MEDIA_REPO_UNSAFE_FEDERATION=true COPY ./docker/complement.sh ./docker/complement-run.sh /usr/local/bin/ RUN dos2unix /usr/local/bin/complement.sh /usr/local/bin/complement-run.sh @@ -35,8 +36,6 @@ RUN mkdir -p /run/postgresql RUN chown postgres:postgres /data/pgdata RUN chown postgres:postgres /run/postgresql RUN su postgres -c initdb -RUN openssl req -new -newkey rsa:1024 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=${SERVER_NAME}" -keyout /data/server.key -out /data/server.crt -RUN sed -i "s/SERVER_NAME/${SERVER_NAME}/g" /data/media-repo.yaml RUN sh /usr/local/bin/complement.sh CMD /usr/local/bin/complement-run.sh \ No newline at end of file diff --git a/cmd/complement_hs/main.go b/cmd/complement_hs/main.go index d98fe59c..ff723e72 100644 --- a/cmd/complement_hs/main.go +++ b/cmd/complement_hs/main.go @@ -1,23 +1,109 @@ package main import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" "log" "net/http" "os" "os/signal" + "strconv" + "strings" "sync" "github.com/gorilla/mux" + "github.com/turt2live/matrix-media-repo/util/cleanup" ) +type VersionsResponse struct { + CSAPIVersions []string `json:"versions,flow"` +} + +type RegisterRequest struct { + DesiredUsername string `json:"username"` +} + +type RegisterResponse struct { + UserID string `json:"user_id"` + AccessToken string `json:"access_token"` +} + +type WhoamiResponse struct { + UserID string `json:"user_id"` +} + +func requestJson(r *http.Request, i interface{}) error { + b, err := ioutil.ReadAll(r.Body) + if err != nil { + return err + } + return json.Unmarshal(b, &i) +} + +func respondJson(w http.ResponseWriter, i interface{}) error { + resp, err := json.Marshal(i) + if err != nil { + return err + } + w.Header().Set("Content-Length",strconv.Itoa(len(resp))) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err = w.Write(resp) + return err +} + func main() { // Prepare local server log.Println("Preparing local server...") rtr := mux.NewRouter() rtr.HandleFunc("/_matrix/client/versions", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - _, err := w.Write([]byte("{\"versions\":[\"r0.6.0\"]}")) + defer cleanup.DumpAndCloseStream(r.Body) + err := respondJson(w, &VersionsResponse{CSAPIVersions: []string{"r0.6.0"}}) + if err != nil { + log.Fatal(err) + } + }) + rtr.HandleFunc("/_matrix/client/r0/register", func(w http.ResponseWriter, r *http.Request) { + rr := &RegisterRequest{} + err := requestJson(r, &rr) + if err != nil { + log.Fatal(err) + } + userId := fmt.Sprintf("@%s:%s", rr.DesiredUsername, os.Getenv("SERVER_NAME")) + err = respondJson(w, &RegisterResponse{ + AccessToken: userId, + UserID: userId, + }) + if err != nil { + log.Fatal(err) + } + }) + rtr.HandleFunc("/_matrix/client/r0/account/whoami", func(w http.ResponseWriter, r *http.Request) { + defer cleanup.DumpAndCloseStream(r.Body) + userId := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") // including space after Bearer. + err := respondJson(w, &WhoamiResponse{UserID: userId}) + if err != nil { + log.Fatal(err) + } + }) + rtr.PathPrefix("/_matrix/media/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Proxy to the media repo running within the container + r2, err := http.NewRequest(r.Method, "http://127.0.0.1:8228" + r.RequestURI, r.Body) + if err != nil { + log.Fatal(err) + } + r2.Host = os.Getenv("SERVER_NAME") + resp, err := http.DefaultClient.Do(r2) + if err != nil { + log.Fatal(err) + } + err = resp.Header.Write(w) + if err != nil { + log.Fatal(err) + } + _, err = io.Copy(w, resp.Body) if err != nil { log.Fatal(err) } diff --git a/docker/complement-run.sh b/docker/complement-run.sh index 4c99443d..9dbef054 100644 --- a/docker/complement-run.sh +++ b/docker/complement-run.sh @@ -1,5 +1,7 @@ #!/usr/bin/env sh +openssl req -new -newkey rsa:1024 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=${SERVER_NAME}" -keyout /data/server.key -out /data/server.crt +sed -i "s/SERVER_NAME/${SERVER_NAME}/g" /data/media-repo.yaml su postgres -c "postgres -h 0.0.0.0" & -sleep 3 +sleep 12 /usr/local/bin/media_repo & -/usr/local/bin/complement_hs \ No newline at end of file +/usr/local/bin/complement_hs diff --git a/docker/complement.yaml b/docker/complement.yaml index 4d4e3c67..e73f26a3 100644 --- a/docker/complement.yaml +++ b/docker/complement.yaml @@ -1,476 +1,21 @@ -# General repo configuration repo: bindAddress: '127.0.0.1' - port: 8000 - - # Where to store the logs, relative to where the repo is started from. Logs will be automatically - # rotated every day and held for 14 days. To disable the repo logging to files, set this to - # "-" (including quotation marks). - # - # Note: to change the log directory you'll have to restart the repository. This setting cannot be - # live reloaded. - logDirectory: logs - - # If true, the media repo will accept any X-Forwarded-For header without validation. In most cases - # this option should be left as "false". Note that the media repo already expects an X-Forwarded-For - # header, but validates it to ensure the IP being given makes sense. - trustAnyForwardedAddress: false - - # If false, the media repo will not use the X-Forwarded-Host header commonly added by reverse proxies. - # Typically this should remain as true, though in some circumstances it may need to be disabled. - # See https://github.com/turt2live/matrix-media-repo/issues/202 for more information. - useForwardedHost: true - -# Options for dealing with federation -federation: - # On a per-host basis, the number of consecutive failures in calling the host before the - # media repo will back off. This defaults to 20 if not given. Note that 404 errors from - # the remote server do not count towards this. - backoffAt: 20 - -# 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. + port: 8228 database: - # Currently only "postgres" is supported. postgres: "postgres://mediarepo:mediarepo@127.0.0.1/mediarepo?sslmode=disable" - - # The database pooling options - pool: - # The maximum number of connects to hold open. More of these allow for more concurrent - # processes to happen. - maxConnections: 25 - - # The maximum number of connects to leave idle. More of these reduces the time it takes - # to serve requests in low-traffic scenarios. - maxIdleConnections: 5 - -# The configuration for the homeservers this media repository is known to control. Servers -# not listed here will not be able to upload media. homeservers: - - name: SERVER_NAME # This should match the server_name of your homeserver, and the Host header - # provided to the media repo. - csApi: "http://localhost:8008/" # The base URL to where the homeserver can actually be reached - backoffAt: 10 # The number of consecutive failures in calling this homeserver before the - # media repository will start backing off. This defaults to 10 if not given. - adminApiKind: "matrix" # The kind of admin API the homeserver supports. If set to "matrix", - # the media repo will use the Synapse-defined endpoints under the - # unstable client-server API. When this is "synapse", the new /_synapse - # endpoints will be used instead. Unknown values are treated as the - # default, "matrix". - -# Options for controlling how access tokens work with the media repo. It is recommended that if -# you are going to use these options that the `/logout` and `/logout/all` client-server endpoints -# be proxied through this process. They will also be called on the homeserver, and the response -# sent straight through the client - they are simply used to invalidate the cache faster for -# a particular user. Without these, the access tokens might still work for a short period of time -# after the user has already invalidated them. -# -# This will also cache errors from the homeserver. -# -# Note that when this config block is used outside of a per-domain config, all hosts will be -# subject to the same cache. This also means that application services on limited homeservers -# could be authorized on the wrong domain. -# -# *************************************************************************** -# * IT IS HIGHLY RECOMMENDED TO USE PER-DOMAIN CONFIGS WITH THIS FEATURE. * -# *************************************************************************** -accessTokens: - # The maximum time a cached access token will be considered valid. Set to zero (the default) - # to disable the cache and constantly hit the homeserver. This is recommended to be set to - # 43200 (12 hours) on servers with the logout endpoints proxied through the media repo, and - # zero for servers who do not proxy the endpoints through. - maxCacheTimeSeconds: 0 - - # Whether or not to use the `appservices` config option below. If disabled (the default), - # the regular access token cache will be used for each user, potentially leading to high - # memory usage. - useLocalAppserviceConfig: false - - # The application services (and their namespaces) registered on the homeserver. Only used - # if `useLocalAppserviceConfig` is enabled (recommended). - # - # Usually the appservice will provide you with these config details - they'll just need - # translating from the appservice registration to here. Note that this does not require - # all options from the registration, and only requires the bare minimum required to run - # the media repo. - appservices: - - id: Name_of_appservice_for_your_reference - asToken: Secret_token_for_appservices_to_use - senderUserId: "@_example_bridge:yourdomain.com" - userNamespaces: - - regex: "@_example_bridge_.+:yourdomain.com" - # A note about regexes: it is best to suffix *all* namespaces with the homeserver - # domain users are valid for, as otherwise the appservice can use any user with - # any domain name it feels like, even if that domain is not configured with the - # media repo. This will lead to inaccurate reporting in the case of the media - # repo, and potentially leading to media being considered "remote". - -# These users have full access to the administrative functions of the media repository. -# See docs/admin.md for information on what these people can do. They must belong to one of the -# configured homeservers above. -admins: - - "@your_username:example.org" - -# Shared secret auth is useful for applications building on top of the media repository, such -# as a management interface. The `token` provided here is treated as a repository administrator -# when shared secret auth is enabled: if the `token` is used in place of an access token, the' -# request will be authorized. This is not limited to any particular domain, giving applications -# the ability to use it on any configured hostname. -sharedSecretAuth: - # Set this to true to enable shared secret auth. - enabled: false - - # Use a secure value here to prevent unauthorized access to the media repository. - token: "PutSomeRandomSecureValueHere" - -# Datastores are places where media should be persisted. This isn't dedicated for just uploads: -# thumbnails and other misc data is also stored in these places. When the media repo is looking -# to store new media (such as user uploads, thumbnails, etc) it will look for a datastore which -# is flagged as forUploads. It will try to use the smallest datastore first. + - name: SERVER_NAME + csApi: "http://127.0.0.1:8008/" datastores: - type: file - enabled: true # Enable this to set up data storage. - # Datastores can be split into many areas when handling uploads. Media is still de-duplicated - # across all datastores (local content which duplicates remote content will re-use the remote - # content's location). This option is useful if your datastore is becoming very large, or if - # you want faster storage for a particular kind of media. - # - # The kinds available are: - # thumbnails - Used to store thumbnails of media (local and remote). - # remote_media - Original copies of remote media (servers not configured by this repo). - # local_media - Original uploads for local media. - # archives - Archives of content (GDPR and similar requests). + enabled: true forKinds: ["all"] opts: path: /data/media - -# Options for controlling archives. Archives are exports of a particular user's content for -# the purpose of GDPR or moving media to a different server. -archiving: - # Whether archiving is enabled or not. Default enabled. - enabled: true - # If true, users can request a copy of their own data. By default, only repository administrators - # can request a copy. - # This includes the ability for homeserver admins to request a copy of their own server's - # data, as known to the repo. - selfService: false - # The number of bytes to target per archive before breaking up the files. This is independent - # of any file upload limits and will require a similar amount of memory when performing an export. - # The file size is also a target, not a guarantee - it is possible to have files that are smaller - # or larger than the target. This is recommended to be approximately double the size of your - # file upload limit, provided there is enough memory available for the demand of exporting. - targetBytesPerPart: 209715200 # 200mb default - -# The file upload settings for the media repository -uploads: - maxBytes: 104857600 # 100MB default, 0 to disable - - # The minimum number of bytes to let people upload - minBytes: 100 # 100 bytes by default - - # The number of bytes to claim as the maximum size for uploads for the limits API. If this - # is not provided then the maxBytes setting will be used instead. This is useful to provide - # if the media repo's settings and the reverse proxy do not match for maximum request size. - # This is purely for informational reasons and does not actually limit any functionality. - # Set this to -1 to indicate that there is no limit. Zero will force the use of maxBytes. - #reportedMaxBytes: 104857600 - - -# Settings related to downloading files from the media repository -downloads: - # The maximum number of bytes to download from other servers - maxBytes: 104857600 # 100MB default, 0 to disable - - # The number of workers to use when downloading remote media. Raise this number if remote - # media is downloading slowly or timing out. - # - # Maximum memory usage = numWorkers multiplied by the maximum download size - # Average memory usage is dependent on how many concurrent downloads your users are doing. - numWorkers: 10 - - # How long, in minutes, to cache errors related to downloading remote media. Once this time - # has passed, the media is able to be re-requested. - failureCacheMinutes: 5 - - # The cache control settings for downloads. This can help speed up downloads for users by - # keeping popular media in the cache. This cache is also used for thumbnails. - cache: - enabled: true - - # The maximum size of cache to have. Higher numbers are better. - maxSizeBytes: 1048576000 # 1GB default - - # The maximum file size to cache. This should normally be the same size as your maximum - # upload size. - maxFileSizeBytes: 104857600 # 100MB default - - # The number of minutes to track how many downloads a file gets - trackedMinutes: 30 - - # The number of downloads a file must receive in the window above (trackedMinutes) in - # order to be cached. - minDownloads: 5 - - # The minimum amount of time an item should remain in the cache. This prevents the cache - # from cycling out the file if it needs more room during this time. Note that the media - # repo regularly cleans out media which is past this point from the cache, so this number - # may need increasing depending on your use case. If the maxSizeBytes is reached for the - # media repo, and some cached items are still under this timer, new items will not be able - # to enter the cache. When this happens, consider raising maxSizeBytes or lowering this - # timer. - minCacheTimeSeconds: 300 - - # The minimum amount of time an item should remain outside the cache once it is removed. - minEvictedTimeSeconds: 60 - - # How many days after a piece of remote content is downloaded before it expires. It can be - # re-downloaded on demand, this just helps free up space in your datastore. Set to zero or - # negative to disable. Defaults to disabled. - expireAfterDays: 0 - -# URL Preview settings urlPreviews: - enabled: true # If enabled, the preview_url routes will be accessible - maxPageSizeBytes: 10485760 # 10MB default, 0 to disable - - # If true, the media repository will try to provide previews for URLs with invalid or unsafe - # certificates. If false (the default), the media repo will fail requests to said URLs. - previewUnsafeCertificates: false - - # Note: URL previews are limited to a given number of words, which are then limited to a number - # of characters, taking off the last word if it needs to. This also applies for the title. - - numWords: 50 # The number of words to include in a preview (maximum) - maxLength: 200 # The maximum number of characters for a description - - numTitleWords: 30 # The maximum number of words to include in a preview's title - maxTitleLength: 150 # The maximum number of characters for a title - - # The mime types to preview when OpenGraph previews cannot be rendered. OpenGraph previews are - # calculated on anything matching "text/*". To have a thumbnail in the preview the URL must be - # an image and the image's type must be allowed by the thumbnailer. - filePreviewTypes: - - "image/*" - - # The number of workers to use when generating url previews. Raise this number if url - # previews are slow or timing out. - # - # Maximum memory usage = numWorkers multiplied by the maximum page size - # Average memory usage is dependent on how many concurrent urls your users are previewing. - numWorkers: 10 - - # Either allowedNetworks or disallowedNetworks must be provided. If both are provided, they - # will be merged. URL previews will be disabled if neither is supplied. Each entry must be - # a CIDR range. disallowedNetworks: - - "127.0.0.1/8" - - "10.0.0.0/8" - - "172.16.0.0/12" - - "192.168.0.0/16" - - "100.64.0.0/10" - - "169.254.0.0/16" - - '::1/128' - - 'fe80::/64' - - 'fc00::/7' + - "192.168.0.0/16" # Don't limit localhost allowedNetworks: - - "0.0.0.0/0" # "Everything". The blacklist will help limit this. - # This is the default value for this field. - - # How many days after a preview is generated before it expires and is deleted. The preview - # can be regenerated safely - this just helps free up some space in your database. Set to - # zero or negative to disable. Defaults to disabled. - expireAfterDays: 0 - - # The default Accept-Language header to supply when generating URL previews when one isn't - # supplied by the client. - # Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language - defaultLanguage: "en-US,en" - -# The thumbnail configuration for the media repository. -thumbnails: - # The maximum number of bytes an image can be before the thumbnailer refuses. - maxSourceBytes: 10485760 # 10MB default, 0 to disable - - # The number of workers to use when generating thumbnails. Raise this number if thumbnails - # are slow to generate or timing out. - # - # Maximum memory usage = numWorkers multiplied by the maximum image source size - # Average memory usage is dependent on how many thumbnails are being generated by your users - numWorkers: 100 - - # All thumbnails are generated into one of the sizes listed here. The first size is used as - # the default for when no width or height is requested. The media repository will return - # either an exact match or the next largest size of thumbnail. - sizes: - - width: 32 - height: 32 - - width: 96 - height: 96 - - width: 320 - height: 240 - - width: 640 - height: 480 - - width: 800 - height: 600 - - # The content types to thumbnail when requested. Types that are not supported by the media repo - # will not be thumbnailed (adding application/json here won't work). Clients may still not request - # thumbnails for these types - this won't make clients automatically thumbnail these file types. - types: - - "image/jpeg" - - "image/jpg" - - "image/png" - - "image/gif" - - "image/heif" - - "image/webp" - #- "image/svg+xml" # Be sure to have ImageMagick installed to thumbnail SVG files - - # Animated thumbnails can be CPU intensive to generate. To disable the generation of animated - # thumbnails, set this to false. If disabled, regular thumbnails will be returned. - allowAnimated: true - - # Default to animated thumbnails, if available - defaultAnimated: false - - # The maximum file size to thumbnail when a capable animated thumbnail is requested. If the image - # is larger than this, the thumbnail will be generated as a static image. - maxAnimateSizeBytes: 10485760 # 10MB default, 0 to disable - - # On a scale of 0 (start of animation) to 1 (end of animation), where should the thumbnailer try - # and thumbnail animated content? Defaults to 0.5 (middle of animation). - stillFrame: 0.5 - - # How many days after a thumbnail is generated before it expires and is deleted. The thumbnail - # can be regenerated safely - this just helps free up some space in your datastores. Set to - # zero or negative to disable. Defaults to disabled. - expireAfterDays: 0 - -# Controls for the rate limit functionality + - "0.0.0.0/0" rateLimit: - # Set this to false if rate limiting is handled at a higher level or you don't want it enabled. - enabled: false - - # The number of requests per second before an IP will be rate limited. Must be a whole number. - requestsPerSecond: 1 - - # The number of requests an IP can send at once before the rate limit is actually considered. - burst: 10 - -# Identicons are generated avatars for a given username. Some clients use these to give users a -# default avatar after signing up. Identicons are not part of the official matrix spec, therefore -# this feature is completely optional. -identicons: - enabled: true - -# The quarantine media settings. -quarantine: - # If true, when a thumbnail of quarantined media is requested an image will be returned. If no - # image is given in the thumbnailPath below then a generated image will be provided. This does - # not affect regular downloads of files. - replaceThumbnails: true - - # If true, when media which has been quarantined is requested an image will be returned. If - # no image is given in the thumbnailPath below then a generated image will be provided. This - # will replace media which is not an image (ie: quarantining a PDF will replace the PDF with - # an image). - replaceDownloads: false - - # If provided, the given image will be returned as a thumbnail for media that is quarantined. - #thumbnailPath: "/path/to/thumbnail.png" - - # If true, administrators of the configured homeservers may quarantine media for their server - # only. Global administrators can quarantine any media (local or remote) regardless of this - # flag. - allowLocalAdmins: true - -# The various timeouts that the media repo will use. -timeouts: - # The maximum amount of time the media repo should spend trying to fetch a resource that is - # being previewed. - urlPreviewTimeoutSeconds: 10 - - # The maximum amount of time the media repo will spend making remote requests to other repos - # or homeservers. This is primarily used to download media. - federationTimeoutSeconds: 120 - - # The maximum amount of time the media repo will spend talking to your configured homeservers. - # This is usually used to verify a user's identity. - clientServerTimeoutSeconds: 30 - -# Prometheus metrics configuration -# For an example Grafana dashboard, import the following JSON: -# https://github.com/turt2live/matrix-media-repo/blob/master/docs/grafana.json -metrics: - # If true, the bindAddress and port below will serve GET /metrics for Prometheus to scrape. enabled: false - - # The address to listen on. Typically "127.0.0.1" or "0.0.0.0" for all interfaces. - bindAddress: "127.0.0.1" - - # The port to listen on. Cannot be the same as the general web server port. - port: 9000 - -# Options for controlling various MSCs/unstable features of the media repo -# Sections of this config might disappear or be added over time. By default all -# features are disabled in here and must be explicitly enabled to be used. -featureSupport: - # MSC2248 - Blurhash - MSC2448: - # Whether or not this MSC is enabled for use in the media repo - enabled: false - - # Maximum dimensions for converting a blurhash to an image. When no width and - # height options are supplied, the default will be half these values. - maxWidth: 1024 - maxHeight: 1024 - - # Thumbnail size in pixels to use to generate the blurhash string - thumbWidth: 64 - thumbHeight: 64 - - # The X and Y components to use. Higher numbers blur less, lower numbers blur more. - xComponents: 4 - yComponents: 3 - - # The amount of contrast to apply when converting a blurhash to an image. Lower values - # make the effect more subtle, larger values make it stronger. - punch: 1 - - # IPFS Support - # This is currently experimental and might not work at all. - IPFS: - # Whether or not IPFS support is enabled for use in the media repo. - enabled: false - - # Options for the built in IPFS daemon - builtInDaemon: - # Enable this to spawn an in-process IPFS node to use instead of a localhost - # HTTP agent. If this is disabled, the media repo will assume you have an HTTP - # IPFS agent running and accessible. Defaults to using a daemon (true). - enabled: true - - # If the Daemon is enabled, set this to the location where the IPFS files should - # be stored. If you're using Docker, this should be something like "/data/ipfs" - # so it can be mapped to a volume. - repoPath: "./ipfs" - - # Support for redis as a cache mechanism - # - # Note: Enabling Redis support will mean that the existing cache mechanism will do nothing. - # It can be safely disabled once Redis support is enabled. - # - # See docs/redis.md for more information on how this works and how to set it up. - redis: - # Whether or not use Redis instead of in-process caching. - enabled: false - - # The Redis shards that should be used by the media repo in the ring. The names of the - # shards are for your reference and have no bearing on the connection, but must be unique. - shards: - - name: "server1" - addr: ":7000" - - name: "server2" - addr: ":7001" - - name: "server3" - addr: ":7002" \ No newline at end of file diff --git a/matrix/federation.go b/matrix/federation.go index ff10a4fe..ea92bb63 100644 --- a/matrix/federation.go +++ b/matrix/federation.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net" "net/http" + "os" "strconv" "strings" "sync" @@ -211,26 +212,55 @@ func FederatedGet(url string, realHost string, ctx rcontext.RequestContext) (*ht req.Header.Set("User-Agent", "matrix-media-repo") req.Host = realHost - // This is how we verify the certificate is valid for the host we expect. - // Previously using `req.URL.Host` we'd end up changing which server we were - // connecting to (ie: matrix.org instead of matrix.org.cdn.cloudflare.net), - // which obviously doesn't help us. We needed to do that though because the - // HTTP client doesn't verify against the req.Host certificate, but it does - // handle it off the req.URL.Host. So, we need to tell it which certificate - // to verify. - - h, _, err := net.SplitHostPort(realHost) - if err == nil { - // Strip the port first, certs are port-insensitive - realHost = h - } - client := http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - ServerName: realHost, + var client *http.Client + if os.Getenv("MEDIA_REPO_UNSAFE_FEDERATION") != "true" { + // This is how we verify the certificate is valid for the host we expect. + // Previously using `req.URL.Host` we'd end up changing which server we were + // connecting to (ie: matrix.org instead of matrix.org.cdn.cloudflare.net), + // which obviously doesn't help us. We needed to do that though because the + // HTTP client doesn't verify against the req.Host certificate, but it does + // handle it off the req.URL.Host. So, we need to tell it which certificate + // to verify. + + h, _, err := net.SplitHostPort(realHost) + if err == nil { + // Strip the port first, certs are port-insensitive + realHost = h + } + client = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + ServerName: realHost, + }, }, - }, - Timeout: time.Duration(ctx.Config.TimeoutSeconds.Federation) * time.Second, + Timeout: time.Duration(ctx.Config.TimeoutSeconds.Federation) * time.Second, + } + } else { + ctx.Log.Warn("Ignoring any certificate errors while making request") + tr := &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + // Based on https://github.com/matrix-org/gomatrixserverlib/blob/51152a681e69a832efcd934b60080b92bc98b286/client.go#L74-L90 + DialTLS: func(network, addr string) (net.Conn, error) { + rawconn, err := net.Dial(network, addr) + if err != nil { + return nil, err + } + // Wrap a raw connection ourselves since tls.Dial defaults the SNI + conn := tls.Client(rawconn, &tls.Config{ + ServerName: "", + InsecureSkipVerify: true, + }) + if err := conn.Handshake(); err != nil { + return nil, err + } + return conn, nil + }, + } + client = &http.Client{ + Transport: tr, + Timeout: time.Duration(ctx.Config.TimeoutSeconds.UrlPreviews) * time.Second, + } } resp, err = client.Do(req) -- GitLab