From 66d6c3f944826adfdf2a2b8e71de7663360ff3e6 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 19 Oct 2021 17:40:01 +0200 Subject: [PATCH] s3: fix broken presigned urls when internal s3 endpoint was different from external --- configuration/config.go | 2 ++ routes/file/file_s3.go | 52 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/configuration/config.go b/configuration/config.go index fef618f..51cd549 100644 --- a/configuration/config.go +++ b/configuration/config.go @@ -67,6 +67,7 @@ func InitConfig() error { adminMail = flag.String("admin-mail", "", "Initial admin mail address") s3Bucket = flag.String("s3-bucket", "", "S3 Bucket for uploading files") s3Endpoint = flag.String("s3-endpoint", "", "Endpoint of S3 API for file uploads") + s3EndpointPublic = flag.String("s3-endpoint-public", "", "Public endpoint address of S3 API for file uploads") s3Region = flag.String("s3-region", "default", "S3 Region for file uploads") s3NoSSL = flag.Bool("s3-nossl", false, "Use encrypted connections to the S3 API") s3PathStyle = flag.Bool("s3-pathstyle", false, "Use path-style S3 API") @@ -103,6 +104,7 @@ func InitConfig() error { "admin.mail": *adminMail, "s3.bucket": *s3Bucket, "s3.endpoint": *s3Endpoint, + "s3.endpoint-public": *s3EndpointPublic, "s3.region": *s3Region, "jwt.secret": *jwtSecret, "jwt.expires-after": *jwtExpiresAfter, diff --git a/routes/file/file_s3.go b/routes/file/file_s3.go index 3a247fd..b1eb7fd 100644 --- a/routes/file/file_s3.go +++ b/routes/file/file_s3.go @@ -22,13 +22,16 @@ package file import ( + "errors" "fmt" "io" + "net/url" "time" "git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" @@ -83,7 +86,7 @@ func createS3Session() (*session.Session, error) { }, ) if err != nil { - return nil, fmt.Errorf("failed to create session: %s", err) + return nil, fmt.Errorf("failed to create session: %w", err) } return sess, nil @@ -110,7 +113,7 @@ func (f *File) putS3(fileContent io.Reader) error { Body: fileContent, }) if err != nil { - return fmt.Errorf("failed to upload file, %v", err) + return fmt.Errorf("failed to upload file: %w", err) } return nil @@ -128,16 +131,21 @@ func (f *File) getS3Url() (string, error) { svc := s3.New(sess) req, _ := svc.GetObjectRequest(&s3.GetObjectInput{ - Bucket: aws.String(bucket), - Key: aws.String(f.Key), - ResponseContentType: aws.String(f.Type), - // ResponseContentDisposition: aws.String("attachment; filename=" + f.Name), + Bucket: aws.String(bucket), + Key: aws.String(f.Key), + ResponseContentType: aws.String(f.Type), + ResponseContentDisposition: aws.String("attachment; filename=" + f.Name), // ResponseContentEncoding: aws.String(), // ResponseContentLanguage: aws.String(), // ResponseCacheControl: aws.String(), // ResponseExpires: aws.String(), }) + err = updateS3Request(req) + if err != nil { + return "", err + } + urlStr, err := req.Presign(5 * 24 * 60 * time.Minute) if err != nil { return "", err @@ -170,3 +178,35 @@ func (f *File) deleteS3() error { return nil } + +// updateS3Request updates the request host to the public accessible S3 +// endpoint host so that presigned URLs are still valid when accessed +// by the user +func updateS3Request(req *request.Request) error { + epURL, err := getS3EndpointURL() + if err != nil { + return err + } + + req.HTTPRequest.URL.Scheme = epURL.Scheme + req.HTTPRequest.URL.Host = epURL.Host + + return nil +} + +func getS3EndpointURL() (*url.URL, error) { + ep, err := configuration.GlobalConfig.String("s3.endpoint-public") + if err != nil { + ep, err = configuration.GlobalConfig.String("s3.endpoint") + if err != nil { + return nil, errors.New("missing s3.endpoint setting") + } + } + + epURL, err := url.Parse(ep) + if err != nil { + return nil, err + } + + return epURL, nil +}