[#467] authmate: Add sign command
Support singing arbitrary data using aws sigv4 algorithm Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
c2adbd758a
commit
03481274f0
3 changed files with 119 additions and 0 deletions
|
@ -8,6 +8,7 @@ This document outlines major changes between releases.
|
|||
- Add support for virtual hosted style addressing (#446, #449)
|
||||
- Support new param `frostfs.graceful_close_on_switch_timeout` (#475)
|
||||
- Support patch object method (#479)
|
||||
- Add `sign` command to `frostfs-s3-authmate` (#467)
|
||||
|
||||
### Changed
|
||||
- Update go version to go1.19 (#470)
|
||||
|
|
|
@ -68,4 +68,7 @@ GoVersion: {{ runtimeVersion }}
|
|||
|
||||
rootCmd.AddCommand(registerUserCmd)
|
||||
initRegisterUserCmd()
|
||||
|
||||
rootCmd.AddCommand(signCmd)
|
||||
initSignCmd()
|
||||
}
|
||||
|
|
115
cmd/s3-authmate/modules/sign.go
Normal file
115
cmd/s3-authmate/modules/sign.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"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`,
|
||||
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.MarkFlagRequired(dataFlag)
|
||||
}
|
||||
|
||||
func runSignCmd(cmd *cobra.Command, _ []string) error {
|
||||
var cfg aws.Config
|
||||
|
||||
if region := viper.GetString(regionFlag); region != "" {
|
||||
cfg.Region = ®ion
|
||||
}
|
||||
accessKeyID := viper.GetString(awsAccessKeyIDFlag)
|
||||
secretAccessKey := viper.GetString(awsSecretAccessKeyFlag)
|
||||
|
||||
if accessKeyID != "" && secretAccessKey != "" {
|
||||
cfg.Credentials = credentials.NewStaticCredentialsFromCreds(credentials.Value{
|
||||
AccessKeyID: accessKeyID,
|
||||
SecretAccessKey: secretAccessKey,
|
||||
})
|
||||
} else if accessKeyID != "" || secretAccessKey != "" {
|
||||
return wrapPreparationError(fmt.Errorf("both flags '%s' and '%s' must be provided", accessKeyIDFlag, awsSecretAccessKeyFlag))
|
||||
}
|
||||
|
||||
sess, err := session.NewSessionWithOptions(session.Options{
|
||||
Config: cfg,
|
||||
Profile: viper.GetString(profileFlag),
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
})
|
||||
if err != nil {
|
||||
return wrapPreparationError(fmt.Errorf("couldn't get aws credentials: %w", err))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
creds, err := sess.Config.Credentials.Get()
|
||||
if err != nil {
|
||||
return wrapPreparationError(fmt.Errorf("get creds: %w", err))
|
||||
}
|
||||
|
||||
if sess.Config.Region == nil || *sess.Config.Region == "" {
|
||||
return wrapPreparationError(errors.New("missing region"))
|
||||
}
|
||||
|
||||
service := viper.GetString(serviceFlag)
|
||||
if service == "" {
|
||||
return wrapPreparationError(errors.New("missing service"))
|
||||
}
|
||||
|
||||
signTime := viper.GetTime(timeFlag)
|
||||
if signTime.IsZero() {
|
||||
signTime = time.Now()
|
||||
}
|
||||
|
||||
signature := auth.SignStr(creds.SecretAccessKey, service, *sess.Config.Region, signTime, data)
|
||||
|
||||
cmd.Println("service:", service)
|
||||
cmd.Println("region:", *sess.Config.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
|
||||
}
|
Loading…
Reference in a new issue