From 978679f38092fe8f1b68f3dbfada72282ae9ba4e Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Tue, 23 Apr 2024 14:49:34 +0300 Subject: [PATCH] [#Issue] presign Signed-off-by: Denis Kirillov --- api/auth/center.go | 17 ++++++++--------- api/auth/center_test.go | 1 + api/auth/presign.go | 10 ++++++++-- api/auth/presign_test.go | 17 +++++++++++++---- api/auth/signer/v4asdk2/v4a.go | 15 ++++++++++----- 5 files changed, 40 insertions(+), 20 deletions(-) diff --git a/api/auth/center.go b/api/auth/center.go index e67b9de..e477aff 100644 --- a/api/auth/center.go +++ b/api/auth/center.go @@ -9,7 +9,6 @@ import ( "io" "mime/multipart" "net/http" - "os" "regexp" "strings" "time" @@ -23,7 +22,6 @@ import ( oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" credentialsv2 "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/smithy-go/logging" ) var ( @@ -410,8 +408,6 @@ func (c *Center) checkSign(authHeader *AuthHeader, box *accessbox.Box, request * case signaturePreambleSigV4A: signer := v4a.NewSigner(func(options *v4a.SignerOptions) { options.DisableURIPathEscaping = true - options.LogSigning = true - options.Logger = logging.NewStandardLogger(os.Stdout) }) credAdapter := v4a.SymmetricCredentialAdaptor{ @@ -423,13 +419,16 @@ func (c *Center) checkSign(authHeader *AuthHeader, box *accessbox.Box, request * return fmt.Errorf("failed to derive assymetric key from credentials: %w", err) } - if authHeader.IsPresigned { - if err = checkPresignedDate(authHeader, signatureDateTime); err != nil { - return err - } + if !authHeader.IsPresigned { + return signer.VerifySignature(creds, request, authHeader.PayloadHash, authHeader.Service, + strings.Split(authHeader.Region, ","), signatureDateTime, authHeader.Signature) } - return signer.VerifySignature(creds, request, authHeader.PayloadHash, authHeader.Service, + if err = checkPresignedDate(authHeader, signatureDateTime); err != nil { + return err + } + + return signer.VerifyPresigned(creds, request, authHeader.PayloadHash, authHeader.Service, strings.Split(authHeader.Region, ","), signatureDateTime, authHeader.Signature) default: return fmt.Errorf("invalid preamble: %s", authHeader.Preamble) diff --git a/api/auth/center_test.go b/api/auth/center_test.go index eb65dc5..754e935 100644 --- a/api/auth/center_test.go +++ b/api/auth/center_test.go @@ -35,6 +35,7 @@ func TestAuthHeaderParse(t *testing.T) { Signature: "2811ccb9e242f41426738fb1f", SignedFields: []string{"host", "x-amz-content-sha256", "x-amz-date"}, Date: "20210809", + Preamble: signaturePreambleSigV4, }, }, { diff --git a/api/auth/presign.go b/api/auth/presign.go index bad3796..b7cbded 100644 --- a/api/auth/presign.go +++ b/api/auth/presign.go @@ -28,6 +28,7 @@ type PresignData struct { Region string Lifetime time.Duration SignTime time.Time + Headers map[string]string } // PresignRequest forms pre-signed request to access objects without aws credentials. @@ -38,8 +39,10 @@ func PresignRequest(creds *credentials.Credentials, reqData RequestData, presign return nil, fmt.Errorf("failed to create new request: %w", err) } + for k, v := range presignData.Headers { + req.Header.Set(k, v) // maybe we should filter system header (or keep responsibility on caller) + } req.Header.Set(AmzDate, presignData.SignTime.Format("20060102T150405Z")) - req.Header.Set(ContentTypeHdr, "text/plain") signer := v4.NewSigner(creds) signer.DisableURIPathEscaping = true @@ -59,8 +62,11 @@ func PresignRequestV4a(credProvider credentialsv2.StaticCredentialsProvider, req return nil, fmt.Errorf("failed to create new request: %w", err) } + for k, v := range presignData.Headers { + req.Header.Set(k, v) // maybe we should filter system header (or keep responsibility on caller) + } + req.Header.Set(AmzDate, presignData.SignTime.Format("20060102T150405Z")) - req.Header.Set(ContentTypeHdr, "text/plain") req.Header.Set(AmzExpires, strconv.Itoa(int(presignData.Lifetime.Seconds()))) signer := v4a.NewSigner(func(options *v4a.SignerOptions) { diff --git a/api/auth/presign_test.go b/api/auth/presign_test.go index e5e18f6..387e803 100644 --- a/api/auth/presign_test.go +++ b/api/auth/presign_test.go @@ -75,6 +75,9 @@ func TestCheckSign(t *testing.T) { Region: "spb", Lifetime: 10 * time.Minute, SignTime: time.Now().UTC(), + Headers: map[string]string{ + ContentTypeHdr: "text/plain", + }, } req, err := PresignRequest(awsCreds, reqData, presignData) @@ -119,11 +122,16 @@ func TestCheckSignV4a(t *testing.T) { Region: "spb", Lifetime: 10 * time.Minute, SignTime: time.Now().UTC(), + Headers: map[string]string{ + ContentTypeHdr: "text/plain", + }, } req, err := PresignRequestV4a(awsCreds, reqData, presignData) require.NoError(t, err) + req.Header.Set(ContentTypeHdr, "text/plain") + expBox := &accessbox.Box{ Gate: &accessbox.GateData{ SecretKey: secretKey, @@ -170,9 +178,7 @@ func TestPresignRequestV4a(t *testing.T) { req, err := http.NewRequest("GET", "http://localhost:8084/bucket/object", nil) require.NoError(t, err) - //req.Header.Set(AmzRegionSet, strings.Join(regionSet, ",")) - //req.Header.Set(AmzDate, signingTime.Format("20060102T150405Z")) - //req.Header.Set(AmzAlgorithm, signaturePreambleSigV4A) + req.Header.Set(AmzExpires, "600") presignedURL, hdr, err := signer.PresignHTTP(req.Context(), creds, req, "", service, regionSet, signingTime) require.NoError(t, err) @@ -184,7 +190,10 @@ func TestPresignRequestV4a(t *testing.T) { r, err := http.NewRequest("GET", presignedURL, nil) require.NoError(t, err) + query := r.URL.Query() + query.Del(AmzSignature) + r.URL.RawQuery = query.Encode() - err = signer.VerifySignature(creds, r, "", service, regionSet, signingTime, signature) + err = signer.VerifyPresigned(creds, r, "", service, regionSet, signingTime, signature) require.NoError(t, err) } diff --git a/api/auth/signer/v4asdk2/v4a.go b/api/auth/signer/v4asdk2/v4a.go index 305fbd1..5f79859 100644 --- a/api/auth/signer/v4asdk2/v4a.go +++ b/api/auth/signer/v4asdk2/v4a.go @@ -202,6 +202,15 @@ func (s *Signer) SignHTTP(ctx context.Context, credentials Credentials, r *http. // VerifySignature checks sigv4a. func (s *Signer) VerifySignature(credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, signature string, optFns ...func(*SignerOptions)) error { + return s.verifySignature(credentials, r, payloadHash, service, regionSet, signingTime, signature, false, optFns...) +} + +// VerifyPresigned checks sigv4a. +func (s *Signer) VerifyPresigned(credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, signature string, optFns ...func(*SignerOptions)) error { + return s.verifySignature(credentials, r, payloadHash, service, regionSet, signingTime, signature, true, optFns...) +} + +func (s *Signer) verifySignature(credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, signature string, isPresigned bool, optFns ...func(*SignerOptions)) error { options := s.options for _, fn := range optFns { fn(&options) @@ -214,6 +223,7 @@ func (s *Signer) VerifySignature(credentials Credentials, r *http.Request, paylo RegionSet: regionSet, Credentials: credentials, Time: signingTime.UTC(), + IsPreSign: isPresigned, DisableHeaderHoisting: options.DisableHeaderHoisting, DisableURIPathEscaping: options.DisableURIPathEscaping, } @@ -465,11 +475,6 @@ func (s *httpSigner) buildCanonicalHeaders(host string, rule v4Internal.Rule, he continue // ignored header } - if strings.EqualFold(k, contentLengthHeader) { - // prevent signing already handled content-length header. - continue - } - lowerCaseKey := strings.ToLower(k) if _, ok := signed[lowerCaseKey]; ok { // include additional values