From 2105b10ccdff58a6d1986776c37fc179249f369f Mon Sep 17 00:00:00 2001 From: Tejay Cardon Date: Tue, 8 Aug 2017 08:15:20 -0600 Subject: [PATCH] FIXES PLT-6648 Add support for Server Side Encryption on S3 (#6467) Help from Jason Blais on wording Update storage_settings.jsx Update en.json --- app/diagnostics.go | 1 + app/file.go | 29 +++++++++++++++---- model/config.go | 6 ++++ model/config_test.go | 9 ++++++ .../admin_console/storage_settings.jsx | 23 ++++++++++++++- webapp/i18n/en.json | 3 ++ 6 files changed, 65 insertions(+), 6 deletions(-) diff --git a/app/diagnostics.go b/app/diagnostics.go index f12681d01a..983ce8077d 100644 --- a/app/diagnostics.go +++ b/app/diagnostics.go @@ -275,6 +275,7 @@ func trackConfig() { "enable_public_links": utils.Cfg.FileSettings.EnablePublicLink, "driver_name": utils.Cfg.FileSettings.DriverName, "amazon_s3_ssl": *utils.Cfg.FileSettings.AmazonS3SSL, + "amazon_s3_sse": *utils.Cfg.FileSettings.AmazonS3SSE, "amazon_s3_signv2": *utils.Cfg.FileSettings.AmazonS3SignV2, "max_file_size": *utils.Cfg.FileSettings.MaxFileSize, "enable_file_attachments": *utils.Cfg.FileSettings.EnableFileAttachments, diff --git a/app/file.go b/app/file.go index 74f70ec167..03d898acd7 100644 --- a/app/file.go +++ b/app/file.go @@ -115,6 +115,7 @@ func MoveFile(oldPath, newPath string) *model.AppError { secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey secure := *utils.Cfg.FileSettings.AmazonS3SSL signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2 + encrypt := *utils.Cfg.FileSettings.AmazonS3SSE region := utils.Cfg.FileSettings.AmazonS3Region s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2, region) if err != nil { @@ -123,7 +124,7 @@ func MoveFile(oldPath, newPath string) *model.AppError { bucket := utils.Cfg.FileSettings.AmazonS3Bucket source := s3.NewSourceInfo(bucket, oldPath, nil) - destination, err := s3.NewDestinationInfo(bucket, newPath, nil, nil) + destination, err := s3.NewDestinationInfo(bucket, newPath, nil, CopyMetadata(encrypt)) if err != nil { return model.NewLocAppError("moveFile", "api.file.write_file.s3.app_error", nil, err.Error()) } @@ -155,6 +156,7 @@ func WriteFile(f []byte, path string) *model.AppError { secretKey := utils.Cfg.FileSettings.AmazonS3SecretAccessKey secure := *utils.Cfg.FileSettings.AmazonS3SSL signV2 := *utils.Cfg.FileSettings.AmazonS3SignV2 + encrypt := *utils.Cfg.FileSettings.AmazonS3SSE region := utils.Cfg.FileSettings.AmazonS3Region s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2, region) if err != nil { @@ -163,12 +165,12 @@ func WriteFile(f []byte, path string) *model.AppError { bucket := utils.Cfg.FileSettings.AmazonS3Bucket ext := filepath.Ext(path) - + metaData := S3Metadata(encrypt, "binary/octet-stream") if model.IsFileExtImage(ext) { - _, err = s3Clnt.PutObject(bucket, path, bytes.NewReader(f), model.GetImageMimeType(ext)) - } else { - _, err = s3Clnt.PutObject(bucket, path, bytes.NewReader(f), "binary/octet-stream") + metaData = S3Metadata(encrypt, model.GetImageMimeType(ext)) } + + _, err = s3Clnt.PutObjectWithMetadata(bucket, path, bytes.NewReader(f), metaData, nil) if err != nil { return model.NewLocAppError("WriteFile", "api.file.write_file.s3.app_error", nil, err.Error()) } @@ -633,3 +635,20 @@ func GetFileInfo(fileId string) (*model.FileInfo, *model.AppError) { return result.Data.(*model.FileInfo), nil } } + +func S3Metadata(encrypt bool, contentType string) map[string][]string { + metaData := make(map[string][]string) + if contentType != "" { + metaData["Content-Type"] = []string{"contentType"} + } + if encrypt { + metaData["x-amz-server-side-encryption"] = []string{"AES256"} + } + return metaData +} + +func CopyMetadata(encrypt bool) map[string]string { + metaData := make(map[string]string) + metaData["x-amz-server-side-encryption"] = "AES256" + return metaData +} diff --git a/model/config.go b/model/config.go index 55fe8490be..1717d61a08 100644 --- a/model/config.go +++ b/model/config.go @@ -262,6 +262,7 @@ type FileSettings struct { AmazonS3Endpoint string AmazonS3SSL *bool AmazonS3SignV2 *bool + AmazonS3SSE *bool } type EmailSettings struct { @@ -551,6 +552,11 @@ func (o *Config) SetDefaults() { // Signature v2 is not enabled by default. } + if o.FileSettings.AmazonS3SSE == nil { + o.FileSettings.AmazonS3SSE = new(bool) + *o.FileSettings.AmazonS3SSE = false // Not Encrypted by default. + } + if o.FileSettings.EnableFileAttachments == nil { o.FileSettings.EnableFileAttachments = new(bool) *o.FileSettings.EnableFileAttachments = true diff --git a/model/config_test.go b/model/config_test.go index 1a944710fe..f10e2d0cdc 100644 --- a/model/config_test.go +++ b/model/config_test.go @@ -24,3 +24,12 @@ func TestConfigDefaultEmailNotificationContentsType(t *testing.T) { t.Fatal("EmailSettings.EmailNotificationContentsType should default to 'full'") } } + +func TestConfigDefaultFileSettingsS3SSE(t *testing.T) { + c1 := Config{} + c1.SetDefaults() + + if *c1.FileSettings.AmazonS3SSE != false { + t.Fatal("FileSettings.AmazonS3SSE should default to false") + } +} diff --git a/webapp/components/admin_console/storage_settings.jsx b/webapp/components/admin_console/storage_settings.jsx index 4e0d9d19b7..89a56c04bf 100644 --- a/webapp/components/admin_console/storage_settings.jsx +++ b/webapp/components/admin_console/storage_settings.jsx @@ -36,6 +36,7 @@ export default class StorageSettings extends AdminSettings { config.FileSettings.AmazonS3Bucket = this.state.amazonS3Bucket; config.FileSettings.AmazonS3Endpoint = this.state.amazonS3Endpoint; config.FileSettings.AmazonS3SSL = this.state.amazonS3SSL; + config.FileSettings.AmazonS3SSE = this.state.amazonS3SSE; return config; } @@ -52,7 +53,8 @@ export default class StorageSettings extends AdminSettings { amazonS3SecretAccessKey: config.FileSettings.AmazonS3SecretAccessKey, amazonS3Bucket: config.FileSettings.AmazonS3Bucket, amazonS3Endpoint: config.FileSettings.AmazonS3Endpoint, - amazonS3SSL: config.FileSettings.AmazonS3SSL + amazonS3SSL: config.FileSettings.AmazonS3SSL, + amazonS3SSE: config.FileSettings.AmazonS3SSE }; } @@ -252,6 +254,25 @@ export default class StorageSettings extends AdminSettings { onChange={this.handleChange} disabled={this.state.driverName !== DRIVER_S3} /> + + } + placeholder={Utils.localizeMessage('admin.image.AmazonSSEExample', 'Ex "false"')} + helpText={ + + } + value={this.state.AmazonSSE} + onChange={this.handleChange} + disabled={this.state.driverName !== DRIVER_S3} + /> documentation to learn more.", + "admin.image.amazonS3SSEExample": "E.g.: \"false\"", + "admin.image.amazonS3SSETitle": "Enable Server-Side Encryption for Amazon S3:", "admin.image.amazonS3SecretDescription": "Obtain this credential from your Amazon EC2 administrator.", "admin.image.amazonS3SecretExample": "E.g.: \"jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY\"", "admin.image.amazonS3SecretTitle": "Amazon S3 Secret Access Key:",