2024-09-27 13:15:28 +00:00
|
|
|
package modules
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
2024-09-04 14:35:39 +00:00
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
2024-09-27 13:15:28 +00:00
|
|
|
"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
|
2024-09-04 14:35:39 +00:00
|
|
|
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`,
|
2024-09-27 13:15:28 +00:00
|
|
|
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)")
|
2024-09-04 14:35:39 +00:00
|
|
|
signCmd.Flags().Bool(sigV4AFlag, false, "Use SigV4A for signing request")
|
2024-09-27 13:15:28 +00:00
|
|
|
|
|
|
|
_ = signCmd.MarkFlagRequired(dataFlag)
|
|
|
|
}
|
|
|
|
|
|
|
|
func runSignCmd(cmd *cobra.Command, _ []string) error {
|
2024-09-04 14:35:39 +00:00
|
|
|
var (
|
|
|
|
region string
|
|
|
|
creds aws.Credentials
|
|
|
|
)
|
2024-09-27 13:15:28 +00:00
|
|
|
|
2024-09-04 14:35:39 +00:00
|
|
|
if profile := viper.GetString(profileFlag); profile == "" {
|
|
|
|
cfg, err := config.LoadDefaultConfig(cmd.Context())
|
|
|
|
if err != nil {
|
|
|
|
return wrapPreparationError(err)
|
|
|
|
}
|
|
|
|
region = cfg.Region
|
|
|
|
if creds, err = cfg.Credentials.Retrieve(cmd.Context()); err != nil {
|
|
|
|
return wrapPreparationError(fmt.Errorf("couldn't get default aws credentials: %w", err))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cfg, err := config.LoadSharedConfigProfile(cmd.Context(), profile)
|
|
|
|
if err != nil {
|
|
|
|
return wrapPreparationError(fmt.Errorf("couldn't get '%s' aws credentials: %w", profile, err))
|
|
|
|
}
|
|
|
|
region = cfg.Region
|
|
|
|
creds = cfg.Credentials
|
2024-09-27 13:15:28 +00:00
|
|
|
}
|
2024-09-04 14:35:39 +00:00
|
|
|
|
|
|
|
accessKeyIDArg := viper.GetString(awsAccessKeyIDFlag)
|
|
|
|
secretAccessKeyArg := viper.GetString(awsSecretAccessKeyFlag)
|
|
|
|
if accessKeyIDArg != "" && secretAccessKeyArg != "" {
|
|
|
|
creds.AccessKeyID = accessKeyIDArg
|
|
|
|
creds.SecretAccessKey = secretAccessKeyArg
|
2024-09-27 13:15:28 +00:00
|
|
|
}
|
|
|
|
|
2024-09-04 14:35:39 +00:00
|
|
|
if regionArg := viper.GetString(regionFlag); regionArg != "" {
|
|
|
|
region = regionArg
|
2024-09-27 13:15:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2024-09-04 14:35:39 +00:00
|
|
|
var signature string
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
} 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"))
|
|
|
|
}
|
2024-09-27 13:15:28 +00:00
|
|
|
|
|
|
|
cmd.Println("accessKeyId:", creds.AccessKeyID)
|
|
|
|
cmd.Printf("secretAccessKey: [****************%s]\n", creds.SecretAccessKey[max(0, len(creds.SecretAccessKey)-4):])
|
|
|
|
cmd.Println("signature:", signature)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|