mirror of
https://github.com/versity/versitygw.git
synced 2026-01-09 04:53:10 +00:00
Merge pull request #55 from versity/ben/upload_errors
fix upload from aws cli
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user