Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/run-mint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ docker volume rm $(docker volume ls -f dangling=true) || true
## change working directory
cd .github/workflows/mint

## always pull latest
docker pull docker.io/minio/mint:edge

docker-compose -f minio-${MODE}.yaml up -d
sleep 1m

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[![MinIO](https://raw.githubusercontent.com/minio/minio/master/.github/logo.svg?sanitize=true)](https://min.io)

MinIO is a High Performance Object Storage released under GNU Affero General Public License v3.0. It is API compatible with Amazon S3 cloud storage service. Use MinIO to build high performance infrastructure for machine learning, analytics and application data workloads.
MinIO is a High Performance Object Storage released under GNU Affero General Public License v3.0. It is API compatible with Amazon S3 cloud storage service. Use MinIO to build high performance infrastructure for machine learning, analytics and application data workloads. To learn more about what MinIO is doing for AI storage, go to [AI storage documentation](https://min.io/solutions/object-storage-for-ai).

This README provides quickstart instructions on running MinIO on bare metal hardware, including container-based installations. For Kubernetes environments, use the [MinIO Kubernetes Operator](https://github.com/minio/operator/blob/master/README.md).

Expand Down
98 changes: 87 additions & 11 deletions cmd/iam-store.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"path"
"sort"
"strings"
"sync"
"time"

jsoniter "github.com/json-iterator/go"
Expand All @@ -37,6 +38,7 @@ import (
"github.com/minio/minio/internal/jwt"
"github.com/minio/pkg/v3/env"
"github.com/minio/pkg/v3/policy"
"github.com/minio/pkg/v3/sync/errgroup"
"github.com/puzpuzpuz/xsync/v3"
"golang.org/x/sync/singleflight"
)
Expand Down Expand Up @@ -357,6 +359,68 @@ func (c *iamCache) removeGroupFromMembershipsMap(group string) {
}
}

func (c *iamCache) policyDBGetGroups(store *IAMStoreSys, userPolicyPresent bool, groups ...string) ([]string, error) {
var policies []string
for _, group := range groups {
if store.getUsersSysType() == MinIOUsersSysType {
g, ok := c.iamGroupsMap[group]
if !ok {
continue
}

// Group is disabled, so we return no policy - this
// ensures the request is denied.
if g.Status == statusDisabled {
continue
}
}

policy, ok := c.iamGroupPolicyMap.Load(group)
if !ok {
continue
}

policies = append(policies, policy.toSlice()...)
}

found := len(policies) > 0
if found {
return policies, nil
}

if userPolicyPresent {
// if user mapping present and no group policies found
// rely on user policy for access, instead of fallback.
return nil, nil
}

var mu sync.Mutex

// no mappings found, fallback for all groups.
g := errgroup.WithNErrs(len(groups)).WithConcurrency(10) // load like 10 groups at a time.

for index := range groups {
index := index
g.Go(func() error {
err := store.loadMappedPolicy(context.TODO(), groups[index], regUser, true, c.iamGroupPolicyMap)
if err != nil && !errors.Is(err, errNoSuchPolicy) {
return err
}
if errors.Is(err, errNoSuchPolicy) {
return nil
}
policy, _ := c.iamGroupPolicyMap.Load(groups[index])
mu.Lock()
policies = append(policies, policy.toSlice()...)
mu.Unlock()
return nil
}, index)
}

err := errors.Join(g.Wait()...)
return policies, err
}

// policyDBGet - lower-level helper; does not take locks.
//
// If a group is passed, it returns policies associated with the group.
Expand Down Expand Up @@ -676,7 +740,8 @@ func (store *IAMStoreSys) LoadIAMCache(ctx context.Context, firstTime bool) erro
type IAMStoreSys struct {
IAMStorageAPI

group *singleflight.Group
group *singleflight.Group
policy *singleflight.Group
}

// HasWatcher - returns if the storage system has a watcher.
Expand Down Expand Up @@ -757,21 +822,32 @@ func (store *IAMStoreSys) PolicyDBGet(name string, groups ...string) ([]string,
cache := store.rlock()
defer store.runlock()

policies, _, err := cache.policyDBGet(store, name, false, false)
if err != nil {
return nil, err
}
getPolicies := func() ([]string, error) {
policies, _, err := cache.policyDBGet(store, name, false, false)
if err != nil {
return nil, err
}

userPolicyPresent := len(policies) > 0
for _, group := range groups {
ps, _, err := cache.policyDBGet(store, group, true, userPolicyPresent)
userPolicyPresent := len(policies) > 0

groupPolicies, err := cache.policyDBGetGroups(store, userPolicyPresent, groups...)
if err != nil {
return nil, err
}
policies = append(policies, ps...)
}

return policies, nil
policies = append(policies, groupPolicies...)
return policies, nil
}
if store.policy != nil {
val, err, _ := store.policy.Do(name, func() (interface{}, error) {
return getPolicies()
})
if err != nil {
return nil, err
}
return val.([]string), nil
}
return getPolicies()
}

// AddUsersToGroup - adds users to group, creating the group if needed.
Expand Down
7 changes: 6 additions & 1 deletion cmd/iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,18 @@ func (sys *IAMSys) initStore(objAPI ObjectLayer, etcdClient *etcd.Client) {
}

if etcdClient == nil {
var group *singleflight.Group
var (
group *singleflight.Group
policy *singleflight.Group
)
if env.Get("_MINIO_IAM_SINGLE_FLIGHT", config.EnableOn) == config.EnableOn {
group = &singleflight.Group{}
policy = &singleflight.Group{}
}
sys.store = &IAMStoreSys{
IAMStorageAPI: newIAMObjectStore(objAPI, sys.usersSysType),
group: group,
policy: policy,
}
} else {
sys.store = &IAMStoreSys{IAMStorageAPI: newIAMEtcdStore(etcdClient, sys.usersSysType)}
Expand Down
20 changes: 12 additions & 8 deletions cmd/post-policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ func newPostPolicyBytesV4WithContentRange(credential, bucketName, objectKey stri
credentialConditionStr := fmt.Sprintf(`["eq", "$x-amz-credential", "%s"]`, credential)
// Add the meta-uuid string, set to 1234
uuidConditionStr := fmt.Sprintf(`["eq", "$x-amz-meta-uuid", "%s"]`, "1234")
// Add the content-encoding string, set to gzip.
contentEncodingConditionStr := fmt.Sprintf(`["eq", "$content-encoding", "%s"]`, "gzip")

// Combine all conditions into one string.
conditionStr := fmt.Sprintf(`"conditions":[%s, %s, %s, %s, %s, %s, %s]`, bucketConditionStr,
keyConditionStr, contentLengthCondStr, algorithmConditionStr, dateConditionStr, credentialConditionStr, uuidConditionStr)
conditionStr := fmt.Sprintf(`"conditions":[%s, %s, %s, %s, %s, %s, %s, %s]`, bucketConditionStr,
keyConditionStr, contentLengthCondStr, algorithmConditionStr, dateConditionStr, credentialConditionStr, uuidConditionStr, contentEncodingConditionStr)
retStr := "{"
retStr = retStr + expirationStr + ","
retStr += conditionStr
Expand All @@ -85,9 +87,11 @@ func newPostPolicyBytesV4(credential, bucketName, objectKey string, expiration t
credentialConditionStr := fmt.Sprintf(`["eq", "$x-amz-credential", "%s"]`, credential)
// Add the meta-uuid string, set to 1234
uuidConditionStr := fmt.Sprintf(`["eq", "$x-amz-meta-uuid", "%s"]`, "1234")
// Add the content-encoding string, set to gzip
contentEncodingConditionStr := fmt.Sprintf(`["eq", "$content-encoding", "%s"]`, "gzip")

// Combine all conditions into one string.
conditionStr := fmt.Sprintf(`"conditions":[%s, %s, %s, %s, %s, %s]`, bucketConditionStr, keyConditionStr, algorithmConditionStr, dateConditionStr, credentialConditionStr, uuidConditionStr)
conditionStr := fmt.Sprintf(`"conditions":[%s, %s, %s, %s, %s, %s, %s]`, bucketConditionStr, keyConditionStr, algorithmConditionStr, dateConditionStr, credentialConditionStr, uuidConditionStr, contentEncodingConditionStr)
retStr := "{"
retStr = retStr + expirationStr + ","
retStr += conditionStr
Expand Down Expand Up @@ -331,7 +335,7 @@ func testPostPolicyBucketHandler(obj ObjectLayer, instanceType string, t TestErr
accessKey: credentials.AccessKey,
secretKey: credentials.SecretKey,
dates: []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)},
policy: `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"]]}`,
policy: `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"],["eq", "$content-encoding", "gzip"]]}`,
},
// Success case, no multipart filename.
{
Expand All @@ -341,7 +345,7 @@ func testPostPolicyBucketHandler(obj ObjectLayer, instanceType string, t TestErr
accessKey: credentials.AccessKey,
secretKey: credentials.SecretKey,
dates: []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)},
policy: `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"]]}`,
policy: `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"],["eq", "$content-encoding", "gzip"]]}`,
noFilename: true,
},
// Success case, big body.
Expand All @@ -352,7 +356,7 @@ func testPostPolicyBucketHandler(obj ObjectLayer, instanceType string, t TestErr
accessKey: credentials.AccessKey,
secretKey: credentials.SecretKey,
dates: []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)},
policy: `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"]]}`,
policy: `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], ["starts-with", "$key", "test/"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$x-amz-meta-uuid", "1234"],["eq", "$content-encoding", "gzip"]]}`,
},
// Corrupted Base 64 result
{
Expand Down Expand Up @@ -447,7 +451,7 @@ func testPostPolicyBucketHandler(obj ObjectLayer, instanceType string, t TestErr
malformedBody: false,
ignoreContentLength: false,
},
// Failed with Content-Length not specified.
// Success with Content-Length not specified.
{
objectName: "test",
data: bytes.Repeat([]byte("a"), 1025),
Expand Down Expand Up @@ -547,7 +551,7 @@ func testPostPolicyBucketHandlerRedirect(obj ObjectLayer, instanceType string, t
rec := httptest.NewRecorder()

dates := []interface{}{curTimePlus5Min.Format(iso8601TimeFormat), curTime.Format(iso8601DateFormat), curTime.Format(yyyymmdd)}
policy := `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], {"success_action_redirect":"` + redirectURL.String() + `"},["starts-with", "$key", "test/"], ["eq", "$x-amz-meta-uuid", "1234"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"]]}`
policy := `{"expiration": "%s","conditions":[["eq", "$bucket", "` + bucketName + `"], {"success_action_redirect":"` + redirectURL.String() + `"},["starts-with", "$key", "test/"], ["eq", "$x-amz-meta-uuid", "1234"], ["eq", "$x-amz-algorithm", "AWS4-HMAC-SHA256"], ["eq", "$x-amz-date", "%s"], ["eq", "$x-amz-credential", "` + credentials.AccessKey + `/%s/us-east-1/s3/aws4_request"],["eq", "$content-encoding", "gzip"]]}`

// Generate the final policy document
policy = fmt.Sprintf(policy, dates...)
Expand Down
Loading
Loading