mirror of
https://github.com/versity/versitygw.git
synced 2026-01-07 12:15:18 +00:00
279 lines
8.7 KiB
Go
279 lines
8.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var method *string
|
|
var url *string
|
|
var bucketName *string
|
|
var objectKey *string
|
|
var query *string
|
|
var awsRegion *string
|
|
var awsAccessKeyId *string
|
|
var awsSecretAccessKey *string
|
|
var serviceName *string
|
|
var debug *bool
|
|
var signedParamsMap restParams
|
|
var payloadFile *string
|
|
var incorrectSignature *bool
|
|
var incorrectCredential *string
|
|
var authorizationScheme *string
|
|
var incorrectYearMonthDay *bool
|
|
var invalidYearMonthDay *bool
|
|
|
|
type S3Command struct {
|
|
Method string
|
|
Url string
|
|
BucketName string
|
|
ObjectKey string
|
|
Query string
|
|
AwsRegion string
|
|
AwsAccessKeyId string
|
|
AwsSecretAccessKey string
|
|
ServiceName string
|
|
SignedParams map[string]string
|
|
PayloadFile string
|
|
IncorrectSignature bool
|
|
AuthorizationHeaderMalformed bool
|
|
AuthorizationScheme string
|
|
IncorrectCredential string
|
|
IncorrectYearMonthDay bool
|
|
InvalidYearMonthDay bool
|
|
|
|
currentDateTime string
|
|
host string
|
|
payloadHash string
|
|
headerValues [][]string
|
|
canonicalRequestHash string
|
|
path string
|
|
signedParamString string
|
|
yearMonthDay string
|
|
signature string
|
|
}
|
|
|
|
type restParams map[string]string
|
|
|
|
func (r *restParams) String() string {
|
|
return fmt.Sprintf("%v", *r)
|
|
}
|
|
|
|
func (r *restParams) Set(value string) error {
|
|
*r = make(map[string]string)
|
|
pairs := strings.Split(value, ",")
|
|
for _, pair := range pairs {
|
|
kv := strings.SplitN(pair, ":", 2)
|
|
if len(kv) != 2 {
|
|
}
|
|
if len(kv) != 2 {
|
|
return fmt.Errorf("invalid key-value pair: %s", pair)
|
|
}
|
|
(*r)[kv[0]] = kv[1]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
if err := checkFlags(); err != nil {
|
|
log.Fatalf("Error checking flags: %v", err)
|
|
}
|
|
|
|
s3Command := &S3Command{
|
|
Method: *method,
|
|
Url: *url,
|
|
BucketName: *bucketName,
|
|
ObjectKey: *objectKey,
|
|
Query: *query,
|
|
AwsRegion: *awsRegion,
|
|
AwsAccessKeyId: *awsAccessKeyId,
|
|
AwsSecretAccessKey: *awsSecretAccessKey,
|
|
ServiceName: *serviceName,
|
|
SignedParams: signedParamsMap,
|
|
PayloadFile: *payloadFile,
|
|
IncorrectSignature: *incorrectSignature,
|
|
AuthorizationScheme: *authorizationScheme,
|
|
IncorrectCredential: *incorrectCredential,
|
|
IncorrectYearMonthDay: *incorrectYearMonthDay,
|
|
InvalidYearMonthDay: *invalidYearMonthDay,
|
|
}
|
|
curlShellCommand, err := s3Command.CurlShellCommand()
|
|
if err != nil {
|
|
log.Fatalf("Error generating curl command: %v", err)
|
|
}
|
|
fmt.Println(curlShellCommand)
|
|
}
|
|
|
|
func checkFlags() error {
|
|
method = flag.String("method", "GET", "HTTP method to use")
|
|
url = flag.String("url", "https://localhost:7070", "S3 server URL")
|
|
bucketName = flag.String("bucketName", "", "Bucket name")
|
|
objectKey = flag.String("objectKey", "", "Object key")
|
|
query = flag.String("query", "", "S3 query")
|
|
awsAccessKeyId = flag.String("awsAccessKeyId", "", "AWS access key ID")
|
|
awsSecretAccessKey = flag.String("awsSecretAccessKey", "", "AWS secret access key")
|
|
awsRegion = flag.String("awsRegion", "us-east-1", "AWS region")
|
|
serviceName = flag.String("serviceName", "s3", "Service name")
|
|
debug = flag.Bool("debug", false, "Print debug statements")
|
|
flag.Var(&signedParamsMap, "signedParams", "Signed params, separated by comma")
|
|
payloadFile = flag.String("payloadFile", "", "Payload file path, if any")
|
|
incorrectSignature = flag.Bool("incorrectSignature", false, "Simulate an incorrect signature")
|
|
incorrectYearMonthDay = flag.Bool("incorrectYearMonthDay", false, "Simulate an incorrect year/month/day")
|
|
invalidYearMonthDay = flag.Bool("invalidYearMonthDay", false, "Simulate an invalid year/month/day")
|
|
incorrectCredential = flag.String("incorrectCredential", "", "Add an incorrect credential string")
|
|
authorizationScheme = flag.String("authorizationScheme", "AWS4-HMAC-SHA256", "Authorization Scheme")
|
|
// Parse the flags
|
|
flag.Parse()
|
|
|
|
if flag.Lookup("awsAccessKeyId").Value.String() == "" {
|
|
return fmt.Errorf("the 'awsAccessKeyId' value must be set")
|
|
}
|
|
if flag.Lookup("awsSecretAccessKey").Value.String() == "" {
|
|
return fmt.Errorf("the 'awsSecretAccessKey' value must be set")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func printDebug(format string, args ...any) {
|
|
if *debug {
|
|
log.Printf(format, args...)
|
|
}
|
|
}
|
|
|
|
func hmacSHA256(key []byte, data string) []byte {
|
|
h := hmac.New(sha256.New, key)
|
|
h.Write([]byte(data))
|
|
return h.Sum(nil)
|
|
}
|
|
|
|
func (s *S3Command) CurlShellCommand() (string, error) {
|
|
if s.IncorrectYearMonthDay {
|
|
s.currentDateTime = time.Now().Add(-48 * time.Hour).UTC().Format("20060102T150405Z")
|
|
} else {
|
|
s.currentDateTime = time.Now().UTC().Format("20060102T150405Z")
|
|
}
|
|
protocolAndHost := strings.Split(s.Url, "://")
|
|
if len(protocolAndHost) != 2 {
|
|
return "", fmt.Errorf("invalid URL value: %s", s.Url)
|
|
}
|
|
s.host = protocolAndHost[1]
|
|
s.payloadHash = "UNSIGNED-PAYLOAD"
|
|
s.headerValues = [][]string{
|
|
{"host", s.host},
|
|
{"x-amz-content-sha256", s.payloadHash},
|
|
{"x-amz-date", s.currentDateTime},
|
|
}
|
|
for key, value := range s.SignedParams {
|
|
s.headerValues = append(s.headerValues, []string{key, value})
|
|
}
|
|
sort.Slice(s.headerValues,
|
|
func(i, j int) bool {
|
|
return s.headerValues[i][0] < s.headerValues[j][0]
|
|
})
|
|
s.path = "/" + s.BucketName
|
|
if s.ObjectKey != "" {
|
|
s.path += "/" + s.ObjectKey
|
|
}
|
|
s.generateCanonicalRequestString()
|
|
|
|
s.yearMonthDay = strings.Split(s.currentDateTime, "T")[0]
|
|
if s.InvalidYearMonthDay {
|
|
s.yearMonthDay = s.yearMonthDay[:len(s.yearMonthDay)-1]
|
|
}
|
|
s.getStsSignature()
|
|
|
|
return s.buildCurlShellCommand(), nil
|
|
}
|
|
|
|
func (s *S3Command) generateCanonicalRequestString() {
|
|
canonicalRequestLines := []string{*method}
|
|
|
|
canonicalRequestLines = append(canonicalRequestLines, s.path)
|
|
canonicalRequestLines = append(canonicalRequestLines, s.Query)
|
|
//canonicalRequestLines = append(canonicalRequestLines, "host:"+s.host)
|
|
|
|
var signedParams []string
|
|
for _, headerValue := range s.headerValues {
|
|
key := strings.ToLower(headerValue[0])
|
|
canonicalRequestLines = append(canonicalRequestLines, key+":"+headerValue[1])
|
|
signedParams = append(signedParams, key)
|
|
}
|
|
|
|
canonicalRequestLines = append(canonicalRequestLines, "")
|
|
s.signedParamString = strings.Join(signedParams, ";")
|
|
canonicalRequestLines = append(canonicalRequestLines, s.signedParamString, s.payloadHash)
|
|
|
|
canonicalRequestString := strings.Join(canonicalRequestLines, "\n")
|
|
printDebug("Canonical request string: %s\n", canonicalRequestString)
|
|
|
|
canonicalRequestHashBytes := sha256.Sum256([]byte(canonicalRequestString))
|
|
s.canonicalRequestHash = hex.EncodeToString(canonicalRequestHashBytes[:])
|
|
}
|
|
|
|
func (s *S3Command) getStsSignature() {
|
|
thirdLine := fmt.Sprintf("%s/%s/%s/aws4_request", s.yearMonthDay, s.AwsRegion, s.ServiceName)
|
|
stsDataLines := []string{
|
|
s.AuthorizationScheme,
|
|
s.currentDateTime,
|
|
thirdLine,
|
|
s.canonicalRequestHash,
|
|
}
|
|
stsDataString := strings.Join(stsDataLines, "\n")
|
|
|
|
// Derive signing key step by step
|
|
dateKey := hmacSHA256([]byte("AWS4"+s.AwsSecretAccessKey), s.yearMonthDay)
|
|
dateRegionKey := hmacSHA256(dateKey, s.AwsRegion)
|
|
dateRegionServiceKey := hmacSHA256(dateRegionKey, s.ServiceName)
|
|
signingKey := hmacSHA256(dateRegionServiceKey, "aws4_request")
|
|
|
|
// Generate signature
|
|
signatureBytes := hmacSHA256(signingKey, stsDataString)
|
|
if s.IncorrectSignature {
|
|
if signatureBytes[0] == 'a' {
|
|
signatureBytes[0] = 'A'
|
|
} else {
|
|
signatureBytes[0] = 'a'
|
|
}
|
|
}
|
|
|
|
// Print hex-encoded signature
|
|
s.signature = hex.EncodeToString(signatureBytes)
|
|
}
|
|
|
|
func (s *S3Command) buildCurlShellCommand() string {
|
|
curlCommand := []string{"curl", "-iks"}
|
|
if s.Method != "GET" {
|
|
curlCommand = append(curlCommand, fmt.Sprintf("-X %s ", s.Method))
|
|
}
|
|
fullPath := "\"" + *url + s.path
|
|
if *query != "" {
|
|
fullPath += "?" + *query
|
|
}
|
|
fullPath += "\""
|
|
curlCommand = append(curlCommand, fullPath)
|
|
var credentialString string
|
|
if s.IncorrectCredential == "" {
|
|
credentialString = fmt.Sprintf("%s/%s/%s/%s/aws4_request", s.AwsAccessKeyId, s.yearMonthDay, s.AwsRegion, s.ServiceName)
|
|
} else {
|
|
credentialString = s.IncorrectCredential
|
|
}
|
|
authorizationString := fmt.Sprintf("\"Authorization: %s Credential=%s,SignedHeaders=%s,Signature=%s\"",
|
|
s.AuthorizationScheme, credentialString, s.signedParamString, s.signature)
|
|
curlCommand = append(curlCommand, "-H", authorizationString)
|
|
for _, headerValue := range s.headerValues {
|
|
headerString := fmt.Sprintf("\"%s: %s\"", headerValue[0], headerValue[1])
|
|
curlCommand = append(curlCommand, "-H", headerString)
|
|
}
|
|
if s.PayloadFile != "" {
|
|
curlCommand = append(curlCommand, "-T", s.PayloadFile)
|
|
}
|
|
return strings.Join(curlCommand, " ")
|
|
}
|