mirror of
https://github.com/minio/minio.git
synced 2024-12-25 06:35:56 -05:00
Remove internal usage of consoleAdmin (#15402)
"consoleAdmin" was used as the policy for root derived accounts, but this lead to unexpected bugs when an administrator modified the consoleAdmin policy This change avoids evaluating a policy for root derived accounts as by default no policy is mapped to the root user. If a session policy is attached to a root derived account, it will be evaluated as expected.
This commit is contained in:
parent
906947a285
commit
7e4e7a66af
@ -337,33 +337,16 @@ func (c *iamCache) policyDBGet(mode UsersSysType, name string, isGroup bool) ([]
|
|||||||
return c.iamGroupPolicyMap[name].toSlice(), c.iamGroupPolicyMap[name].UpdatedAt, nil
|
return c.iamGroupPolicyMap[name].toSlice(), c.iamGroupPolicyMap[name].UpdatedAt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == globalActiveCred.AccessKey {
|
|
||||||
return []string{"consoleAdmin"}, time.Time{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// When looking for a user's policies, we also check if the user
|
// When looking for a user's policies, we also check if the user
|
||||||
// and the groups they are member of are enabled.
|
// and the groups they are member of are enabled.
|
||||||
var parentName string
|
|
||||||
u, ok := c.iamUsersMap[name]
|
u, ok := c.iamUsersMap[name]
|
||||||
if ok {
|
if ok {
|
||||||
if !u.Credentials.IsValid() {
|
if !u.Credentials.IsValid() {
|
||||||
return nil, time.Time{}, nil
|
return nil, time.Time{}, nil
|
||||||
}
|
}
|
||||||
parentName = u.Credentials.ParentUser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mp, ok := c.iamUserPolicyMap[name]
|
mp := c.iamUserPolicyMap[name]
|
||||||
if !ok {
|
|
||||||
// Service accounts with root credentials, inherit parent permissions
|
|
||||||
if parentName == globalActiveCred.AccessKey && u.Credentials.IsServiceAccount() {
|
|
||||||
// even if this is set, the claims present in the service
|
|
||||||
// accounts apply the final permissions if any.
|
|
||||||
return []string{"consoleAdmin"}, mp.UpdatedAt, nil
|
|
||||||
}
|
|
||||||
if parentName != "" {
|
|
||||||
mp = c.iamUserPolicyMap[parentName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returned policy could be empty
|
// returned policy could be empty
|
||||||
policies := mp.toSlice()
|
policies := mp.toSlice()
|
||||||
|
131
cmd/iam.go
131
cmd/iam.go
@ -1458,7 +1458,7 @@ const sessionPolicyNameExtracted = iampolicy.SessionPolicyName + "-extracted"
|
|||||||
// IsAllowedServiceAccount - checks if the given service account is allowed to perform
|
// IsAllowedServiceAccount - checks if the given service account is allowed to perform
|
||||||
// actions. The permission of the parent user is checked first
|
// actions. The permission of the parent user is checked first
|
||||||
func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parentUser string) bool {
|
func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parentUser string) bool {
|
||||||
// Now check if we have a subject claim
|
// Verify if the parent claim matches the parentUser.
|
||||||
p, ok := args.Claims[parentClaim]
|
p, ok := args.Claims[parentClaim]
|
||||||
if ok {
|
if ok {
|
||||||
parentInClaim, ok := p.(string)
|
parentInClaim, ok := p.(string)
|
||||||
@ -1478,41 +1478,56 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parentUser strin
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check policy for parent user of service account.
|
isOwnerDerived := parentUser == globalActiveCred.AccessKey
|
||||||
svcPolicies, err := sys.PolicyDBGet(parentUser, false, args.Groups...)
|
|
||||||
if err != nil {
|
|
||||||
logger.LogIf(GlobalContext, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(svcPolicies) == 0 {
|
var err error
|
||||||
// If parent user has no policies, check for OpenID
|
var svcPolicies []string
|
||||||
// claims/RoleARN in case it exists.
|
roleArn := args.GetRoleArn()
|
||||||
roleArn := args.GetRoleArn()
|
|
||||||
if roleArn != "" {
|
switch {
|
||||||
arn, err := arn.Parse(roleArn)
|
case isOwnerDerived:
|
||||||
if err != nil {
|
// All actions are allowed by default and no policy evaluation is
|
||||||
logger.LogIf(GlobalContext, fmt.Errorf("error parsing role ARN %s: %v", roleArn, err))
|
// required.
|
||||||
return false
|
|
||||||
}
|
case roleArn != "":
|
||||||
svcPolicies = newMappedPolicy(sys.rolesMap[arn]).toSlice()
|
arn, err := arn.Parse(roleArn)
|
||||||
} else {
|
if err != nil {
|
||||||
// If there is no roleArn claim, check the OpenID
|
logger.LogIf(GlobalContext, fmt.Errorf("error parsing role ARN %s: %v", roleArn, err))
|
||||||
// provider's policy claim.
|
return false
|
||||||
|
}
|
||||||
|
svcPolicies = newMappedPolicy(sys.rolesMap[arn]).toSlice()
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Check policy for parent user of service account.
|
||||||
|
svcPolicies, err = sys.PolicyDBGet(parentUser, false, args.Groups...)
|
||||||
|
if err != nil {
|
||||||
|
logger.LogIf(GlobalContext, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, if there is no parent policy, check if a policy claim is
|
||||||
|
// present.
|
||||||
|
if len(svcPolicies) == 0 {
|
||||||
policySet, _ := iampolicy.GetPoliciesFromClaims(args.Claims, iamPolicyClaimNameOpenID())
|
policySet, _ := iampolicy.GetPoliciesFromClaims(args.Claims, iamPolicyClaimNameOpenID())
|
||||||
svcPolicies = policySet.ToSlice()
|
svcPolicies = policySet.ToSlice()
|
||||||
}
|
}
|
||||||
if len(svcPolicies) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Policies were found, evaluate all of them.
|
// Defensive code: Do not allow any operation if no policy is found.
|
||||||
availablePoliciesStr, combinedPolicy := sys.store.FilterPolicies(strings.Join(svcPolicies, ","), "")
|
if !isOwnerDerived && len(svcPolicies) == 0 {
|
||||||
if availablePoliciesStr == "" {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var combinedPolicy iampolicy.Policy
|
||||||
|
// Policies were found, evaluate all of them.
|
||||||
|
if !isOwnerDerived {
|
||||||
|
availablePoliciesStr, c := sys.store.FilterPolicies(strings.Join(svcPolicies, ","), "")
|
||||||
|
if availablePoliciesStr == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
combinedPolicy = c
|
||||||
|
}
|
||||||
|
|
||||||
parentArgs := args
|
parentArgs := args
|
||||||
parentArgs.AccountName = parentUser
|
parentArgs.AccountName = parentUser
|
||||||
// These are dynamic values set them appropriately.
|
// These are dynamic values set them appropriately.
|
||||||
@ -1532,7 +1547,7 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parentUser strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if saPolicyClaimStr == inheritedPolicyType {
|
if saPolicyClaimStr == inheritedPolicyType {
|
||||||
return combinedPolicy.IsAllowed(parentArgs)
|
return isOwnerDerived || combinedPolicy.IsAllowed(parentArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now check if we have a sessionPolicy.
|
// Now check if we have a sessionPolicy.
|
||||||
@ -1558,37 +1573,51 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parentUser strin
|
|||||||
|
|
||||||
// This can only happen if policy was set but with an empty JSON.
|
// This can only happen if policy was set but with an empty JSON.
|
||||||
if subPolicy.Version == "" && len(subPolicy.Statements) == 0 {
|
if subPolicy.Version == "" && len(subPolicy.Statements) == 0 {
|
||||||
return combinedPolicy.IsAllowed(parentArgs)
|
return isOwnerDerived || combinedPolicy.IsAllowed(parentArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if subPolicy.Version == "" {
|
if subPolicy.Version == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return combinedPolicy.IsAllowed(parentArgs) && subPolicy.IsAllowed(parentArgs)
|
return subPolicy.IsAllowed(parentArgs) && (isOwnerDerived || combinedPolicy.IsAllowed(parentArgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAllowedSTS is meant for STS based temporary credentials,
|
// IsAllowedSTS is meant for STS based temporary credentials,
|
||||||
// which implements claims validation and verification other than
|
// which implements claims validation and verification other than
|
||||||
// applying policies.
|
// applying policies.
|
||||||
func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool {
|
func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool {
|
||||||
|
// 1. Determine mapped policies
|
||||||
|
|
||||||
|
isOwnerDerived := parentUser == globalActiveCred.AccessKey
|
||||||
var policies []string
|
var policies []string
|
||||||
roleArn := args.GetRoleArn()
|
roleArn := args.GetRoleArn()
|
||||||
if roleArn != "" {
|
|
||||||
|
switch {
|
||||||
|
case isOwnerDerived:
|
||||||
|
// All actions are allowed by default and no policy evaluation is
|
||||||
|
// required.
|
||||||
|
|
||||||
|
case roleArn != "":
|
||||||
|
// If a roleARN is present, the role policy is applied.
|
||||||
arn, err := arn.Parse(roleArn)
|
arn, err := arn.Parse(roleArn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogIf(GlobalContext, fmt.Errorf("error parsing role ARN %s: %v", roleArn, err))
|
logger.LogIf(GlobalContext, fmt.Errorf("error parsing role ARN %s: %v", roleArn, err))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
policies = newMappedPolicy(sys.rolesMap[arn]).toSlice()
|
policies = newMappedPolicy(sys.rolesMap[arn]).toSlice()
|
||||||
} else {
|
|
||||||
// Lookup the parent user's mapping if there's no role-ARN.
|
default:
|
||||||
|
// Otherwise, inherit parent user's policy
|
||||||
var err error
|
var err error
|
||||||
policies, err = sys.store.PolicyDBGet(parentUser, false, args.Groups...)
|
policies, err = sys.store.PolicyDBGet(parentUser, false, args.Groups...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogIf(GlobalContext, fmt.Errorf("error fetching policies on %s: %v", parentUser, err))
|
logger.LogIf(GlobalContext, fmt.Errorf("error fetching policies on %s: %v", parentUser, err))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finally, if there is no parent policy, check if a policy claim is
|
||||||
|
// present in the session token.
|
||||||
if len(policies) == 0 {
|
if len(policies) == 0 {
|
||||||
// If there is no parent policy mapping, we fall back to
|
// If there is no parent policy mapping, we fall back to
|
||||||
// using policy claim from JWT.
|
// using policy claim from JWT.
|
||||||
@ -1599,27 +1628,37 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool {
|
|||||||
}
|
}
|
||||||
policies = policySet.ToSlice()
|
policies = policySet.ToSlice()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defensive code: Do not allow any operation if no policy is found in the session token
|
// Defensive code: Do not allow any operation if no policy is found in the session token
|
||||||
if len(policies) == 0 {
|
if !isOwnerDerived && len(policies) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
combinedPolicy, err := sys.store.GetPolicy(strings.Join(policies, ","))
|
// 2. Combine the mapped policies into a single combined policy.
|
||||||
if err == errNoSuchPolicy {
|
|
||||||
for _, pname := range policies {
|
var combinedPolicy iampolicy.Policy
|
||||||
_, err := sys.store.GetPolicy(pname)
|
if !isOwnerDerived {
|
||||||
if err == errNoSuchPolicy {
|
var err error
|
||||||
// all policies presented in the claim should exist
|
combinedPolicy, err = sys.store.GetPolicy(strings.Join(policies, ","))
|
||||||
logger.LogIf(GlobalContext, fmt.Errorf("expected policy (%s) missing from the JWT claim %s, rejecting the request", pname, iamPolicyClaimNameOpenID()))
|
if err == errNoSuchPolicy {
|
||||||
return false
|
for _, pname := range policies {
|
||||||
|
_, err := sys.store.GetPolicy(pname)
|
||||||
|
if err == errNoSuchPolicy {
|
||||||
|
// all policies presented in the claim should exist
|
||||||
|
logger.LogIf(GlobalContext, fmt.Errorf("expected policy (%s) missing from the JWT claim %s, rejecting the request", pname, iamPolicyClaimNameOpenID()))
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
logger.LogIf(GlobalContext, fmt.Errorf("all policies were unexpectedly present!"))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
logger.LogIf(GlobalContext, fmt.Errorf("all policies were unexpectedly present!"))
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. If an inline session-policy is present, evaluate it.
|
||||||
|
|
||||||
// These are dynamic values set them appropriately.
|
// These are dynamic values set them appropriately.
|
||||||
args.ConditionValues["username"] = []string{parentUser}
|
args.ConditionValues["username"] = []string{parentUser}
|
||||||
args.ConditionValues["userid"] = []string{parentUser}
|
args.ConditionValues["userid"] = []string{parentUser}
|
||||||
@ -1627,12 +1666,12 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool {
|
|||||||
// Now check if we have a sessionPolicy.
|
// Now check if we have a sessionPolicy.
|
||||||
hasSessionPolicy, isAllowedSP := isAllowedBySessionPolicy(args)
|
hasSessionPolicy, isAllowedSP := isAllowedBySessionPolicy(args)
|
||||||
if hasSessionPolicy {
|
if hasSessionPolicy {
|
||||||
return isAllowedSP && combinedPolicy.IsAllowed(args)
|
return isAllowedSP && (isOwnerDerived || combinedPolicy.IsAllowed(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sub policy not set, this is most common since subPolicy
|
// Sub policy not set, this is most common since subPolicy
|
||||||
// is optional, use the inherited policies.
|
// is optional, use the inherited policies.
|
||||||
return combinedPolicy.IsAllowed(args)
|
return isOwnerDerived || combinedPolicy.IsAllowed(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAllowedBySessionPolicy(args iampolicy.Args) (hasSessionPolicy bool, isAllowed bool) {
|
func isAllowedBySessionPolicy(args iampolicy.Args) (hasSessionPolicy bool, isAllowed bool) {
|
||||||
|
Loading…
Reference in New Issue
Block a user