From ea714c2e9e6f433154ee7e4bf16668410921264c Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Wed, 27 Nov 2024 11:31:53 +0300 Subject: [PATCH] [#339] Fix logging in authmate [pre]sign command Signed-off-by: Denis Kirillov --- api/auth/center.go | 17 ++++------- api/auth/center_fuzz_test.go | 3 +- api/auth/presign.go | 30 ++++++++++++++----- api/auth/presign_test.go | 5 ++-- .../modules/generate-presigned-url.go | 16 ++++++---- cmd/s3-authmate/modules/sign.go | 14 +++++---- 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/api/auth/center.go b/api/auth/center.go index 1dbf2392..adb998a5 100644 --- a/api/auth/center.go +++ b/api/auth/center.go @@ -142,10 +142,6 @@ func (c *Center) parseAuthHeader(authHeader string, headers http.Header) (*AuthH default: return nil, fmt.Errorf("%w: %s", apierr.GetAPIError(apierr.ErrAuthorizationHeaderMalformed), authHeader) } - // AWS4-ECDSA-P256-SHA256 - // Credential=2XGRML5EW3LMHdf64W2DkBy1Nkuu4y4wGhUj44QjbXBi05ZNvs8WVwy1XTmSEkcVkydPKzCgtmR7U3zyLYTj3Snxf/20240326/s3/aws4_request, - // SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;host;x-amz-content-sha256;x-amz-date;x-amz-region-set, - // Signature=3044022006a2bc760140834101d0a79667d6aa75768c1a28e9cafc8963484d0752a6c6050220629dc06d7d6505e1b1e2a5d1f974b25ba32fdffc6f3f70dc4dda31b8a6f7ea2b return &AuthHeader{ AccessKeyID: submatches["access_key_id"], @@ -487,20 +483,19 @@ func SignStr(secret, service, region string, t time.Time, strToSign string) stri return hex.EncodeToString(signature) } -func SignStrV4A(cred aws.Credentials, strToSign string) (string, error) { - hash := sha256.New() - hash.Write([]byte(strToSign)) - +func SignStrV4A(ctx context.Context, cred aws.Credentials, strToSign string) (string, error) { credAdapter := v4a.SymmetricCredentialAdaptor{ SymmetricProvider: credentials.NewStaticCredentialsProvider(cred.AccessKeyID, cred.SecretAccessKey, ""), } - creds, err := credAdapter.RetrievePrivateKey(context.Background()) // because of using StaticCredentialsProvider + creds, err := credAdapter.RetrievePrivateKey(ctx) if err != nil { - // no error is expected - panic(err) + return "", err } + hash := sha256.New() + hash.Write([]byte(strToSign)) + sig, err := creds.PrivateKey.Sign(rand.Reader, hash.Sum(nil), crypto.SHA256) if err != nil { return "", err diff --git a/api/auth/center_fuzz_test.go b/api/auth/center_fuzz_test.go index a5247da9..9f5cfc8e 100644 --- a/api/auth/center_fuzz_test.go +++ b/api/auth/center_fuzz_test.go @@ -13,6 +13,7 @@ import ( oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/aws/aws-sdk-go-v2/aws" utils "github.com/trailofbits/go-fuzz-utils" + "go.uber.org/zap" ) const ( @@ -60,7 +61,7 @@ func DoFuzzAuthenticate(input []byte) int { SignTime: time.Now().UTC(), } - req, err := PresignRequest(context.Background(), awsCreds, reqData, presignData) + req, err := PresignRequest(context.Background(), awsCreds, reqData, presignData, zap.NewNop()) if req == nil { return fuzzFailExitCode } diff --git a/api/auth/presign.go b/api/auth/presign.go index 8d0f2d38..fa712558 100644 --- a/api/auth/presign.go +++ b/api/auth/presign.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" "net/url" - "os" "strconv" "strings" "time" @@ -16,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/smithy-go/encoding/httpbinding" "github.com/aws/smithy-go/logging" + "go.uber.org/zap" ) type RequestData struct { @@ -34,7 +34,7 @@ type PresignData struct { } // PresignRequest forms pre-signed request to access objects without aws credentials. -func PresignRequest(ctx context.Context, creds aws.Credentials, reqData RequestData, presignData PresignData) (*http.Request, error) { +func PresignRequest(ctx context.Context, creds aws.Credentials, reqData RequestData, presignData PresignData, log *zap.Logger) (*http.Request, error) { urlStr := fmt.Sprintf("%s/%s/%s", reqData.Endpoint, httpbinding.EscapePath(reqData.Bucket, false), httpbinding.EscapePath(reqData.Object, false)) req, err := http.NewRequest(strings.ToUpper(reqData.Method), urlStr, nil) if err != nil { @@ -49,6 +49,8 @@ func PresignRequest(ctx context.Context, creds aws.Credentials, reqData RequestD signer := v4.NewSigner(func(options *v4.SignerOptions) { options.DisableURIPathEscaping = true + options.LogSigning = true + options.Logger = &logWrapper{log: log} }) signedURI, _, err := signer.PresignHTTP(ctx, creds, req, presignData.Headers[AmzContentSHA256], presignData.Service, presignData.Region, presignData.SignTime) @@ -64,7 +66,7 @@ func PresignRequest(ctx context.Context, creds aws.Credentials, reqData RequestD } // PresignRequestV4a forms pre-signed request to access objects without aws credentials. -func PresignRequestV4a(cred aws.Credentials, reqData RequestData, presignData PresignData) (*http.Request, error) { +func PresignRequestV4a(cred aws.Credentials, reqData RequestData, presignData PresignData, log *zap.Logger) (*http.Request, error) { urlStr := fmt.Sprintf("%s/%s/%s", reqData.Endpoint, httpbinding.EscapePath(reqData.Bucket, false), httpbinding.EscapePath(reqData.Object, false)) req, err := http.NewRequest(strings.ToUpper(reqData.Method), urlStr, nil) if err != nil { @@ -76,12 +78,12 @@ func PresignRequestV4a(cred aws.Credentials, reqData RequestData, presignData Pr } req.Header.Set(AmzDate, presignData.SignTime.Format("20060102T150405Z")) - req.Header.Set(AmzExpires, strconv.Itoa(int(presignData.Lifetime.Seconds()))) + req.Header.Set(AmzExpires, strconv.FormatFloat(presignData.Lifetime.Round(time.Second).Seconds(), 'f', 0, 64)) signer := v4a.NewSigner(func(options *v4a.SignerOptions) { options.DisableURIPathEscaping = true options.LogSigning = true - options.Logger = logging.NewStandardLogger(os.Stdout) + options.Logger = &logWrapper{log: log} }) credAdapter := v4a.SymmetricCredentialAdaptor{ @@ -97,7 +99,21 @@ func PresignRequestV4a(cred aws.Credentials, reqData RequestData, presignData Pr return nil, fmt.Errorf("presign: %w", err) } - fmt.Println(presignedURL) - return http.NewRequest(reqData.Method, presignedURL, nil) } + +type logWrapper struct { + log *zap.Logger +} + +func (l *logWrapper) Logf(classification logging.Classification, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + switch classification { + case logging.Warn: + l.log.Warn(msg) + case logging.Debug: + l.log.Debug(msg) + default: + l.log.Info(msg) + } +} diff --git a/api/auth/presign_test.go b/api/auth/presign_test.go index c91499b0..bc2bf34c 100644 --- a/api/auth/presign_test.go +++ b/api/auth/presign_test.go @@ -20,6 +20,7 @@ import ( credentialsv2 "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/smithy-go/logging" "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" ) var _ tokens.Credentials = (*credentialsMock)(nil) @@ -83,7 +84,7 @@ func TestCheckSign(t *testing.T) { }, } - req, err := PresignRequest(ctx, awsCreds, reqData, presignData) + req, err := PresignRequest(ctx, awsCreds, reqData, presignData, zaptest.NewLogger(t)) require.NoError(t, err) expBox := &accessbox.Box{ @@ -131,7 +132,7 @@ func TestCheckSignV4a(t *testing.T) { }, } - req, err := PresignRequestV4a(awsCreds, reqData, presignData) + req, err := PresignRequestV4a(awsCreds, reqData, presignData, zaptest.NewLogger(t)) require.NoError(t, err) req.Header.Set(ContentTypeHdr, "text/plain") diff --git a/cmd/s3-authmate/modules/generate-presigned-url.go b/cmd/s3-authmate/modules/generate-presigned-url.go index e7b61638..330e766c 100644 --- a/cmd/s3-authmate/modules/generate-presigned-url.go +++ b/cmd/s3-authmate/modules/generate-presigned-url.go @@ -1,6 +1,7 @@ package modules import ( + "context" "encoding/json" "fmt" "net/http" @@ -60,6 +61,11 @@ func initGeneratePresignedURLCmd() { } func runGeneratePresignedURLCmd(cmd *cobra.Command, _ []string) error { + ctx, cancel := context.WithTimeout(cmd.Context(), viper.GetDuration(timeoutFlag)) + defer cancel() + + log := getLogger() + var ( region string creds aws.Credentials @@ -68,16 +74,16 @@ func runGeneratePresignedURLCmd(cmd *cobra.Command, _ []string) error { profile := viper.GetString(profileFlag) if profile == "" { - cfg, err := config.LoadDefaultConfig(cmd.Context()) + cfg, err := config.LoadDefaultConfig(ctx) if err != nil { return wrapPreparationError(err) } region = cfg.Region - if creds, err = cfg.Credentials.Retrieve(cmd.Context()); err != nil { + if creds, err = cfg.Credentials.Retrieve(ctx); err != nil { return wrapPreparationError(fmt.Errorf("couldn't get default aws credentials: %w", err)) } } else { - cfg, err := config.LoadSharedConfigProfile(cmd.Context(), viper.GetString(profileFlag)) + cfg, err := config.LoadSharedConfigProfile(ctx, viper.GetString(profileFlag)) if err != nil { return wrapPreparationError(fmt.Errorf("couldn't get '%s' aws credentials: %w", viper.GetString(profileFlag), err)) } @@ -117,9 +123,9 @@ func runGeneratePresignedURLCmd(cmd *cobra.Command, _ []string) error { var req *http.Request if viper.GetBool(sigV4AFlag) { - req, err = auth.PresignRequestV4a(creds, reqData, presignData) + req, err = auth.PresignRequestV4a(creds, reqData, presignData, log) } else { - req, err = auth.PresignRequest(cmd.Context(), creds, reqData, presignData) + req, err = auth.PresignRequest(ctx, creds, reqData, presignData, log) } if err != nil { return wrapBusinessLogicError(err) diff --git a/cmd/s3-authmate/modules/sign.go b/cmd/s3-authmate/modules/sign.go index e2005e89..a20dde55 100644 --- a/cmd/s3-authmate/modules/sign.go +++ b/cmd/s3-authmate/modules/sign.go @@ -1,6 +1,7 @@ package modules import ( + "context" "errors" "fmt" "os" @@ -49,22 +50,25 @@ func initSignCmd() { } func runSignCmd(cmd *cobra.Command, _ []string) error { + ctx, cancel := context.WithTimeout(cmd.Context(), viper.GetDuration(timeoutFlag)) + defer cancel() + var ( region string creds aws.Credentials ) if profile := viper.GetString(profileFlag); profile == "" { - cfg, err := config.LoadDefaultConfig(cmd.Context()) + cfg, err := config.LoadDefaultConfig(ctx) if err != nil { return wrapPreparationError(err) } region = cfg.Region - if creds, err = cfg.Credentials.Retrieve(cmd.Context()); err != nil { + if creds, err = cfg.Credentials.Retrieve(ctx); err != nil { return wrapPreparationError(fmt.Errorf("couldn't get default aws credentials: %w", err)) } } else { - cfg, err := config.LoadSharedConfigProfile(cmd.Context(), profile) + cfg, err := config.LoadSharedConfigProfile(ctx, profile) if err != nil { return wrapPreparationError(fmt.Errorf("couldn't get '%s' aws credentials: %w", profile, err)) } @@ -106,8 +110,8 @@ func runSignCmd(cmd *cobra.Command, _ []string) error { sigv4a := viper.GetBool(sigV4AFlag) if sigv4a { var err error - if signature, err = auth.SignStrV4A(creds, data); err != nil { - return wrapPreparationError(fmt.Errorf("sign v4a: %w", err)) + if signature, err = auth.SignStrV4A(ctx, creds, data); err != nil { + return wrapPreparationError(fmt.Errorf("sign str v4a: %w", err)) } } else { signature = auth.SignStr(creds.SecretAccessKey, service, region, signTime, data)