mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	backport of commit 8b15e7d216 (#23766)
				
					
				
			Co-authored-by: davidadeleon <56207066+davidadeleon@users.noreply.github.com>
This commit is contained in:
		 hc-github-team-secure-vault-core
					hc-github-team-secure-vault-core
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							ce0c6f9336
						
					
				
				
					commit
					33edf89a86
				
			| @@ -1,3 +0,0 @@ | |||||||
| ```release-note:improvement |  | ||||||
| core: emit logs when user(s) are locked out and when all lockouts have been cleared |  | ||||||
| ``` |  | ||||||
| @@ -3074,7 +3074,6 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical. | |||||||
| 		DisableSSCTokens:               config.DisableSSCTokens, | 		DisableSSCTokens:               config.DisableSSCTokens, | ||||||
| 		Experiments:                    config.Experiments, | 		Experiments:                    config.Experiments, | ||||||
| 		AdministrativeNamespacePath:    config.AdministrativeNamespacePath, | 		AdministrativeNamespacePath:    config.AdministrativeNamespacePath, | ||||||
| 		UserLockoutLogInterval:         config.UserLockoutLogInterval, |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if c.flagDev { | 	if c.flagDev { | ||||||
|   | |||||||
| @@ -780,7 +780,6 @@ func testConfig_Sanitized(t *testing.T) { | |||||||
| 		"enable_response_header_hostname":     false, | 		"enable_response_header_hostname":     false, | ||||||
| 		"enable_response_header_raft_node_id": false, | 		"enable_response_header_raft_node_id": false, | ||||||
| 		"log_requests_level":                  "basic", | 		"log_requests_level":                  "basic", | ||||||
| 		"user_lockout_log_interval":           0 * time.Second, |  | ||||||
| 		"ha_storage": map[string]interface{}{ | 		"ha_storage": map[string]interface{}{ | ||||||
| 			"cluster_addr":       "top_level_cluster_addr", | 			"cluster_addr":       "top_level_cluster_addr", | ||||||
| 			"disable_clustering": true, | 			"disable_clustering": true, | ||||||
|   | |||||||
| @@ -167,7 +167,6 @@ func TestSysConfigState_Sanitized(t *testing.T) { | |||||||
| 				"enable_response_header_hostname":     false, | 				"enable_response_header_hostname":     false, | ||||||
| 				"enable_response_header_raft_node_id": false, | 				"enable_response_header_raft_node_id": false, | ||||||
| 				"log_requests_level":                  "", | 				"log_requests_level":                  "", | ||||||
| 				"user_lockout_log_interval":           json.Number("0"), |  | ||||||
| 				"listeners": []interface{}{ | 				"listeners": []interface{}{ | ||||||
| 					map[string]interface{}{ | 					map[string]interface{}{ | ||||||
| 						"config": nil, | 						"config": nil, | ||||||
|   | |||||||
| @@ -24,8 +24,6 @@ type SharedConfig struct { | |||||||
| 	Listeners []*Listener `hcl:"-"` | 	Listeners []*Listener `hcl:"-"` | ||||||
|  |  | ||||||
| 	UserLockouts []*UserLockout `hcl:"-"` | 	UserLockouts []*UserLockout `hcl:"-"` | ||||||
| 	UserLockoutLogInterval    time.Duration  `hcl:"-"` |  | ||||||
| 	UserLockoutLogIntervalRaw interface{}    `hcl:"user_lockout_log_interval"` |  | ||||||
|  |  | ||||||
| 	Seals   []*KMS   `hcl:"-"` | 	Seals   []*KMS   `hcl:"-"` | ||||||
| 	Entropy *Entropy `hcl:"-"` | 	Entropy *Entropy `hcl:"-"` | ||||||
| @@ -89,14 +87,6 @@ func ParseConfig(d string) (*SharedConfig, error) { | |||||||
| 		result.DisableMlockRaw = nil | 		result.DisableMlockRaw = nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if result.UserLockoutLogIntervalRaw != nil { |  | ||||||
| 		if result.UserLockoutLogInterval, err = parseutil.ParseDurationSecond(result.UserLockoutLogIntervalRaw); err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		result.FoundKeys = append(result.FoundKeys, "UserLockoutLogInterval") |  | ||||||
| 		result.UserLockoutLogIntervalRaw = nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	list, ok := obj.Node.(*ast.ObjectList) | 	list, ok := obj.Node.(*ast.ObjectList) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, fmt.Errorf("error parsing: file doesn't contain a root object") | 		return nil, fmt.Errorf("error parsing: file doesn't contain a root object") | ||||||
| @@ -186,7 +176,6 @@ func (c *SharedConfig) Sanitized() map[string]interface{} { | |||||||
| 		"pid_file":                      c.PidFile, | 		"pid_file":                      c.PidFile, | ||||||
| 		"cluster_name":                  c.ClusterName, | 		"cluster_name":                  c.ClusterName, | ||||||
| 		"administrative_namespace_path": c.AdministrativeNamespacePath, | 		"administrative_namespace_path": c.AdministrativeNamespacePath, | ||||||
| 		"user_lockout_log_interval":     c.UserLockoutLogInterval, |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Optional log related settings | 	// Optional log related settings | ||||||
|   | |||||||
| @@ -56,11 +56,6 @@ func (c *SharedConfig) Merge(c2 *SharedConfig) *SharedConfig { | |||||||
| 		result.DefaultMaxRequestDuration = c2.DefaultMaxRequestDuration | 		result.DefaultMaxRequestDuration = c2.DefaultMaxRequestDuration | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	result.UserLockoutLogInterval = c.UserLockoutLogInterval |  | ||||||
| 	if c2.UserLockoutLogInterval > result.UserLockoutLogInterval { |  | ||||||
| 		result.UserLockoutLogInterval = c2.UserLockoutLogInterval |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	result.LogLevel = c.LogLevel | 	result.LogLevel = c.LogLevel | ||||||
| 	if c2.LogLevel != "" { | 	if c2.LogLevel != "" { | ||||||
| 		result.LogLevel = c2.LogLevel | 		result.LogLevel = c2.LogLevel | ||||||
|   | |||||||
| @@ -105,11 +105,6 @@ const ( | |||||||
| 	// MfaAuthResponse when the value is not specified in the server config | 	// MfaAuthResponse when the value is not specified in the server config | ||||||
| 	defaultMFAAuthResponseTTL = 300 * time.Second | 	defaultMFAAuthResponseTTL = 300 * time.Second | ||||||
|  |  | ||||||
| 	// defaultUserLockoutLogInterval is the default duration that Vault will |  | ||||||
| 	// emit a log informing that a user lockout is in effect when the value |  | ||||||
| 	// is not specified in the server config |  | ||||||
| 	defaultUserLockoutLogInterval = 1 * time.Minute |  | ||||||
|  |  | ||||||
| 	// defaultMaxTOTPValidateAttempts is the default value for the number | 	// defaultMaxTOTPValidateAttempts is the default value for the number | ||||||
| 	// of failed attempts to validate a request subject to TOTP MFA. If the | 	// of failed attempts to validate a request subject to TOTP MFA. If the | ||||||
| 	// number of failed totp passcode validations exceeds this max value, the | 	// number of failed totp passcode validations exceeds this max value, the | ||||||
| @@ -667,9 +662,6 @@ type Core struct { | |||||||
|  |  | ||||||
| 	updateLockedUserEntriesCancel context.CancelFunc | 	updateLockedUserEntriesCancel context.CancelFunc | ||||||
|  |  | ||||||
| 	lockoutLoggerCancel    context.CancelFunc |  | ||||||
| 	userLockoutLogInterval time.Duration |  | ||||||
|  |  | ||||||
| 	// number of workers to use for lease revocation in the expiration manager | 	// number of workers to use for lease revocation in the expiration manager | ||||||
| 	numExpirationWorkers int | 	numExpirationWorkers int | ||||||
|  |  | ||||||
| @@ -885,8 +877,6 @@ type CoreConfig struct { | |||||||
| 	AdministrativeNamespacePath string | 	AdministrativeNamespacePath string | ||||||
|  |  | ||||||
| 	NumRollbackWorkers int | 	NumRollbackWorkers int | ||||||
|  |  | ||||||
| 	UserLockoutLogInterval time.Duration |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // SubloggerHook implements the SubloggerAdder interface. This implementation | // SubloggerHook implements the SubloggerAdder interface. This implementation | ||||||
| @@ -934,10 +924,6 @@ func CreateCore(conf *CoreConfig) (*Core, error) { | |||||||
| 		return nil, fmt.Errorf("cannot have DefaultLeaseTTL larger than MaxLeaseTTL") | 		return nil, fmt.Errorf("cannot have DefaultLeaseTTL larger than MaxLeaseTTL") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if conf.UserLockoutLogInterval == 0 { |  | ||||||
| 		conf.UserLockoutLogInterval = defaultUserLockoutLogInterval |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Validate the advertise addr if its given to us | 	// Validate the advertise addr if its given to us | ||||||
| 	if conf.RedirectAddr != "" { | 	if conf.RedirectAddr != "" { | ||||||
| 		u, err := url.Parse(conf.RedirectAddr) | 		u, err := url.Parse(conf.RedirectAddr) | ||||||
| @@ -1062,7 +1048,6 @@ func CreateCore(conf *CoreConfig) (*Core, error) { | |||||||
| 		disableSSCTokens:               conf.DisableSSCTokens, | 		disableSSCTokens:               conf.DisableSSCTokens, | ||||||
| 		effectiveSDKVersion:            effectiveSDKVersion, | 		effectiveSDKVersion:            effectiveSDKVersion, | ||||||
| 		userFailedLoginInfo:            make(map[FailedLoginUser]*FailedLoginInfo), | 		userFailedLoginInfo:            make(map[FailedLoginUser]*FailedLoginInfo), | ||||||
| 		userLockoutLogInterval:         conf.UserLockoutLogInterval, |  | ||||||
| 		experiments:                    conf.Experiments, | 		experiments:                    conf.Experiments, | ||||||
| 		pendingRemovalMountsAllowed:    conf.PendingRemovalMountsAllowed, | 		pendingRemovalMountsAllowed:    conf.PendingRemovalMountsAllowed, | ||||||
| 		expirationRevokeRetryBase:      conf.ExpirationRevokeRetryBase, | 		expirationRevokeRetryBase:      conf.ExpirationRevokeRetryBase, | ||||||
| @@ -3677,51 +3662,6 @@ func (c *Core) setupCachedMFAResponseAuth() { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Core) startLockoutLogger() { |  | ||||||
| 	// Are we already running a logger |  | ||||||
| 	if c.lockoutLoggerCancel != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx, cancelFunc := context.WithCancel(c.activeContext) |  | ||||||
| 	c.lockoutLoggerCancel = cancelFunc |  | ||||||
|  |  | ||||||
| 	// Perform first check for lockout entries |  | ||||||
| 	lockedUserCount := c.getUserFailedLoginCount(ctx) |  | ||||||
|  |  | ||||||
| 	if lockedUserCount > 0 { |  | ||||||
| 		c.Logger().Warn("user lockout(s) in effect") |  | ||||||
| 	} else { |  | ||||||
| 		// We shouldn't end up here |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Start lockout watcher |  | ||||||
| 	go func() { |  | ||||||
| 		ticker := time.NewTicker(c.userLockoutLogInterval) |  | ||||||
| 		for { |  | ||||||
| 			select { |  | ||||||
| 			case <-ticker.C: |  | ||||||
| 				// Check for lockout entries |  | ||||||
| 				lockedUserCount := c.getUserFailedLoginCount(ctx) |  | ||||||
|  |  | ||||||
| 				if lockedUserCount > 0 { |  | ||||||
| 					c.Logger().Warn("user lockout(s) in effect") |  | ||||||
| 					break |  | ||||||
| 				} |  | ||||||
| 				c.Logger().Info("user lockout(s) cleared") |  | ||||||
| 				ticker.Stop() |  | ||||||
| 				c.lockoutLoggerCancel = nil |  | ||||||
| 				return |  | ||||||
| 			case <-ctx.Done(): |  | ||||||
| 				ticker.Stop() |  | ||||||
| 				c.lockoutLoggerCancel = nil |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // updateLockedUserEntries runs every 15 mins to remove stale user entries from storage | // updateLockedUserEntries runs every 15 mins to remove stale user entries from storage | ||||||
| // it also updates the userFailedLoginInfo map with correct information for locked users if incorrect | // it also updates the userFailedLoginInfo map with correct information for locked users if incorrect | ||||||
| func (c *Core) updateLockedUserEntries() { | func (c *Core) updateLockedUserEntries() { | ||||||
| @@ -3750,13 +3690,7 @@ func (c *Core) updateLockedUserEntries() { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| } | 	return | ||||||
|  |  | ||||||
| func (c *Core) getUserFailedLoginCount(ctx context.Context) int { |  | ||||||
| 	c.userFailedLoginInfoLock.Lock() |  | ||||||
| 	defer c.userFailedLoginInfoLock.Unlock() |  | ||||||
|  |  | ||||||
| 	return len(c.userFailedLoginInfo) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // runLockedUserEntryUpdates runs updates for locked user storage entries and userFailedLoginInfo map | // runLockedUserEntryUpdates runs updates for locked user storage entries and userFailedLoginInfo map | ||||||
|   | |||||||
| @@ -51,18 +51,6 @@ func unlockUser(ctx context.Context, core *Core, mountAccessor string, aliasName | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if core.lockoutLoggerCancel != nil { |  | ||||||
| 		// Check if we have no more locked users and cancel any running lockout logger |  | ||||||
| 		core.userFailedLoginInfoLock.RLock() |  | ||||||
| 		numLockedUsers := len(core.userFailedLoginInfo) |  | ||||||
| 		core.userFailedLoginInfoLock.RUnlock() |  | ||||||
|  |  | ||||||
| 		if numLockedUsers == 0 { |  | ||||||
| 			core.Logger().Info("user lockout(s) cleared") |  | ||||||
| 			core.lockoutLoggerCancel() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1478,7 +1478,6 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re | |||||||
| 			return nil, nil, err | 			return nil, nil, err | ||||||
| 		} | 		} | ||||||
| 		if isloginUserLocked { | 		if isloginUserLocked { | ||||||
| 			c.startLockoutLogger() |  | ||||||
| 			return nil, nil, logical.ErrPermissionDenied | 			return nil, nil, logical.ErrPermissionDenied | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -2294,8 +2293,6 @@ func (c *Core) LocalGetUserFailedLoginInfo(ctx context.Context, userKey FailedLo | |||||||
| // LocalUpdateUserFailedLoginInfo updates the failed login information for a user based on alias name and mountAccessor | // LocalUpdateUserFailedLoginInfo updates the failed login information for a user based on alias name and mountAccessor | ||||||
| func (c *Core) LocalUpdateUserFailedLoginInfo(ctx context.Context, userKey FailedLoginUser, failedLoginInfo *FailedLoginInfo, deleteEntry bool) error { | func (c *Core) LocalUpdateUserFailedLoginInfo(ctx context.Context, userKey FailedLoginUser, failedLoginInfo *FailedLoginInfo, deleteEntry bool) error { | ||||||
| 	c.userFailedLoginInfoLock.Lock() | 	c.userFailedLoginInfoLock.Lock() | ||||||
| 	defer c.userFailedLoginInfoLock.Unlock() |  | ||||||
|  |  | ||||||
| 	switch deleteEntry { | 	switch deleteEntry { | ||||||
| 	case false: | 	case false: | ||||||
| 		// update entry in the map | 		// update entry in the map | ||||||
| @@ -2338,6 +2335,7 @@ func (c *Core) LocalUpdateUserFailedLoginInfo(ctx context.Context, userKey Faile | |||||||
| 		// delete the entry from the map, if no key exists it is no-op | 		// delete the entry from the map, if no key exists it is no-op | ||||||
| 		delete(c.userFailedLoginInfo, userKey) | 		delete(c.userFailedLoginInfo, userKey) | ||||||
| 	} | 	} | ||||||
|  | 	c.userFailedLoginInfoLock.Unlock() | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user