package modules import ( "context" "errors" "fmt" "os" "strings" "time" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/spf13/cobra" "github.com/spf13/viper" ) var signCmd = &cobra.Command{ Use: "sign", Short: "Sign arbitrary data using AWS Signature Version 4", Long: `Generate signature for provided data using AWS credentials. Credentials must be placed in ~/.aws/credentials. You can provide profile to load using --profile flag or explicitly provide credentials and region using --aws-access-key-id, --aws-secret-access-key, --region. Note to override credentials you must provide both access key and secret key.`, Example: `frostfs-s3-authmate sign --data some-data frostfs-s3-authmate sign --data file://data.txt frostfs-s3-authmate sign --data file://data.txt --profile my-profile --time 2024-09-27 frostfs-s3-authmate sign --data some-data --region ru --service s3 --time 2024-09-27 --aws-access-key-id ETaA2CadPcA7bAkLsML2PbTudXY8uRt2PDjCCwkvRv9s0FDCxWDXYc1SA1vKv8KbyCNsLY2AmAjJ92Vz5rgvsFCy --aws-secret-access-key c2d65ef2980f03f4f495bdebedeeae760496697880d61d106bb9a4e5cd2e0607 frostfs-s3-authmate sign --data some-data --sigv4a`, RunE: runSignCmd, } const ( serviceFlag = "s3" timeFlag = "time" dataFlag = "data" ) func initSignCmd() { signCmd.Flags().StringP(dataFlag, "d", "", "Data to sign. Can be provided as string or as a file ('file://path-to-file')") signCmd.Flags().String(profileFlag, "", "AWS profile to load") signCmd.Flags().String(serviceFlag, "s3", "AWS service name to form signature") signCmd.Flags().String(timeFlag, "", "Signing time in '2006-01-02' format (default is current UTC time)") signCmd.Flags().String(regionFlag, "", "AWS region to use in signature (default is taken from ~/.aws/config)") signCmd.Flags().String(awsAccessKeyIDFlag, "", "AWS access key id to sign data (default is taken from ~/.aws/credentials)") signCmd.Flags().String(awsSecretAccessKeyFlag, "", "AWS secret access key to sign data (default is taken from ~/.aws/credentials)") signCmd.Flags().Bool(sigV4AFlag, false, "Use SigV4A for signing request") _ = signCmd.MarkFlagRequired(dataFlag) } 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(ctx) if err != nil { return wrapPreparationError(err) } region = cfg.Region 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(ctx, profile) if err != nil { return wrapPreparationError(fmt.Errorf("couldn't get '%s' aws credentials: %w", profile, err)) } region = cfg.Region creds = cfg.Credentials } accessKeyIDArg := viper.GetString(awsAccessKeyIDFlag) secretAccessKeyArg := viper.GetString(awsSecretAccessKeyFlag) if accessKeyIDArg != "" && secretAccessKeyArg != "" { creds.AccessKeyID = accessKeyIDArg creds.SecretAccessKey = secretAccessKeyArg } if regionArg := viper.GetString(regionFlag); regionArg != "" { region = regionArg } data := viper.GetString(dataFlag) if strings.HasPrefix(data, "file://") { dataToSign, err := os.ReadFile(data[7:]) if err != nil { return wrapPreparationError(fmt.Errorf("read data file: %w", err)) } data = string(dataToSign) } service := viper.GetString(serviceFlag) if service == "" { return wrapPreparationError(errors.New("missing service")) } signTime := viper.GetTime(timeFlag) if signTime.IsZero() { signTime = time.Now() } var signature string sigv4a := viper.GetBool(sigV4AFlag) if sigv4a { var err error 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) } if !sigv4a { cmd.Println("service:", service) cmd.Println("region:", region) cmd.Println("time:", signTime.UTC().Format("20060102")) } cmd.Println("accessKeyId:", creds.AccessKeyID) cmd.Printf("secretAccessKey: [****************%s]\n", creds.SecretAccessKey[max(0, len(creds.SecretAccessKey)-4):]) cmd.Println("signature:", signature) return nil }