MM-29572: Add Bifrost support to codebase (#15955)

* MM-29572: Add Bifrost support to codebase

We use a custom transport for minio when an environment variable
indicates that Mattermost is running in cloud.

The transport is used to redirect the request to the S3 endpoint set
in the config. And the scheme is set depending on if S3SSL is set.

A custom credentials provider is needed to return empty credentials
which will be overridden by the service anyways. This is just to allow
the minio client library to work transparently without knowing that
there is something else in the middle intercepting requests.

https://mattermost.atlassian.net/browse/MM-29572

* incorporate review comments

* Update services/filesstore/s3_overrides.go

Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>

* Change to new env key

Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>
Этот коммит содержится в:
Agniva De Sarker
2020-10-15 22:39:23 +05:30
коммит произвёл GitHub
родитель b5047e51fe
Коммит 4b57cea789
2 изменённых файлов: 78 добавлений и 1 удалений

56
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 }

Просмотреть файл

@@ -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