Support AWS Signature V2 for Mattermost for S3 storage. (#6462)
Certain S3 compatible servers only use Legacy Signature (AWS Signature V2), current code only supports signature v4. This PR adds facility to click a button on the UI to enable legacy signature with S3 compatible servers.
Этот коммит содержится в:
коммит произвёл
Corey Hulen
родитель
d409c7c1c6
Коммит
f520aa1f4d
@@ -820,13 +820,21 @@ func readTestFile(name string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool) (*s3.Client, error) {
|
||||||
|
if signV2 {
|
||||||
|
return s3.NewV2(endpoint, accessKey, secretKey, secure)
|
||||||
|
}
|
||||||
|
return s3.NewV4(endpoint, accessKey, secretKey, secure)
|
||||||
|
}
|
||||||
|
|
||||||
func cleanupTestFile(info *model.FileInfo) error {
|
func cleanupTestFile(info *model.FileInfo) error {
|
||||||
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
||||||
endpoint := utils.Cfg.FileSettings.AmazonS3Endpoint
|
endpoint := utils.Cfg.FileSettings.AmazonS3Endpoint
|
||||||
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
||||||
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
||||||
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
||||||
s3Clnt, err := s3.New(endpoint, accessKey, secretKey, secure)
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
||||||
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ import (
|
|||||||
"github.com/mattermost/platform/model"
|
"github.com/mattermost/platform/model"
|
||||||
"github.com/mattermost/platform/store"
|
"github.com/mattermost/platform/store"
|
||||||
"github.com/mattermost/platform/utils"
|
"github.com/mattermost/platform/utils"
|
||||||
|
|
||||||
s3 "github.com/minio/minio-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateUser(t *testing.T) {
|
func TestCreateUser(t *testing.T) {
|
||||||
@@ -696,7 +694,8 @@ func TestUserCreateImage(t *testing.T) {
|
|||||||
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
||||||
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
||||||
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
||||||
s3Clnt, err := s3.New(endpoint, accessKey, secretKey, secure)
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
||||||
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -800,7 +799,8 @@ func TestUserUploadProfileImage(t *testing.T) {
|
|||||||
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
||||||
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
||||||
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
||||||
s3Clnt, err := s3.New(endpoint, accessKey, secretKey, secure)
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
||||||
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -626,13 +626,21 @@ func readTestFile(name string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool) (*s3.Client, error) {
|
||||||
|
if signV2 {
|
||||||
|
return s3.NewV2(endpoint, accessKey, secretKey, secure)
|
||||||
|
}
|
||||||
|
return s3.NewV4(endpoint, accessKey, secretKey, secure)
|
||||||
|
}
|
||||||
|
|
||||||
func cleanupTestFile(info *model.FileInfo) error {
|
func cleanupTestFile(info *model.FileInfo) error {
|
||||||
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
||||||
endpoint := utils.Cfg.FileSettings.AmazonS3Endpoint
|
endpoint := utils.Cfg.FileSettings.AmazonS3Endpoint
|
||||||
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
||||||
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
||||||
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
||||||
s3Clnt, err := s3.New(endpoint, accessKey, secretKey, secure)
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
||||||
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ func trackConfig() {
|
|||||||
"enable_public_links": utils.Cfg.FileSettings.EnablePublicLink,
|
"enable_public_links": utils.Cfg.FileSettings.EnablePublicLink,
|
||||||
"driver_name": utils.Cfg.FileSettings.DriverName,
|
"driver_name": utils.Cfg.FileSettings.DriverName,
|
||||||
"amazon_s3_ssl": *utils.Cfg.FileSettings.AmazonS3SSL,
|
"amazon_s3_ssl": *utils.Cfg.FileSettings.AmazonS3SSL,
|
||||||
|
"amazon_s3_signv2": *utils.Cfg.FileSettings.AmazonS3SignV2,
|
||||||
"thumbnail_width": utils.Cfg.FileSettings.ThumbnailWidth,
|
"thumbnail_width": utils.Cfg.FileSettings.ThumbnailWidth,
|
||||||
"thumbnail_height": utils.Cfg.FileSettings.ThumbnailHeight,
|
"thumbnail_height": utils.Cfg.FileSettings.ThumbnailHeight,
|
||||||
"preview_width": utils.Cfg.FileSettings.PreviewWidth,
|
"preview_width": utils.Cfg.FileSettings.PreviewWidth,
|
||||||
|
|||||||
19
app/file.go
19
app/file.go
@@ -56,13 +56,23 @@ const (
|
|||||||
MaxImageSize = 6048 * 4032 // 24 megapixels, roughly 36MB as a raw image
|
MaxImageSize = 6048 * 4032 // 24 megapixels, roughly 36MB as a raw image
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Similar to s3.New() but allows initialization of signature v2 or signature v4 client.
|
||||||
|
// If signV2 input is false, function always returns signature v4.
|
||||||
|
func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool) (*s3.Client, error) {
|
||||||
|
if signV2 {
|
||||||
|
return s3.NewV2(endpoint, accessKey, secretKey, secure)
|
||||||
|
}
|
||||||
|
return s3.NewV4(endpoint, accessKey, secretKey, secure)
|
||||||
|
}
|
||||||
|
|
||||||
func ReadFile(path string) ([]byte, *model.AppError) {
|
func ReadFile(path string) ([]byte, *model.AppError) {
|
||||||
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
||||||
endpoint := utils.Cfg.FileSettings.AmazonS3Endpoint
|
endpoint := utils.Cfg.FileSettings.AmazonS3Endpoint
|
||||||
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
||||||
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
||||||
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
||||||
s3Clnt, err := s3.New(endpoint, accessKey, secretKey, secure)
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
||||||
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.NewLocAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error())
|
return nil, model.NewLocAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error())
|
||||||
}
|
}
|
||||||
@@ -94,7 +104,8 @@ func MoveFile(oldPath, newPath string) *model.AppError {
|
|||||||
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
||||||
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
||||||
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
||||||
s3Clnt, err := s3.New(endpoint, accessKey, secretKey, secure)
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
||||||
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.NewLocAppError("moveFile", "api.file.write_file.s3.app_error", nil, err.Error())
|
return model.NewLocAppError("moveFile", "api.file.write_file.s3.app_error", nil, err.Error())
|
||||||
}
|
}
|
||||||
@@ -128,10 +139,12 @@ func WriteFile(f []byte, path string) *model.AppError {
|
|||||||
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
accessKey := utils.Cfg.FileSettings.AmazonS3AccessKeyId
|
||||||
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey
|
||||||
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
secure := *utils.Cfg.FileSettings.AmazonS3SSL
|
||||||
s3Clnt, err := s3.New(endpoint, accessKey, secretKey, secure)
|
signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2
|
||||||
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.NewLocAppError("WriteFile", "api.file.write_file.s3.app_error", nil, err.Error())
|
return model.NewLocAppError("WriteFile", "api.file.write_file.s3.app_error", nil, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
bucket := utils.Cfg.FileSettings.AmazonS3Bucket
|
bucket := utils.Cfg.FileSettings.AmazonS3Bucket
|
||||||
ext := filepath.Ext(path)
|
ext := filepath.Ext(path)
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,8 @@
|
|||||||
"AmazonS3Bucket": "",
|
"AmazonS3Bucket": "",
|
||||||
"AmazonS3Region": "us-east-1",
|
"AmazonS3Region": "us-east-1",
|
||||||
"AmazonS3Endpoint": "s3.amazonaws.com",
|
"AmazonS3Endpoint": "s3.amazonaws.com",
|
||||||
"AmazonS3SSL": true
|
"AmazonS3SSL": true,
|
||||||
|
"AmazonS3SignV2": false
|
||||||
},
|
},
|
||||||
"EmailSettings": {
|
"EmailSettings": {
|
||||||
"EnableSignUpWithEmail": true,
|
"EnableSignUpWithEmail": true,
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ type FileSettings struct {
|
|||||||
AmazonS3Region string
|
AmazonS3Region string
|
||||||
AmazonS3Endpoint string
|
AmazonS3Endpoint string
|
||||||
AmazonS3SSL *bool
|
AmazonS3SSL *bool
|
||||||
|
AmazonS3SignV2 *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmailSettings struct {
|
type EmailSettings struct {
|
||||||
@@ -502,6 +503,11 @@ func (o *Config) SetDefaults() {
|
|||||||
*o.FileSettings.AmazonS3SSL = true // Secure by default.
|
*o.FileSettings.AmazonS3SSL = true // Secure by default.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.FileSettings.AmazonS3SignV2 == nil {
|
||||||
|
o.FileSettings.AmazonS3SignV2 = new(bool)
|
||||||
|
// Signature v2 is not enabled by default.
|
||||||
|
}
|
||||||
|
|
||||||
if o.FileSettings.EnableFileAttachments == nil {
|
if o.FileSettings.EnableFileAttachments == nil {
|
||||||
o.FileSettings.EnableFileAttachments = new(bool)
|
o.FileSettings.EnableFileAttachments = new(bool)
|
||||||
*o.FileSettings.EnableFileAttachments = true
|
*o.FileSettings.EnableFileAttachments = true
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default class StorageSettings extends AdminSettings {
|
|||||||
config.FileSettings.AmazonS3Bucket = this.state.amazonS3Bucket;
|
config.FileSettings.AmazonS3Bucket = this.state.amazonS3Bucket;
|
||||||
config.FileSettings.AmazonS3Endpoint = this.state.amazonS3Endpoint;
|
config.FileSettings.AmazonS3Endpoint = this.state.amazonS3Endpoint;
|
||||||
config.FileSettings.AmazonS3SSL = this.state.amazonS3SSL;
|
config.FileSettings.AmazonS3SSL = this.state.amazonS3SSL;
|
||||||
|
config.FileSettings.AmazonS3SignV2 = this.state.amazonS3SignV2;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -48,7 +49,8 @@ export default class StorageSettings extends AdminSettings {
|
|||||||
amazonS3SecretAccessKey: config.FileSettings.AmazonS3SecretAccessKey,
|
amazonS3SecretAccessKey: config.FileSettings.AmazonS3SecretAccessKey,
|
||||||
amazonS3Bucket: config.FileSettings.AmazonS3Bucket,
|
amazonS3Bucket: config.FileSettings.AmazonS3Bucket,
|
||||||
amazonS3Endpoint: config.FileSettings.AmazonS3Endpoint,
|
amazonS3Endpoint: config.FileSettings.AmazonS3Endpoint,
|
||||||
amazonS3SSL: config.FileSettings.AmazonS3SSL
|
amazonS3SSL: config.FileSettings.AmazonS3SSL,
|
||||||
|
amazonS3SignV2: config.FileSettings.AmazonS3SignV2
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +203,25 @@ export default class StorageSettings extends AdminSettings {
|
|||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
disabled={this.state.driverName !== DRIVER_S3}
|
disabled={this.state.driverName !== DRIVER_S3}
|
||||||
/>
|
/>
|
||||||
|
<BooleanSetting
|
||||||
|
id='amazonS3SignV2'
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.image.amazonS3SignV2Title'
|
||||||
|
defaultMessage='Enable Signature V2 for S3 Connections:'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder={Utils.localizeMessage('admin.image.amazonS3SignV2Example', 'Ex "false"')}
|
||||||
|
helpText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.image.amazonS3SignV2Desc'
|
||||||
|
defaultMessage='When true, allow signature v2 to Amazon S3. Defaults to Signature V4 requests.'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
value={this.state.amazonS3SignV2}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={this.state.driverName !== DRIVER_S3}
|
||||||
|
/>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
id='enableFileAttachments'
|
id='enableFileAttachments'
|
||||||
label={
|
label={
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user