Merge pull request #55 from versity/ben/upload_errors

fix upload from aws cli
This commit is contained in:
Ben McClelland
2023-06-06 07:16:04 -07:00
committed by GitHub
5 changed files with 149 additions and 12 deletions

View File

@@ -23,6 +23,7 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/urfave/cli/v2"
"github.com/versity/versitygw/backend"
"github.com/versity/versitygw/backend/auth"
"github.com/versity/versitygw/s3api"
"github.com/versity/versitygw/s3api/middlewares"
)
@@ -144,7 +145,12 @@ func runGateway(be backend.Backend) error {
opts = append(opts, s3api.WithTLS(cert))
}
srv, err := s3api.New(app, be, port, middlewares.AdminConfig{AdminAccess: adminAccess, AdminSecret: adminSecret, Region: region}, opts...)
srv, err := s3api.New(app, be, port,
middlewares.AdminConfig{
AdminAccess: adminAccess,
AdminSecret: adminSecret,
Region: region,
}, auth.IAMServiceUnsupported{}, opts...)
if err != nil {
return fmt.Errorf("init gateway: %v", err)
}

View File

@@ -23,6 +23,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/gofiber/fiber/v2"
"github.com/versity/versitygw/backend/auth"
"github.com/versity/versitygw/s3api/controllers"
"github.com/versity/versitygw/s3api/utils"
"github.com/versity/versitygw/s3err"
@@ -38,7 +39,12 @@ type AdminConfig struct {
Region string
}
func VerifyV4Signature(config AdminConfig) fiber.Handler {
func VerifyV4Signature(config AdminConfig, iam auth.IAMService) fiber.Handler {
acct := accounts{
admin: config,
iam: iam,
}
return func(ctx *fiber.Ctx) error {
authorization := ctx.Get("Authorization")
if authorization == "" {
@@ -47,11 +53,22 @@ func VerifyV4Signature(config AdminConfig) fiber.Handler {
// Check the signature version
authParts := strings.Split(authorization, " ")
if len(authParts) < 4 {
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrMissingFields))
}
if authParts[0] != "AWS4-HMAC-SHA256" {
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrSignatureVersionNotSupported))
}
creds := strings.Split(strings.Split(authParts[1], "=")[1], "/")
if len(creds) < 4 {
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrCredMalformed))
}
secret, ok := acct.getAcctSecret(creds[0])
if !ok {
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
}
// Check X-Amz-Date header
date := ctx.Get("X-Amz-Date")
@@ -79,20 +96,23 @@ func VerifyV4Signature(config AdminConfig) fiber.Handler {
// Create a new http request instance from fasthttp request
req, err := utils.CreateHttpRequestFromCtx(ctx)
if err != nil {
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrAccessDenied))
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrInternalError))
}
signer := v4.NewSigner()
signErr := signer.SignHTTP(req.Context(), aws.Credentials{
AccessKeyID: config.AdminAccess,
SecretAccessKey: config.AdminSecret,
AccessKeyID: creds[0],
SecretAccessKey: secret,
}, req, hexPayload, creds[3], config.Region, tdate)
if signErr != nil {
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrAccessDenied))
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrInternalError))
}
parts := strings.Split(req.Header.Get("Authorization"), " ")
if len(parts) < 4 {
return controllers.Responce[any](ctx, nil, s3err.GetAPIError(s3err.ErrMissingFields))
}
calculatedSign := strings.Split(parts[3], "=")[1]
expectedSign := strings.Split(authParts[3], "=")[1]
@@ -103,3 +123,22 @@ func VerifyV4Signature(config AdminConfig) fiber.Handler {
return ctx.Next()
}
}
type accounts struct {
admin AdminConfig
iam auth.IAMService
}
func (a accounts) getAcctSecret(access string) (string, bool) {
if a.admin.AdminAccess == access {
return a.admin.AdminSecret, true
}
conf, err := a.iam.GetIAMConfig()
if err != nil {
return "", false
}
secret, ok := conf.AccessAccounts[access]
return secret, ok
}

View File

@@ -20,6 +20,7 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/versity/versitygw/backend"
"github.com/versity/versitygw/backend/auth"
"github.com/versity/versitygw/s3api/middlewares"
)
@@ -31,7 +32,7 @@ type S3ApiServer struct {
cert *tls.Certificate
}
func New(app *fiber.App, be backend.Backend, port string, adminUser middlewares.AdminConfig, opts ...Option) (*S3ApiServer, error) {
func New(app *fiber.App, be backend.Backend, port string, adminUser middlewares.AdminConfig, iam auth.IAMService, opts ...Option) (*S3ApiServer, error) {
server := &S3ApiServer{
app: app,
backend: be,
@@ -43,7 +44,7 @@ func New(app *fiber.App, be backend.Backend, port string, adminUser middlewares.
opt(server)
}
app.Use(middlewares.VerifyV4Signature(adminUser))
app.Use(middlewares.VerifyV4Signature(adminUser, iam))
app.Use(logger.New())
server.router.Init(app, be)
return server, nil

View File

@@ -20,6 +20,7 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/versity/versitygw/backend"
"github.com/versity/versitygw/backend/auth"
"github.com/versity/versitygw/s3api/middlewares"
)
@@ -61,7 +62,8 @@ func TestNew(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotS3ApiServer, err := New(tt.args.app, tt.args.be, tt.args.port, tt.args.adminUser)
gotS3ApiServer, err := New(tt.args.app, tt.args.be,
tt.args.port, tt.args.adminUser, auth.IAMServiceUnsupported{})
if (err != nil) != tt.wantErr {
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
return

View File

@@ -48,16 +48,105 @@ func CreateHttpRequestFromCtx(ctx *fiber.Ctx) (*http.Request, error) {
// Set the request headers
req.Header.VisitAll(func(key, value []byte) {
keyStr := string(key)
if keyStr == "X-Amz-Date" || keyStr == "X-Amz-Content-Sha256" || keyStr == "Host" {
if includeHeader(keyStr) {
httpReq.Header.Add(keyStr, string(value))
}
})
// Set the Content-Length header
httpReq.ContentLength = int64(len(req.Body()))
// Content-Length header ignored for signing
httpReq.ContentLength = 0
// Set the Host header
httpReq.Host = string(req.Header.Host())
return httpReq, nil
}
func includeHeader(hdr string) bool {
switch {
case strings.EqualFold(hdr, "Cache-Control"):
return true
case strings.EqualFold(hdr, "Content-Disposition"):
return true
case strings.EqualFold(hdr, "Content-Encoding"):
return true
case strings.EqualFold(hdr, "Content-Language"):
return true
case strings.EqualFold(hdr, "Content-Md5"):
return true
case strings.EqualFold(hdr, "Content-Type"):
return true
case strings.EqualFold(hdr, "Expires"):
return true
case strings.EqualFold(hdr, "If-Match"):
return true
case strings.EqualFold(hdr, "If-Modified-Since"):
return true
case strings.EqualFold(hdr, "If-None-Match"):
return true
case strings.EqualFold(hdr, "If-Unmodified-Since"):
return true
case strings.EqualFold(hdr, "Range"):
return true
case strings.EqualFold(hdr, "X-Amz-Acl"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-If-Match"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-If-Modified-Since"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-If-None-Match"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-If-Unmodified-Since"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-Range"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key"):
return true
case strings.EqualFold(hdr, "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5"):
return true
case strings.EqualFold(hdr, "X-Amz-Grant-Full-control"):
return true
case strings.EqualFold(hdr, "X-Amz-Grant-Read"):
return true
case strings.EqualFold(hdr, "X-Amz-Grant-Read-Acp"):
return true
case strings.EqualFold(hdr, "X-Amz-Grant-Write"):
return true
case strings.EqualFold(hdr, "X-Amz-Grant-Write-Acp"):
return true
case strings.EqualFold(hdr, "X-Amz-Metadata-Directive"):
return true
case strings.EqualFold(hdr, "X-Amz-Mfa"):
return true
case strings.EqualFold(hdr, "X-Amz-Request-Payer"):
return true
case strings.EqualFold(hdr, "X-Amz-Server-Side-Encryption"):
return true
case strings.EqualFold(hdr, "X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id"):
return true
case strings.EqualFold(hdr, "X-Amz-Server-Side-Encryption-Customer-Algorithm"):
return true
case strings.EqualFold(hdr, "X-Amz-Server-Side-Encryption-Customer-Key"):
return true
case strings.EqualFold(hdr, "X-Amz-Server-Side-Encryption-Customer-Key-Md5"):
return true
case strings.EqualFold(hdr, "X-Amz-Storage-Class"):
return true
case strings.EqualFold(hdr, "X-Amz-Website-Redirect-Location"):
return true
case strings.EqualFold(hdr, "X-Amz-Content-Sha256"):
return true
case strings.EqualFold(hdr, "X-Amz-Tagging"):
return true
case strings.HasPrefix(hdr, "X-Amz-Object-Lock-"):
return true
case strings.HasPrefix(hdr, "X-Amz-Meta-"):
return true
default:
return false
}
}