mirror of
https://github.com/minio/minio.git
synced 2025-03-30 17:23:42 -04:00
iam: Hot load of the policy during request authorization (#20007)
Hot load a policy document when during account authorization evaluation to avoid returning 403 during server startup, when not all policies are already loaded. Add this support for group policies as well.
This commit is contained in:
parent
709612cb37
commit
722118386d
@ -457,6 +457,8 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
|||||||
|
|
||||||
bootstrapTraceMsgFirstTime("loading all IAM items")
|
bootstrapTraceMsgFirstTime("loading all IAM items")
|
||||||
|
|
||||||
|
setDefaultCannedPolicies(cache.iamPolicyDocsMap)
|
||||||
|
|
||||||
listStartTime := UTCNow()
|
listStartTime := UTCNow()
|
||||||
listedConfigItems, err := iamOS.listAllIAMConfigItems(ctx)
|
listedConfigItems, err := iamOS.listAllIAMConfigItems(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -485,7 +487,6 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
|||||||
if took := time.Since(policyLoadStartTime); took > maxIAMLoadOpTime {
|
if took := time.Since(policyLoadStartTime); took > maxIAMLoadOpTime {
|
||||||
logger.Info("Policy docs load took %.2fs (for %d items)", took.Seconds(), len(policiesList))
|
logger.Info("Policy docs load took %.2fs (for %d items)", took.Seconds(), len(policiesList))
|
||||||
}
|
}
|
||||||
setDefaultCannedPolicies(cache.iamPolicyDocsMap)
|
|
||||||
|
|
||||||
if iamOS.usersSysType == MinIOUsersSysType {
|
if iamOS.usersSysType == MinIOUsersSysType {
|
||||||
bootstrapTraceMsgFirstTime("loading regular IAM users")
|
bootstrapTraceMsgFirstTime("loading regular IAM users")
|
||||||
|
126
cmd/iam-store.go
126
cmd/iam-store.go
@ -431,8 +431,41 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returned policy could be empty
|
// returned policy could be empty, we use set to de-duplicate.
|
||||||
policies := mp.toSlice()
|
policies := set.CreateStringSet(mp.toSlice()...)
|
||||||
|
|
||||||
|
for _, group := range u.Credentials.Groups {
|
||||||
|
if store.getUsersSysType() == MinIOUsersSysType {
|
||||||
|
g, ok := c.iamGroupsMap[group]
|
||||||
|
if !ok {
|
||||||
|
if err := store.loadGroup(context.Background(), group, c.iamGroupsMap); err != nil {
|
||||||
|
return nil, time.Time{}, err
|
||||||
|
}
|
||||||
|
g, ok = c.iamGroupsMap[group]
|
||||||
|
if !ok {
|
||||||
|
return nil, time.Time{}, errNoSuchGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group is disabled, so we return no policy - this
|
||||||
|
// ensures the request is denied.
|
||||||
|
if g.Status == statusDisabled {
|
||||||
|
return nil, time.Time{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, ok := c.iamGroupPolicyMap.Load(group)
|
||||||
|
if !ok {
|
||||||
|
if err := store.loadMappedPolicyWithRetry(context.TODO(), group, regUser, true, c.iamGroupPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||||
|
return nil, time.Time{}, err
|
||||||
|
}
|
||||||
|
policy, _ = c.iamGroupPolicyMap.Load(group)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range policy.toSlice() {
|
||||||
|
policies.Add(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, group := range c.iamUserGroupMemberships[name].ToSlice() {
|
for _, group := range c.iamUserGroupMemberships[name].ToSlice() {
|
||||||
if store.getUsersSysType() == MinIOUsersSysType {
|
if store.getUsersSysType() == MinIOUsersSysType {
|
||||||
@ -462,10 +495,12 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
|
|||||||
policy, _ = c.iamGroupPolicyMap.Load(group)
|
policy, _ = c.iamGroupPolicyMap.Load(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
policies = append(policies, policy.toSlice()...)
|
for _, p := range policy.toSlice() {
|
||||||
|
policies.Add(p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return policies, mp.UpdatedAt, nil
|
return policies.ToSlice(), mp.UpdatedAt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *iamCache) updateUserWithClaims(key string, u UserIdentity) error {
|
func (c *iamCache) updateUserWithClaims(key string, u UserIdentity) error {
|
||||||
@ -937,12 +972,7 @@ func (store *IAMStoreSys) GetGroupDescription(group string) (gd madmin.GroupDesc
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListGroups - lists groups. Since this is not going to be a frequent
|
func (store *IAMStoreSys) updateGroups(ctx context.Context, cache *iamCache) (res []string, err error) {
|
||||||
// operation, we fetch this info from storage, and refresh the cache as well.
|
|
||||||
func (store *IAMStoreSys) ListGroups(ctx context.Context) (res []string, err error) {
|
|
||||||
cache := store.lock()
|
|
||||||
defer store.unlock()
|
|
||||||
|
|
||||||
if store.getUsersSysType() == MinIOUsersSysType {
|
if store.getUsersSysType() == MinIOUsersSysType {
|
||||||
m := map[string]GroupInfo{}
|
m := map[string]GroupInfo{}
|
||||||
err = store.loadGroups(ctx, m)
|
err = store.loadGroups(ctx, m)
|
||||||
@ -970,7 +1000,16 @@ func (store *IAMStoreSys) ListGroups(ctx context.Context) (res []string, err err
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGroups - lists groups. Since this is not going to be a frequent
|
||||||
|
// operation, we fetch this info from storage, and refresh the cache as well.
|
||||||
|
func (store *IAMStoreSys) ListGroups(ctx context.Context) (res []string, err error) {
|
||||||
|
cache := store.lock()
|
||||||
|
defer store.unlock()
|
||||||
|
|
||||||
|
return store.updateGroups(ctx, cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
// listGroups - lists groups - fetch groups from cache
|
// listGroups - lists groups - fetch groups from cache
|
||||||
@ -1445,16 +1484,51 @@ func filterPolicies(cache *iamCache, policyName string, bucketName string) (stri
|
|||||||
return strings.Join(policies, ","), policy.MergePolicies(toMerge...)
|
return strings.Join(policies, ","), policy.MergePolicies(toMerge...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterPolicies - accepts a comma separated list of policy names as a string
|
// MergePolicies - accepts a comma separated list of policy names as a string
|
||||||
// and bucket and returns only policies that currently exist in MinIO. If
|
// and returns only policies that currently exist in MinIO. It includes hot loading
|
||||||
// bucketName is non-empty, additionally filters policies matching the bucket.
|
// of policies if not in the memory
|
||||||
// The first returned value is the list of currently existing policies, and the
|
func (store *IAMStoreSys) MergePolicies(policyName string) (string, policy.Policy) {
|
||||||
// second is their combined policy definition.
|
var policies []string
|
||||||
func (store *IAMStoreSys) FilterPolicies(policyName string, bucketName string) (string, policy.Policy) {
|
var missingPolicies []string
|
||||||
cache := store.rlock()
|
var toMerge []policy.Policy
|
||||||
defer store.runlock()
|
|
||||||
|
|
||||||
return filterPolicies(cache, policyName, bucketName)
|
cache := store.rlock()
|
||||||
|
for _, policy := range newMappedPolicy(policyName).toSlice() {
|
||||||
|
if policy == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p, found := cache.iamPolicyDocsMap[policy]
|
||||||
|
if !found {
|
||||||
|
missingPolicies = append(missingPolicies, policy)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
policies = append(policies, policy)
|
||||||
|
toMerge = append(toMerge, p.Policy)
|
||||||
|
}
|
||||||
|
store.runlock()
|
||||||
|
|
||||||
|
if len(missingPolicies) > 0 {
|
||||||
|
m := make(map[string]PolicyDoc)
|
||||||
|
for _, policy := range missingPolicies {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
_ = store.loadPolicyDoc(ctx, policy, m)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
cache := store.lock()
|
||||||
|
for policy, p := range m {
|
||||||
|
cache.iamPolicyDocsMap[policy] = p
|
||||||
|
}
|
||||||
|
store.unlock()
|
||||||
|
|
||||||
|
for policy, p := range m {
|
||||||
|
policies = append(policies, policy)
|
||||||
|
toMerge = append(toMerge, p.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(policies, ","), policy.MergePolicies(toMerge...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBucketUsers - returns users (not STS or service accounts) that have access
|
// GetBucketUsers - returns users (not STS or service accounts) that have access
|
||||||
@ -2675,6 +2749,18 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load := len(cache.iamGroupsMap) == 0
|
||||||
|
if store.getUsersSysType() == LDAPUsersSysType && cache.iamGroupPolicyMap.Size() == 0 {
|
||||||
|
load = true
|
||||||
|
}
|
||||||
|
if load {
|
||||||
|
if _, err = store.updateGroups(ctx, cache); err != nil {
|
||||||
|
return "done", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.buildUserGroupMemberships()
|
||||||
|
|
||||||
return "done", err
|
return "done", err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -436,7 +436,7 @@ func (sys *IAMSys) validateAndAddRolePolicyMappings(ctx context.Context, m map[a
|
|||||||
// running server by creating the policies after start up.
|
// running server by creating the policies after start up.
|
||||||
for arn, rolePolicies := range m {
|
for arn, rolePolicies := range m {
|
||||||
specifiedPoliciesSet := newMappedPolicy(rolePolicies).policySet()
|
specifiedPoliciesSet := newMappedPolicy(rolePolicies).policySet()
|
||||||
validPolicies, _ := sys.store.FilterPolicies(rolePolicies, "")
|
validPolicies, _ := sys.store.MergePolicies(rolePolicies)
|
||||||
knownPoliciesSet := newMappedPolicy(validPolicies).policySet()
|
knownPoliciesSet := newMappedPolicy(validPolicies).policySet()
|
||||||
unknownPoliciesSet := specifiedPoliciesSet.Difference(knownPoliciesSet)
|
unknownPoliciesSet := specifiedPoliciesSet.Difference(knownPoliciesSet)
|
||||||
if len(unknownPoliciesSet) > 0 {
|
if len(unknownPoliciesSet) > 0 {
|
||||||
@ -672,7 +672,7 @@ func (sys *IAMSys) CurrentPolicies(policyName string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
policies, _ := sys.store.FilterPolicies(policyName, "")
|
policies, _ := sys.store.MergePolicies(policyName)
|
||||||
return policies
|
return policies
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2122,7 +2122,7 @@ func (sys *IAMSys) IsAllowedServiceAccount(args policy.Args, parentUser string)
|
|||||||
var combinedPolicy policy.Policy
|
var combinedPolicy policy.Policy
|
||||||
// Policies were found, evaluate all of them.
|
// Policies were found, evaluate all of them.
|
||||||
if !isOwnerDerived {
|
if !isOwnerDerived {
|
||||||
availablePoliciesStr, c := sys.store.FilterPolicies(strings.Join(svcPolicies, ","), "")
|
availablePoliciesStr, c := sys.store.MergePolicies(strings.Join(svcPolicies, ","))
|
||||||
if availablePoliciesStr == "" {
|
if availablePoliciesStr == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -2350,7 +2350,7 @@ func isAllowedBySessionPolicy(args policy.Args) (hasSessionPolicy bool, isAllowe
|
|||||||
|
|
||||||
// GetCombinedPolicy returns a combined policy combining all policies
|
// GetCombinedPolicy returns a combined policy combining all policies
|
||||||
func (sys *IAMSys) GetCombinedPolicy(policies ...string) policy.Policy {
|
func (sys *IAMSys) GetCombinedPolicy(policies ...string) policy.Policy {
|
||||||
_, policy := sys.store.FilterPolicies(strings.Join(policies, ","), "")
|
_, policy := sys.store.MergePolicies(strings.Join(policies, ","))
|
||||||
return policy
|
return policy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user