Skip to content
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 18227/introduce cap ldap library #22185

Merged
merged 12 commits into from
Sep 14, 2023

Conversation

raymonstah
Copy link
Contributor

@raymonstah raymonstah commented Aug 2, 2023

Summary

Introduces the cap/ldap package as an alternative to the existing LDAP client. This allows for better security updates, patches, etc in one library to improve DRY-ness across our repos that leverage LDAP. Internally, this addresses VAULT-18227.

Note:
Since the cap/ldap package doesn't differentiate between connection_timeout and request_timeout like Vault does, this also introduces a change where it takes the minimum timeout between the two values, and uses that as the timeout for both. As a result, this PR also deprecates the connection_timeout field introduced in #20144. As a result, ldaputil.Client has also been deprecated, since cap/ldap.Client can be used to replace it.

Testing

Configuration

➜  vault git:(VAULT-18227/introduce-cap-ldap-library) ✗ vault auth enable ldap
Success! Enabled ldap auth method at: ldap/
➜  vault git:(VAULT-18227/introduce-cap-ldap-library) vault write auth/ldap/config \
binddn="cn=admin,dc=example,dc=org" \
bindpass="adminpassword" \
url="ldap://localhost:1389" \
userdn="ou=users,dc=example,dc=org" \
groupdn="ou=users,dc=example,dc=org" \
userattr="uid" \
request_timeout=3s
Success! Data written to: auth/ldap/config

Valid credentials

➜  vault git:(VAULT-18227/introduce-cap-ldap-library) vault login -method=ldap username=alice
Password (will be hidden):
Success! You are now authenticated...

Wrong username

➜  vault git:(VAULT-18227/introduce-cap-ldap-library) vault login -method=ldap username=aliced
Password (will be hidden):
Error authenticating: Error making API request.

URL: PUT http://127.0.0.1:8200/v1/auth/ldap/login/aliced
Code: 400. Errors:

* ldap operation failed: failed to bind as user

Wrong Password

➜  vault git:(VAULT-18227/introduce-cap-ldap-library) vault login -method=ldap username=alice
Password (will be hidden):
Error authenticating: Error making API request.

URL: PUT http://127.0.0.1:8200/v1/auth/ldap/login/alice
Code: 400. Errors:

* ldap operation failed: failed to bind as user

@github-actions github-actions bot added the hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed label Aug 2, 2023
@raymonstah raymonstah added this to the 1.15 milestone Aug 2, 2023
@github-actions
Copy link

github-actions bot commented Aug 2, 2023

CI Results:
All Go tests succeeded! ✅

@@ -14,7 +14,7 @@ import (
// but through an interface.
type Connection interface {
Bind(username, password string) error
Close()
Close() error
Copy link
Contributor Author

@raymonstah raymonstah Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed as a result of updating the go-ldap/ldap library to v3.4.5 when cap/ldap was installed.

@@ -53,9 +53,6 @@ to search and change entry passwords in LDAP.
string for authentication. The constructed UPN will appear as `[binddn]@[upndomain]`. For
example, if `upndomain=example.com` and `binddn=admin`, the UPN string `[email protected]`
will be used to log in to Active Directory.
- `connection_timeout` `(integer: 30 or string: "30s")` - Timeout, in seconds,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to Deprecated Parameters and added a note.

"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/ldaputil"
)

func PrepareTestContainer(t *testing.T, version string) (cleanup func(), cfg *ldaputil.ConfigEntry) {
// Skipping on ARM, as this image can't run on ARM architecture
if strings.Contains(runtime.GOARCH, "arm") {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to run these tests locally on my Mac M1, but if this causes test failures I'll revert these changes.

Comment on lines -209 to -213
entityAliasAttribute, err := ldapClient.GetUserAliasAttributeValue(cfg.ConfigEntry, c, username)
if err != nil {
return "", nil, logical.ErrorResponse(err.Error()), nil, nil
}
if entityAliasAttribute == "" {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of this can be found here.

I believe it boils down to finding the 'CN' (common name) attribute.


// Clean connection
defer c.Close()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the code deleted below is implemented via the Authenticate method found in cap/ldap.

We do need to specify options to also get groups and attributes.

@@ -76,82 +77,25 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
return "", nil, logical.ErrorResponse("password cannot be of zero length when passwordless binds are being denied"), nil, nil
}

ldapClient := ldaputil.Client{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋🏼

Comment on lines +90 to +91
if strings.Contains(err.Error(), "discovery of user bind DN failed") ||
strings.Contains(err.Error(), "unable to bind user") {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little manual, as the cap/ldap library doesn't have any exported error types we can leverage.

@raymonstah raymonstah marked this pull request as ready for review August 3, 2023 18:52
@raymonstah raymonstah requested a review from a team as a code owner August 3, 2023 18:52
@raymonstah raymonstah requested review from a team and jasonodonnell August 3, 2023 18:52
@github-actions
Copy link

github-actions bot commented Aug 3, 2023

Build Results:
All builds succeeded! ✅

@raymonstah raymonstah requested a review from jimlambrt August 3, 2023 19:10
@jimlambrt
Copy link
Contributor

I'm happy to see cap/ldap get adopted by Vault! I just have one question/suggestion, but otherwise it looks good.

return "", nil, logical.ErrorResponse(err.Error()), nil, nil
}
if entityAliasAttribute == "" {
cn := c.UserAttributes["cn"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is quite correct. It appears that
Client.GetUserAliasAttributeValue(...) retrieves cfg.UserAttr which could be
really any attribute name (of course it's typically cn or uid). So perhaps
this should be something like userAttrValues := c.UserAttributes[cfg.UserAttr]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, thanks for catching this!

Copy link
Contributor

@austingebauer austingebauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on, @raymonstah! Looking good. Left a few questions/suggestions.

return "", nil, logical.ErrorResponse(err.Error()), nil, nil
}
if entityAliasAttribute == "" {
userAttrValues := c.UserAttributes[cfg.UserAttr]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UUIC c.UserAttributes[cfg.UserAttr] is set from cap/ldap getUserBindDN()? Would be worth double-checking that the cap/ldap implementation would result in the same entity alias as the prior code in GetUserAliasAttributeValue(). We've have entity alias changes result in privilege escalation in the past so worth a look!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it seems like the cap/ldap implementation doesn't apply the userfilter but I'm not sure if that would matter since the userfilter would've already been applied from the search to get the user bind dn.

But based on the existing client,

cfg.UserAttr, // Return only needed attributes
it does like it uses cfg.UserAttr as well.

StartTLS: cfg.StartTLS,
BindDN: cfg.BindDN,
BindPassword: cfg.BindPassword,
AllowEmptyPasswordBinds: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if this should be cfg.DenyNullBind instead of true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I think that's correct. or more accurately, !cfg.DenyNullBind. Updated!

sdk/helper/ldaputil/config.go Show resolved Hide resolved
@raymonstah
Copy link
Contributor Author

Closing, until pagination gets added to cap/ldap.

@raymonstah raymonstah reopened this Sep 12, 2023
@raymonstah raymonstah requested a review from a team as a code owner September 12, 2023 00:09
Copy link
Contributor

@austingebauer austingebauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM given that our tests are passing. Thanks for following up on this @raymonstah!

@@ -24,6 +24,7 @@ import (
"github.com/hashicorp/go-secure-stdlib/tlsutil"
)

// Deprecated: Use ldap.Client instead
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking we'll want to leave this as not deprecated. It's still used in a couple of different plugins (openldap example) for secrets engine use cases. I don't think cap/ldap will ever replace the usage given that it's targeted at authentication.

@raymonstah raymonstah modified the milestones: 1.15.0-rc1, 1.16 Sep 12, 2023
Copy link
Contributor

@jimlambrt jimlambrt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor bit about go.sum

sdk/go.mod Show resolved Hide resolved
website/content/api-docs/secret/ldap.mdx Outdated Show resolved Hide resolved
Copy link
Contributor

@jimlambrt jimlambrt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ty!

@raymonstah raymonstah requested a review from schavis September 13, 2023 21:07
@raymonstah raymonstah merged commit 018e567 into main Sep 14, 2023
106 checks passed
@raymonstah raymonstah deleted the VAULT-18227/introduce-cap-ldap-library branch September 14, 2023 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants