-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vault 8305 Prevent Brute Forcing in Auth methods : Setting user lockout configuration #17338
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not gone through the entire review yet. It is a nice work! Some comments and questions.
} | ||
|
||
} | ||
if userLockoutConfig.DisableLockoutRaw != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The raw
entries in this function are parsed if they are non-nil. I believe we have this practice in parsing config options that if a raw
entry is parsed, it is being set to nil afterwards. Would you think doing the same would be better for consistency? I do notice that these raw values are relied upon to set default values in setMissingUserLockoutValuesInMap
function. But, those logics could be changed to checking the non-raw
entries and if they have not set before, setting them to the default values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree that the usual way is to set nil for 'raw' values as soon as they are parsed. The issue with checking non-raw entries is because of fields lockout threshold (uint) and disable lockout (bool). It will be difficult to figure out if the user configured lockout threshold with value 0 or if the user did not configure this value at all. In case of other configurations like Listeners, we append values after the parse but in this case, we parse through the map values to set default values for each "type" mentioned in config in result.UserLockouts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One way to get away from this is set these values to defaults before parsing raw values. But, I guess then there are values that are populated by all
type.
switch apiuserLockoutConfig.LockoutCounterResetDuration { | ||
case "": | ||
newUserLockoutCounterReset = oldUserLockoutCounterReset | ||
case "system": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just out of curiosity, what does "system" mean here? Why would the value for counter reset be passed in as system
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And, Is there a default value for the counter reset? or time.Duration(0)
is the default value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In command/auth_tune.go , we set lockout duration as LockoutDuration = ttlToAPI(c.flagUserLockoutDuration). ttlToApi converts user-supplied ttl into an API-compatible string. If the TTL is 0, this returns the empty string. If the TTL is negative, this returns "system" to indicate to use the system values. Otherwise, the time.Duration ttl is used. In the api part, if we do not have any values for this string, we use the already existing values that is present in the entry. If the string is system, we set time.Duration(0) else we use the input ttl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we have a default value of 15 minutes for lockout duration and lockout counter reset if not configured by the user using config file / auth tune.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have similar thoughts to some of the comments that @hghaf099 made. Will keep an eye on those threads. Looks good though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor follow ups. Nice work!
} | ||
|
||
} | ||
if userLockoutConfig.DisableLockoutRaw != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One way to get away from this is set these values to defaults before parsing raw values. But, I guess then there are values that are populated by all
type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some minor comments. The code looks good to me!
{ | ||
Type: "all", | ||
LockoutThreshold: 5, | ||
LockoutThresholdRaw: nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Would you think removing the nil values here would help readability? Seems like they are unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test would failed if we remove this as it compares with what the values in SharedConfig would look like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I am curious to know what is the failure. The zero value for an interface is nil, so if you don't set it nil explicitly, it should still be nil, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, the test was flaky before and the comparison failed for config and expected config sometimes due to arrangement of array elements. When I removed raw entries then, the comparison failed and I felt it was because of that. I think the flaky test fix fixed that and on removing the nil fields, the comparison is successful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
@@ -0,0 +1,3 @@ | |||
```release-note:feature | |||
core: Add user lockout field to config and configuring this for auth mount using auth tune to prevent brute forcing in auth methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@akshya96 - I left a comment on the enterprise PR as well - for new features, they only need one changelog entry per new feature. If you want to make separate changelog entries per PR, those entries shouldn't be in the "features" section (and you may or may not need to make separate additional changelog entries).
For new features, they have a different format for changelog entries. Could you please open a new PR (or use the enterprise PR) to consolidate the changelog entries for this feature and change the formatting?
Jira: https://hashicorp.atlassian.net/browse/VAULT-8305
RFC: https://docs.google.com/document/d/1ypCRDMrjmKkElN51PcRSHJtyYNmolhdTu_M_EkPPv5U/edit?usp=sharing
Includes:
Adding user lockout configuration for each auth method using config file
Modifying user lockout configuration for each auth mount using auth tune cli and api
Example of user lockout in config file:
user_lockout "all" {
lockout_threshold = "25"
lockout_duration = "40m"
lockout_counter_reset = "45m"
disable_lockout = "false"
}
user_lockout "userpass" {
lockout_threshold = "100"
lockout_duration = "20m"
}