loadUser() if not able to load() credential return error (#19931)

This commit is contained in:
Harshavardhana 2024-06-13 15:26:38 -07:00 committed by GitHub
parent 62e6dc950d
commit ba39ed9af7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 71 additions and 29 deletions

View File

@ -445,6 +445,8 @@ const (
ErrAdminNoAccessKey ErrAdminNoAccessKey
ErrAdminNoSecretKey ErrAdminNoSecretKey
ErrIAMNotInitialized
apiErrCodeEnd // This is used only for the testing code apiErrCodeEnd // This is used only for the testing code
) )
@ -1305,6 +1307,11 @@ var errorCodes = errorCodeMap{
Description: "Server not initialized yet, please try again.", Description: "Server not initialized yet, please try again.",
HTTPStatusCode: http.StatusServiceUnavailable, HTTPStatusCode: http.StatusServiceUnavailable,
}, },
ErrIAMNotInitialized: {
Code: "XMinioIAMNotInitialized",
Description: "IAM sub-system not initialized yet, please try again.",
HTTPStatusCode: http.StatusServiceUnavailable,
},
ErrBucketMetadataNotInitialized: { ErrBucketMetadataNotInitialized: {
Code: "XMinioBucketMetadataNotInitialized", Code: "XMinioBucketMetadataNotInitialized",
Description: "Bucket metadata not initialized yet, please try again.", Description: "Bucket metadata not initialized yet, please try again.",

File diff suppressed because one or more lines are too long

View File

@ -236,9 +236,8 @@ func (ies *IAMEtcdStore) addUser(ctx context.Context, user string, userType IAMU
// for the expiring credentials. // for the expiring credentials.
deleteKeyEtcd(ctx, ies.client, getUserIdentityPath(user, userType)) deleteKeyEtcd(ctx, ies.client, getUserIdentityPath(user, userType))
deleteKeyEtcd(ctx, ies.client, getMappedPolicyPath(user, userType, false)) deleteKeyEtcd(ctx, ies.client, getMappedPolicyPath(user, userType, false))
return nil
} }
return err return nil
} }
u.Credentials.Claims = jwtClaims.Map() u.Credentials.Claims = jwtClaims.Map()
} }

View File

@ -254,9 +254,8 @@ func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType
// for the expiring credentials. // for the expiring credentials.
iamOS.deleteIAMConfig(ctx, getUserIdentityPath(user, userType)) iamOS.deleteIAMConfig(ctx, getUserIdentityPath(user, userType))
iamOS.deleteIAMConfig(ctx, getMappedPolicyPath(user, userType, false)) iamOS.deleteIAMConfig(ctx, getMappedPolicyPath(user, userType, false))
return nil
} }
return err return nil
} }
u.Credentials.Claims = jwtClaims.Map() u.Credentials.Claims = jwtClaims.Map()

View File

@ -2568,10 +2568,10 @@ func (store *IAMStoreSys) UpdateUserIdentity(ctx context.Context, cred auth.Cred
} }
// LoadUser - attempts to load user info from storage and updates cache. // LoadUser - attempts to load user info from storage and updates cache.
func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) { func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) error {
// We use singleflight to de-duplicate requests when server // We use singleflight to de-duplicate requests when server
// is coming up and loading accessKey and its associated assets // is coming up and loading accessKey and its associated assets
val, err, shared := store.group.Do(accessKey, func() (interface{}, error) { val, err, shared := store.group.Do(accessKey, func() (val interface{}, err error) {
cache := store.lock() cache := store.lock()
defer func() { defer func() {
cache.updatedAt = time.Now() cache.updatedAt = time.Now()
@ -2582,27 +2582,29 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
// Check for regular user access key // Check for regular user access key
if !found { if !found {
store.loadUser(ctx, accessKey, regUser, cache.iamUsersMap) err = store.loadUser(ctx, accessKey, regUser, cache.iamUsersMap)
if _, found = cache.iamUsersMap[accessKey]; found { if _, found = cache.iamUsersMap[accessKey]; found {
// load mapped policies // load mapped policies
store.loadMappedPolicyWithRetry(ctx, accessKey, regUser, false, cache.iamUserPolicyMap, 3) err = store.loadMappedPolicyWithRetry(ctx, accessKey, regUser, false, cache.iamUserPolicyMap, 3)
} }
} }
// Check for service account // Check for service account
if !found { if !found {
store.loadUser(ctx, accessKey, svcUser, cache.iamUsersMap) err = store.loadUser(ctx, accessKey, svcUser, cache.iamUsersMap)
var svc UserIdentity var svc UserIdentity
svc, found = cache.iamUsersMap[accessKey] svc, found = cache.iamUsersMap[accessKey]
if found { if found {
// Load parent user and mapped policies. // Load parent user and mapped policies.
if store.getUsersSysType() == MinIOUsersSysType { if store.getUsersSysType() == MinIOUsersSysType {
store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap) err = store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap)
store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap, 3) if err == nil {
err = store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap, 3)
}
} else { } else {
// In case of LDAP the parent user's policy mapping needs to be // In case of LDAP the parent user's policy mapping needs to be
// loaded into sts map // loaded into sts map
store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3) err = store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
} }
} }
} }
@ -2611,10 +2613,10 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
stsAccountFound := false stsAccountFound := false
var stsUserCred UserIdentity var stsUserCred UserIdentity
if !found { if !found {
store.loadUser(ctx, accessKey, stsUser, cache.iamSTSAccountsMap) err = store.loadUser(ctx, accessKey, stsUser, cache.iamSTSAccountsMap)
if stsUserCred, found = cache.iamSTSAccountsMap[accessKey]; found { if stsUserCred, found = cache.iamSTSAccountsMap[accessKey]; found {
// Load mapped policy // Load mapped policy
store.loadMappedPolicyWithRetry(ctx, stsUserCred.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3) err = store.loadMappedPolicyWithRetry(ctx, stsUserCred.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
stsAccountFound = true stsAccountFound = true
} }
} }
@ -2624,24 +2626,30 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
pols, _ := cache.iamUserPolicyMap.Load(accessKey) pols, _ := cache.iamUserPolicyMap.Load(accessKey)
for _, policy := range pols.toSlice() { for _, policy := range pols.toSlice() {
if _, found = cache.iamPolicyDocsMap[policy]; !found { if _, found = cache.iamPolicyDocsMap[policy]; !found {
store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3) err = store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
} }
} }
} else { } else {
pols, _ := cache.iamSTSPolicyMap.Load(stsUserCred.Credentials.AccessKey) pols, _ := cache.iamSTSPolicyMap.Load(stsUserCred.Credentials.AccessKey)
for _, policy := range pols.toSlice() { for _, policy := range pols.toSlice() {
if _, found = cache.iamPolicyDocsMap[policy]; !found { if _, found = cache.iamPolicyDocsMap[policy]; !found {
store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3) err = store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
} }
} }
} }
return "done", nil return "done", err
}) })
if serverDebugLog { if serverDebugLog {
console.Debugln("loadUser: loading shared", val, err, shared) console.Debugln("loadUser: loading shared", val, err, shared)
} }
if IsErr(err, errNoSuchUser, errNoSuchPolicy, errNoSuchGroup) {
return nil
}
return err
} }
func extractJWTClaims(u UserIdentity) (*jwt.MapClaims, error) { func extractJWTClaims(u UserIdentity) (*jwt.MapClaims, error) {

View File

@ -1703,31 +1703,46 @@ func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
return nil return nil
} }
// GetUser - get user credentials // CheckKey validates the incoming accessKey
func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentity, ok bool) { func (sys *IAMSys) CheckKey(ctx context.Context, accessKey string) (u UserIdentity, ok bool, err error) {
if !sys.Initialized() { if !sys.Initialized() {
return u, false return u, false, nil
} }
if accessKey == globalActiveCred.AccessKey { if accessKey == globalActiveCred.AccessKey {
return newUserIdentity(globalActiveCred), true return newUserIdentity(globalActiveCred), true, nil
} }
loadUserCalled := false loadUserCalled := false
select { select {
case <-sys.configLoaded: case <-sys.configLoaded:
default: default:
sys.store.LoadUser(ctx, accessKey) err = sys.store.LoadUser(ctx, accessKey)
loadUserCalled = true loadUserCalled = true
} }
u, ok = sys.store.GetUser(accessKey) u, ok = sys.store.GetUser(accessKey)
if !ok && !loadUserCalled { if !ok && !loadUserCalled {
sys.store.LoadUser(ctx, accessKey) err = sys.store.LoadUser(ctx, accessKey)
loadUserCalled = true
u, ok = sys.store.GetUser(accessKey) u, ok = sys.store.GetUser(accessKey)
} }
return u, ok && u.Credentials.IsValid() if !ok && loadUserCalled && err != nil {
iamLogOnceIf(ctx, err, accessKey)
// return 503 to application
return u, false, errIAMNotInitialized
}
return u, ok && u.Credentials.IsValid(), nil
}
// GetUser - get user credentials
func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentity, ok bool) {
u, ok, _ = sys.CheckKey(ctx, accessKey)
return u, ok
} }
// Notify all other MinIO peers to load group. // Notify all other MinIO peers to load group.

View File

@ -20,6 +20,10 @@ func replLogOnceIf(ctx context.Context, err error, id string, errKind ...interfa
logger.LogOnceIf(ctx, "replication", err, id, errKind...) logger.LogOnceIf(ctx, "replication", err, id, errKind...)
} }
func iamLogOnceIf(ctx context.Context, err error, id string, errKind ...interface{}) {
logger.LogOnceIf(ctx, "iam", err, id, errKind...)
}
func iamLogIf(ctx context.Context, err error, errKind ...interface{}) { func iamLogIf(ctx context.Context, err error, errKind ...interface{}) {
if !errors.Is(err, grid.ErrDisconnected) { if !errors.Is(err, grid.ErrDisconnected) {
logger.LogIf(ctx, "iam", err, errKind...) logger.LogIf(ctx, "iam", err, errKind...)

View File

@ -152,11 +152,14 @@ func checkKeyValid(r *http.Request, accessKey string) (auth.Credentials, bool, A
// Check if server has initialized, then only proceed // Check if server has initialized, then only proceed
// to check for IAM users otherwise its okay for clients // to check for IAM users otherwise its okay for clients
// to retry with 503 errors when server is coming up. // to retry with 503 errors when server is coming up.
return auth.Credentials{}, false, ErrServerNotInitialized return auth.Credentials{}, false, ErrIAMNotInitialized
} }
// Check if the access key is part of users credentials. // Check if the access key is part of users credentials.
u, ok := globalIAMSys.GetUser(r.Context(), accessKey) u, ok, err := globalIAMSys.CheckKey(r.Context(), accessKey)
if err != nil {
return auth.Credentials{}, false, ErrIAMNotInitialized
}
if !ok { if !ok {
// Credentials could be valid but disabled - return a different // Credentials could be valid but disabled - return a different
// error in such a scenario. // error in such a scenario.

View File

@ -37,6 +37,12 @@ func niceError(code APIErrorCode) string {
} }
func TestDoesPolicySignatureMatch(t *testing.T) { func TestDoesPolicySignatureMatch(t *testing.T) {
_, fsDir, err := prepareFS(context.Background())
if err != nil {
t.Fatal(err)
}
defer removeRoots([]string{fsDir})
credentialTemplate := "%s/%s/%s/s3/aws4_request" credentialTemplate := "%s/%s/%s/s3/aws4_request"
now := UTCNow() now := UTCNow()
accessKey := globalActiveCred.AccessKey accessKey := globalActiveCred.AccessKey