fix: remove embedded-policy as requested by the user (#14847)

this PR introduces a few changes such as

- sessionPolicyName is not reused in an extracted manner
  to apply policies for incoming authenticated calls,
  instead uses a different key to designate this
  information for the callers.

- this differentiation is needed to ensure that service
  account updates do not accidentally store JSON representation
  instead of base64 equivalent on the disk.

- relax requirements for Deleting a service account, allow
  deleting a service account that might be unreadable, i.e
  a situation where the user might have removed session policy 
  which now carries a JSON representation, making it unparsable.

- introduce some constants to reuse instead of strings.

fixes #14784
This commit is contained in:
Harshavardhana 2022-05-02 17:56:19 -07:00 committed by GitHub
parent c59d2a6288
commit f0462322fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 20 deletions

View File

@ -1014,11 +1014,9 @@ func (a adminAPIHandlers) DeleteServiceAccount(w http.ResponseWriter, r *http.Re
return return
} }
svcAccount, _, err := globalIAMSys.GetServiceAccount(ctx, serviceAccount) // We do not care if service account is readable or not at this point,
if err != nil { // since this is a delete call we shall allow it to be deleted if possible.
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) svcAccount, _, _ := globalIAMSys.GetServiceAccount(ctx, serviceAccount)
return
}
adminPrivilege := globalIAMSys.IsAllowed(iampolicy.Args{ adminPrivilege := globalIAMSys.IsAllowed(iampolicy.Args{
AccountName: cred.AccessKey, AccountName: cred.AccessKey,
@ -1033,7 +1031,7 @@ func (a adminAPIHandlers) DeleteServiceAccount(w http.ResponseWriter, r *http.Re
if cred.ParentUser != "" { if cred.ParentUser != "" {
parentUser = cred.ParentUser parentUser = cred.ParentUser
} }
if parentUser != svcAccount.ParentUser { if svcAccount.ParentUser != "" && parentUser != svcAccount.ParentUser {
// The service account belongs to another user but return not // The service account belongs to another user but return not
// found error to mitigate brute force attacks. or the // found error to mitigate brute force attacks. or the
// serviceAccount doesn't exist. // serviceAccount doesn't exist.
@ -1042,23 +1040,21 @@ func (a adminAPIHandlers) DeleteServiceAccount(w http.ResponseWriter, r *http.Re
} }
} }
err = globalIAMSys.DeleteServiceAccount(ctx, serviceAccount, true) if err := globalIAMSys.DeleteServiceAccount(ctx, serviceAccount, true); err != nil {
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return return
} }
// Call site replication hook - non-root user accounts are replicated. // Call site replication hook - non-root user accounts are replicated.
if svcAccount.ParentUser != globalActiveCred.AccessKey { if svcAccount.ParentUser != "" && svcAccount.ParentUser != globalActiveCred.AccessKey {
err = globalSiteReplicationSys.IAMChangeHook(ctx, madmin.SRIAMItem{ if err := globalSiteReplicationSys.IAMChangeHook(ctx, madmin.SRIAMItem{
Type: madmin.SRIAMItemSvcAcc, Type: madmin.SRIAMItemSvcAcc,
SvcAccChange: &madmin.SRSvcAccChange{ SvcAccChange: &madmin.SRSvcAccChange{
Delete: &madmin.SRSvcAccDelete{ Delete: &madmin.SRSvcAccDelete{
AccessKey: serviceAccount, AccessKey: serviceAccount,
}, },
}, },
}) }); err != nil {
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return return
} }

View File

@ -235,7 +235,7 @@ func getClaimsFromTokenWithSecret(token, secret string) (map[string]interface{},
logger.LogIf(GlobalContext, err, logger.Application) logger.LogIf(GlobalContext, err, logger.Application)
return nil, errAuthentication return nil, errAuthentication
} }
claims.MapClaims[iampolicy.SessionPolicyName] = string(spBytes) claims.MapClaims[sessionPolicyNameExtracted] = string(spBytes)
} }
return claims.Map(), nil return claims.Map(), nil

View File

@ -1684,6 +1684,17 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st
return fmt.Errorf("unable to get svc acc claims: %v", err) return fmt.Errorf("unable to get svc acc claims: %v", err)
} }
// Extracted session policy name string can be removed as its not useful
// at this point.
delete(m, sessionPolicyNameExtracted)
// sessionPolicy is nil and there is embedded policy attached we remove
// rembedded policy at that point.
if _, ok := m[iampolicy.SessionPolicyName]; ok && opts.sessionPolicy == nil {
delete(m, iampolicy.SessionPolicyName)
m[iamPolicyClaimNameSA()] = inheritedPolicyType
}
if opts.sessionPolicy != nil { if opts.sessionPolicy != nil {
if err := opts.sessionPolicy.Validate(); err != nil { if err := opts.sessionPolicy.Validate(); err != nil {
return err return err
@ -1700,7 +1711,7 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st
// Overwrite session policy claims. // Overwrite session policy claims.
m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString(policyBuf) m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString(policyBuf)
m[iamPolicyClaimNameSA()] = "embedded-policy" m[iamPolicyClaimNameSA()] = embeddedPolicyType
} }
cr.SessionToken, err = auth.JWTSignWithAccessKey(accessKey, m, cr.SecretKey) cr.SessionToken, err = auth.JWTSignWithAccessKey(accessKey, m, cr.SecretKey)

View File

@ -63,6 +63,11 @@ const (
statusDisabled = "disabled" statusDisabled = "disabled"
) )
const (
embeddedPolicyType = "embedded-policy"
inheritedPolicyType = "inherited-policy"
)
// IAMSys - config system. // IAMSys - config system.
type IAMSys struct { type IAMSys struct {
// Need to keep them here to keep alignment - ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG // Need to keep them here to keep alignment - ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
@ -866,9 +871,9 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, gro
if len(policyBuf) > 0 { if len(policyBuf) > 0 {
m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString(policyBuf) m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString(policyBuf)
m[iamPolicyClaimNameSA()] = "embedded-policy" m[iamPolicyClaimNameSA()] = embeddedPolicyType
} else { } else {
m[iamPolicyClaimNameSA()] = "inherited-policy" m[iamPolicyClaimNameSA()] = inheritedPolicyType
} }
// Add all the necessary claims for the service accounts. // Add all the necessary claims for the service accounts.
@ -989,7 +994,7 @@ func (sys *IAMSys) getServiceAccount(ctx context.Context, accessKey string) (aut
} }
pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA()) pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA())
sp, spok := jwtClaims.Lookup(iampolicy.SessionPolicyName) sp, spok := jwtClaims.Lookup(iampolicy.SessionPolicyName)
if ptok && spok && pt == "embedded-policy" { if ptok && spok && pt == embeddedPolicyType {
policyBytes, err := base64.StdEncoding.DecodeString(sp) policyBytes, err := base64.StdEncoding.DecodeString(sp)
if err != nil { if err != nil {
return auth.Credentials{}, nil, err return auth.Credentials{}, nil, err
@ -1417,6 +1422,8 @@ func (sys *IAMSys) PolicyDBGet(name string, isGroup bool, groups ...string) ([]s
return sys.store.PolicyDBGet(name, isGroup, groups...) return sys.store.PolicyDBGet(name, isGroup, groups...)
} }
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 {
@ -1493,12 +1500,12 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parentUser strin
return false return false
} }
if saPolicyClaimStr == "inherited-policy" { if saPolicyClaimStr == inheritedPolicyType {
return combinedPolicy.IsAllowed(parentArgs) return combinedPolicy.IsAllowed(parentArgs)
} }
// Now check if we have a sessionPolicy. // Now check if we have a sessionPolicy.
spolicy, ok := args.Claims[iampolicy.SessionPolicyName] spolicy, ok := args.Claims[sessionPolicyNameExtracted]
if !ok { if !ok {
return false return false
} }
@ -1646,7 +1653,7 @@ func isAllowedBySessionPolicy(args iampolicy.Args) (hasSessionPolicy bool, isAll
isAllowed = false isAllowed = false
// Now check if we have a sessionPolicy. // Now check if we have a sessionPolicy.
spolicy, ok := args.Claims[iampolicy.SessionPolicyName] spolicy, ok := args.Claims[sessionPolicyNameExtracted]
if !ok { if !ok {
return return
} }