From a7887166cfad779b693731dbf4fc1d678a2c81d0 Mon Sep 17 00:00:00 2001 From: chrislu Date: Mon, 3 Jan 2022 15:39:36 -0800 Subject: [PATCH] wildcard prefix to restrict access to directories in s3 bucket https://github.com/chrislusf/seaweedfs/discussions/2551 --- weed/s3api/auth_credentials.go | 12 +++---- weed/s3api/auth_credentials_test.go | 49 +++++++++++++++++++++++++++++ weed/s3api/chunked_reader_v4.go | 5 +-- weed/s3api/s3api_bucket_handlers.go | 2 +- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/weed/s3api/auth_credentials.go b/weed/s3api/auth_credentials.go index 87d478136..3c27b7d35 100644 --- a/weed/s3api/auth_credentials.go +++ b/weed/s3api/auth_credentials.go @@ -247,9 +247,9 @@ func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action) - bucket, _ := xhttp.GetBucketAndObject(r) + bucket, object := xhttp.GetBucketAndObject(r) - if !identity.canDo(action, bucket) { + if !identity.canDo(action, bucket, object) { return identity, s3err.ErrAccessDenied } @@ -307,7 +307,7 @@ func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err return identity, s3err.ErrNone } -func (identity *Identity) canDo(action Action, bucket string) bool { +func (identity *Identity) canDo(action Action, bucket string, objectKey string) bool { if identity.isAdmin() { return true } @@ -319,15 +319,13 @@ func (identity *Identity) canDo(action Action, bucket string) bool { if bucket == "" { return false } + target := string(action) + ":" + bucket + "/" + objectKey limitedByBucket := string(action) + ":" + bucket adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket for _, a := range identity.Actions { act := string(a) if strings.HasSuffix(act, "*") { - if strings.HasPrefix(limitedByBucket, act[:len(act)-1]) { - return true - } - if strings.HasPrefix(adminLimitedByBucket, act[:len(act)-1]) { + if strings.HasPrefix(target, act[:len(act)-1]) { return true } } else { diff --git a/weed/s3api/auth_credentials_test.go b/weed/s3api/auth_credentials_test.go index 0383ddbcd..b674557fa 100644 --- a/weed/s3api/auth_credentials_test.go +++ b/weed/s3api/auth_credentials_test.go @@ -2,6 +2,7 @@ package s3api import ( . "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants" + "github.com/stretchr/testify/assert" "testing" "github.com/golang/protobuf/jsonpb" @@ -67,3 +68,51 @@ func TestIdentityListFileFormat(t *testing.T) { println(text) } + +func TestCanDo(t *testing.T) { + ident1 := &Identity{ + Name: "anything", + Actions: []Action{ + "Write:bucket1/a/b/c/*", + "Write:bucket1/a/b/other", + }, + } + // object specific + assert.Equal(t, true, ident1.canDo(ACTION_WRITE, "bucket1", "a/b/c/d.txt")) + assert.Equal(t, false, ident1.canDo(ACTION_WRITE, "bucket1", "a/b/other/some"), "action without *") + + // bucket specific + ident2 := &Identity{ + Name: "anything", + Actions: []Action{ + "Read:bucket1", + "Write:bucket1/*", + }, + } + assert.Equal(t, true, ident2.canDo(ACTION_READ, "bucket1", "a/b/c/d.txt")) + assert.Equal(t, true, ident2.canDo(ACTION_WRITE, "bucket1", "a/b/c/d.txt")) + assert.Equal(t, false, ident2.canDo(ACTION_LIST, "bucket1", "a/b/c/d.txt")) + + // across buckets + ident3 := &Identity{ + Name: "anything", + Actions: []Action{ + "Read", + "Write", + }, + } + assert.Equal(t, true, ident3.canDo(ACTION_READ, "bucket1", "a/b/c/d.txt")) + assert.Equal(t, true, ident3.canDo(ACTION_WRITE, "bucket1", "a/b/c/d.txt")) + assert.Equal(t, false, ident3.canDo(ACTION_LIST, "bucket1", "a/b/other/some")) + + // partial buckets + ident4 := &Identity{ + Name: "anything", + Actions: []Action{ + "Read:special_*", + }, + } + assert.Equal(t, true, ident4.canDo(ACTION_READ, "special_bucket", "a/b/c/d.txt")) + assert.Equal(t, false, ident4.canDo(ACTION_READ, "bucket1", "a/b/c/d.txt")) + +} diff --git a/weed/s3api/chunked_reader_v4.go b/weed/s3api/chunked_reader_v4.go index 5dd0648c6..e683faf22 100644 --- a/weed/s3api/chunked_reader_v4.go +++ b/weed/s3api/chunked_reader_v4.go @@ -25,6 +25,7 @@ import ( "encoding/hex" "errors" xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http" + "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants" "github.com/chrislusf/seaweedfs/weed/s3api/s3err" "hash" "io" @@ -91,8 +92,8 @@ func (iam *IdentityAccessManagement) calculateSeedSignature(r *http.Request) (cr return nil, "", "", time.Time{}, s3err.ErrInvalidAccessKeyID } - bucket, _ := xhttp.GetBucketAndObject(r) - if !identity.canDo("Write", bucket) { + bucket, object := xhttp.GetBucketAndObject(r) + if !identity.canDo(s3_constants.ACTION_WRITE, bucket, object) { errCode = s3err.ErrAccessDenied return } diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index c3e50ec44..3233f4006 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -55,7 +55,7 @@ func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Reques var buckets []*s3.Bucket for _, entry := range entries { if entry.IsDirectory { - if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name) { + if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name, "") { continue } buckets = append(buckets, &s3.Bucket{