mirror of https://github.com/minio/minio.git
sr: Avoid recursion when loading site replicator credentials (#20262)
If the site replication is enabled and the code tries to extract jwt claims while the site replication service account credentials are still not loaded yet, the code will enter an infinite loop, causing in a high CPU usage. Another possibility of the infinite loop is having some service accounts created by an old deployment version where the service account JWT was signed by the root credentials, but not anymore. This commit will remove the possibility of the infinite loop in the code and add root credential fallback to extract claims from old service accounts.
This commit is contained in:
parent
db78431b1d
commit
b508264ac4
|
@ -249,6 +249,18 @@ func (ies *IAMEtcdStore) addUser(ctx context.Context, user string, userType IAMU
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ies *IAMEtcdStore) loadSecretKey(ctx context.Context, user string, userType IAMUserType) (string, error) {
|
||||||
|
var u UserIdentity
|
||||||
|
err := ies.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, errConfigNotFound) {
|
||||||
|
return "", errNoSuchUser
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return u.Credentials.SecretKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ies *IAMEtcdStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error {
|
func (ies *IAMEtcdStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error {
|
||||||
var u UserIdentity
|
var u UserIdentity
|
||||||
err := ies.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
|
err := ies.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
|
||||||
|
|
|
@ -225,6 +225,18 @@ func (iamOS *IAMObjectStore) loadPolicyDocs(ctx context.Context, m map[string]Po
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iamOS *IAMObjectStore) loadSecretKey(ctx context.Context, user string, userType IAMUserType) (string, error) {
|
||||||
|
var u UserIdentity
|
||||||
|
err := iamOS.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, errConfigNotFound) {
|
||||||
|
return "", errNoSuchUser
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return u.Credentials.SecretKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error {
|
func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error {
|
||||||
var u UserIdentity
|
var u UserIdentity
|
||||||
err := iamOS.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
|
err := iamOS.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
|
||||||
|
|
|
@ -535,6 +535,7 @@ type IAMStorageAPI interface {
|
||||||
loadPolicyDocWithRetry(ctx context.Context, policy string, m map[string]PolicyDoc, retries int) error
|
loadPolicyDocWithRetry(ctx context.Context, policy string, m map[string]PolicyDoc, retries int) error
|
||||||
loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error
|
loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error
|
||||||
loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error
|
loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error
|
||||||
|
loadSecretKey(ctx context.Context, user string, userType IAMUserType) (string, error)
|
||||||
loadUsers(ctx context.Context, userType IAMUserType, m map[string]UserIdentity) error
|
loadUsers(ctx context.Context, userType IAMUserType, m map[string]UserIdentity) error
|
||||||
loadGroup(ctx context.Context, group string, m map[string]GroupInfo) error
|
loadGroup(ctx context.Context, group string, m map[string]GroupInfo) error
|
||||||
loadGroups(ctx context.Context, m map[string]GroupInfo) error
|
loadGroups(ctx context.Context, m map[string]GroupInfo) error
|
||||||
|
@ -2820,20 +2821,29 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractJWTClaims(u UserIdentity) (*jwt.MapClaims, error) {
|
func extractJWTClaims(u UserIdentity) (jwtClaims *jwt.MapClaims, err error) {
|
||||||
jwtClaims, err := auth.ExtractClaims(u.Credentials.SessionToken, u.Credentials.SecretKey)
|
keys := make([]string, 0, 3)
|
||||||
if err != nil {
|
// Append credentials secret key itself
|
||||||
secretKey, err := getTokenSigningKey()
|
keys = append(keys, u.Credentials.SecretKey)
|
||||||
if err != nil {
|
// Use site-replication credentials if found
|
||||||
return nil, err
|
if globalSiteReplicationSys.isEnabled() {
|
||||||
|
siteReplSecretKey, e := globalSiteReplicatorCred.Get(GlobalContext)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
}
|
}
|
||||||
// Session tokens for STS creds will be generated with root secret or site-replicator-0 secret
|
keys = append(keys, siteReplSecretKey)
|
||||||
jwtClaims, err = auth.ExtractClaims(u.Credentials.SessionToken, secretKey)
|
}
|
||||||
if err != nil {
|
// Use root credentials for credentials created with older deployments
|
||||||
return nil, err
|
keys = append(keys, globalActiveCred.SecretKey)
|
||||||
|
|
||||||
|
// Iterate over all keys and return with the first successful claim extraction
|
||||||
|
for _, key := range keys {
|
||||||
|
jwtClaims, err = auth.ExtractClaims(u.Credentials.SessionToken, key)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return jwtClaims, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSvcExpirationInUTC(expirationInUTC time.Time) error {
|
func validateSvcExpirationInUTC(expirationInUTC time.Time) error {
|
||||||
|
|
|
@ -211,7 +211,7 @@ func (sys *IAMSys) Load(ctx context.Context, firstTime bool) error {
|
||||||
if !globalSiteReplicatorCred.IsValid() {
|
if !globalSiteReplicatorCred.IsValid() {
|
||||||
sa, _, err := sys.getServiceAccount(ctx, siteReplicatorSvcAcc)
|
sa, _, err := sys.getServiceAccount(ctx, siteReplicatorSvcAcc)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
globalSiteReplicatorCred.Set(sa.Credentials)
|
globalSiteReplicatorCred.Set(sa.Credentials.SecretKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -597,7 +597,7 @@ func (c *SiteReplicationSys) AddPeerClusters(ctx context.Context, psites []madmi
|
||||||
}
|
}
|
||||||
|
|
||||||
if !globalSiteReplicatorCred.IsValid() {
|
if !globalSiteReplicatorCred.IsValid() {
|
||||||
globalSiteReplicatorCred.Set(svcCred)
|
globalSiteReplicatorCred.Set(svcCred.SecretKey)
|
||||||
}
|
}
|
||||||
result := madmin.ReplicateAddStatus{
|
result := madmin.ReplicateAddStatus{
|
||||||
Success: true,
|
Success: true,
|
||||||
|
@ -659,7 +659,7 @@ func (c *SiteReplicationSys) PeerJoinReq(ctx context.Context, arg madmin.SRPeerJ
|
||||||
return errSRBackendIssue(fmt.Errorf("unable to save cluster-replication state to drive on %s: %v", ourName, err))
|
return errSRBackendIssue(fmt.Errorf("unable to save cluster-replication state to drive on %s: %v", ourName, err))
|
||||||
}
|
}
|
||||||
if !globalSiteReplicatorCred.IsValid() {
|
if !globalSiteReplicatorCred.IsValid() {
|
||||||
globalSiteReplicatorCred.Set(sa)
|
globalSiteReplicatorCred.Set(sa.SecretKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -6269,34 +6269,36 @@ func ilmExpiryReplicationEnabled(sites map[string]madmin.PeerInfo) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type siteReplicatorCred struct {
|
type siteReplicatorCred struct {
|
||||||
Creds auth.Credentials
|
secretKey string
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get or attempt to load site replicator credentials from disk.
|
// Get or attempt to load site replicator credentials from disk.
|
||||||
func (s *siteReplicatorCred) Get(ctx context.Context) (auth.Credentials, error) {
|
func (s *siteReplicatorCred) Get(ctx context.Context) (string, error) {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
if s.Creds.IsValid() {
|
secretKey := s.secretKey
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
return s.Creds, nil
|
|
||||||
|
if secretKey != "" {
|
||||||
|
return secretKey, nil
|
||||||
}
|
}
|
||||||
s.RUnlock()
|
|
||||||
m := make(map[string]UserIdentity)
|
secretKey, err := globalIAMSys.store.loadSecretKey(ctx, siteReplicatorSvcAcc, svcUser)
|
||||||
if err := globalIAMSys.store.loadUser(ctx, siteReplicatorSvcAcc, svcUser, m); err != nil {
|
if err != nil {
|
||||||
return auth.Credentials{}, err
|
return "", err
|
||||||
}
|
}
|
||||||
s.Set(m[siteReplicatorSvcAcc].Credentials)
|
s.Set(secretKey)
|
||||||
return m[siteReplicatorSvcAcc].Credentials, nil
|
return secretKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *siteReplicatorCred) Set(c auth.Credentials) {
|
func (s *siteReplicatorCred) Set(secretKey string) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
s.Creds = c
|
s.secretKey = secretKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *siteReplicatorCred) IsValid() bool {
|
func (s *siteReplicatorCred) IsValid() bool {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
return s.Creds.IsValid()
|
return s.secretKey != ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,11 +241,11 @@ func parseForm(r *http.Request) error {
|
||||||
func getTokenSigningKey() (string, error) {
|
func getTokenSigningKey() (string, error) {
|
||||||
secret := globalActiveCred.SecretKey
|
secret := globalActiveCred.SecretKey
|
||||||
if globalSiteReplicationSys.isEnabled() {
|
if globalSiteReplicationSys.isEnabled() {
|
||||||
c, err := globalSiteReplicatorCred.Get(GlobalContext)
|
secretKey, err := globalSiteReplicatorCred.Get(GlobalContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return c.SecretKey, nil
|
return secretKey, nil
|
||||||
}
|
}
|
||||||
return secret, nil
|
return secret, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue