-
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
Use service bind for searching LDAP groups #2534
Conversation
@shomron Any chance I can get your opinion on this? |
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.
Overall 👍 - this is the fix follows what we originally discussed way back in August.
Aside from improving the error messages I think we can proceed with this logic.
Separately, the code could do with a little refactoring and cleanup to make it easier to follow and manage the relation between DiscoverDN
and BindDN/BindPassword
in a more concise way.
Regarding the discussion in #2387, we should definitely not perform the group search before user authentication - that would just expose the LDAP server to unauthenticated DoS.
builtin/credential/ldap/backend.go
Outdated
@@ -122,6 +122,17 @@ func (b *backend) Login(req *logical.Request, username string, password string) | |||
return nil, logical.ErrorResponse(fmt.Sprintf("LDAP bind failed: %v", err)), nil | |||
} | |||
|
|||
// We re-bind to the BindDN if it's defined because we assume | |||
// the BindDN should be the one to search, not the user logging in. | |||
if cfg.DiscoverDN || (cfg.BindDN != "" && cfg.BindPassword != "") { |
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.
This is symmetric with the logic from getBindDN
- my only hesitation is that in cases where (cfg.DiscoverDN == true && (cfg.BindDN == "" || cfg.BindPassword == "")
- we will do an anonymous rebind after an authenticated user bind - potentially resulting in more restricted privileges.
I am not sure whether that is a realistic configuration to know whether this is a valid concern.
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.
@shomron, should I just get rid of cfg.DiscoverDN
clause? And simply re-bind if the second clause is true? That makes the most sense to me.
builtin/credential/ldap/backend.go
Outdated
// the BindDN should be the one to search, not the user logging in. | ||
if cfg.DiscoverDN || (cfg.BindDN != "" && cfg.BindPassword != "") { | ||
if err := c.Bind(cfg.BindDN, cfg.BindPassword); err != nil { | ||
return nil, logical.ErrorResponse(err.Error()), 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.
Given we are performing up to three binds per login - I think we should improve the error messages to more clearly indicate where in the process we failed.
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.
So maybe:
return nil, logical.ErrorResponse("Encountered an error when re-binding as the BindDN User: " + err.Error()), nil
builtin/credential/ldap/backend.go
Outdated
b.Logger().Debug("auth/ldap: Re-Bound to original BindDN") | ||
} | ||
} | ||
|
||
userDN, err := b.getUserDN(cfg, c, bindDN) |
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.
Between bindDN
and cfg.BindDN
, following the flow is a bit challenging. Perhaps we should rename the bindDN
local variable to something like userBindDN
, to differentiate it from the service bind DN - cfg.BindDN
?
Also note userBindDN
would not necessarily equal userDN
- userDN is the canonical representation of the user, whereas userBindDN
could in fact be using a different mechanism such as user principal name (user@domain
).
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.
That would make sense to me, but if we're going to go down that path, shouldn't we rename the getBindDN
method then? Line 107 is where bindDN
get's set so...
@mitchelldavis thanks for addressing this, I just started working on JumpCloud LDAP integration with our Vault cluster 👍 |
@shomron, I believe I've addressed all that. Does the code reflect what you were thinking? |
@shomron @mitchelldavis anything I can do to help get this into the next release? |
I'm not sure @lattwood. I think we're just waiting for a final review. |
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.
Thanks for jumping on this! Two minor nits, but looks good!
In the future I'd like to see the tests overhauled with a mock LDAP provider, allowing us to capture this behavior in test.
builtin/credential/ldap/backend.go
Outdated
if err != nil { | ||
return nil, logical.ErrorResponse(err.Error()), nil | ||
} | ||
|
||
if b.Logger().IsDebug() { | ||
b.Logger().Debug("auth/ldap: BindDN fetched", "username", username, "binddn", bindDN) | ||
b.Logger().Debug("auth/ldap: BindDN fetched", "username", username, "binddn", userBindDN) |
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: Debug message does not reflect binddn->userbinddn
.
builtin/credential/ldap/backend.go
Outdated
// the BindDN should be the one to search, not the user logging in. | ||
if cfg.BindDN != "" && cfg.BindPassword != "" { | ||
if err := c.Bind(cfg.BindDN, cfg.BindPassword); err != nil { | ||
return nil, logical.ErrorResponse("Encountered an error while attempting to re-bind with the BindDN User: " + err.Error()), 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: use fmt
.
return nil, logical.ErrorResponse(fmt.Sprintf("Encountered an error while attempting to re-bind with the BindDN User: %v", err)), nil
@shomron thanks for reviewing this! @shomron @mitchelldavis any backwards incompatible or notably changed behavior as a result of this? In addition, any necessary documentation changes? |
@jefferai, @shomron, that build issue looks like the vault unit tests failed. Have we seen that happening in other places because I didn't touch that code. @jefferai, to answer your question about the documentation - The documentation didn't really specify much to begin with. It makes sense to me to mention that the binddn user needs permission to search groups because it's what is being used to do that search. |
@mitchelldavis We get some transient unit test failures that we know of (often issues with the MySQL docker container) so don't worry about it if it's not a part of your changes. Please add the mention in the docs about the binddn user needing permission to search groups. Still waiting to hear from you and @shomron about any backwards incompatibilities. |
…rming the group search.
@mitchelldavis might be worth mentioning the user and group searches are done under separate (but identical) binds. |
Because we have to bind to the |
The clarification in the documentation looks good to me. @jefferai I don't see this as a backwards-incompatible change, more of a bugfix. It might be worthwhile adding something along these lines to the release notes: LDAP Auth Backend - Group membership queries will now run as the |
Sounds good to me! Merging -- thanks everyone! |
Addressing #2387!