diff --git a/services/filesstore/s3_overrides.go b/services/filesstore/s3_overrides.go new file mode 100644 index 0000000000..975889fb7a --- /dev/null +++ b/services/filesstore/s3_overrides.go @@ -0,0 +1,56 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package filesstore + +import ( + "context" + "net/http" + + "github.com/minio/minio-go/v7/pkg/credentials" +) + +// customTransport is used to point the request to a different server. +// This is helpful in situations where a different service is handling AWS S3 requests +// from multiple Mattermost applications, and the Mattermost service itself does not +// have any S3 credentials. +type customTransport struct { + base http.RoundTripper + host string + scheme string + client http.Client +} + +// RoundTrip implements the http.Roundtripper interface. +func (t *customTransport) RoundTrip(req *http.Request) (*http.Response, error) { + // Rountrippers should not modify the original request. + newReq := req.Clone(context.Background()) + *newReq.URL = *req.URL + req.URL.Scheme = t.scheme + req.URL.Host = t.host + return t.client.Do(req) +} + +// customProvider is a dummy credentials provider for the minio client to work +// without actually providing credentials. This is needed with a custom transport +// in cases where the minio client does not actually have credentials with itself, +// rather needs responses from another entity. +// +// It satisfies the credentials.Provider interface. +type customProvider struct { + isSignV2 bool +} + +// Retrieve just returns empty credentials. +func (cp customProvider) Retrieve() (credentials.Value, error) { + sign := credentials.SignatureV4 + if cp.isSignV2 { + sign = credentials.SignatureV2 + } + return credentials.Value{ + SignerType: sign, + }, nil +} + +// IsExpired always returns false. +func (cp customProvider) IsExpired() bool { return false } diff --git a/services/filesstore/s3store.go b/services/filesstore/s3store.go index 03f59c8595..0868da5859 100644 --- a/services/filesstore/s3store.go +++ b/services/filesstore/s3store.go @@ -72,7 +72,10 @@ func NewS3FileBackend(settings *model.FileSettings, enableComplianceFeatures boo func (b *S3FileBackend) s3New() (*s3.Client, error) { var creds *credentials.Credentials - if b.accessKey == "" && b.secretKey == "" { + isCloud := os.Getenv("MM_CLOUD_FILESTORE_BIFROST") != "" + if isCloud { + creds = credentials.New(customProvider{isSignV2: b.signV2}) + } else if b.accessKey == "" && b.secretKey == "" { creds = credentials.NewIAM("") } else if b.signV2 { creds = credentials.NewStatic(b.accessKey, b.secretKey, "", credentials.SignatureV2) @@ -85,6 +88,24 @@ func (b *S3FileBackend) s3New() (*s3.Client, error) { Secure: b.secure, Region: b.region, } + + // If this is a cloud installation, we override the default transport. + if isCloud { + tr, err := s3.DefaultTransport(b.secure) + if err != nil { + return nil, err + } + scheme := "http" + if b.secure { + scheme = "https" + } + opts.Transport = &customTransport{ + base: tr, + host: b.endpoint, + scheme: scheme, + } + } + s3Clnt, err := s3.New(b.endpoint, &opts) if err != nil { return nil, err