From d931e8d542916ff8f1cbd1ddff67bcf596326b30 Mon Sep 17 00:00:00 2001 From: Jakob Beckmann Date: Mon, 5 Dec 2022 17:25:12 +0100 Subject: [PATCH 1/7] impr(auth/ldap): allow to dereference aliases in searches --- builtin/credential/ldap/backend_test.go | 1 + sdk/helper/ldaputil/client.go | 6 ++++++ sdk/helper/ldaputil/config.go | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/builtin/credential/ldap/backend_test.go b/builtin/credential/ldap/backend_test.go index 74b4e18a17e3..3ad9066129e2 100644 --- a/builtin/credential/ldap/backend_test.go +++ b/builtin/credential/ldap/backend_test.go @@ -1231,6 +1231,7 @@ func TestLdapAuthBackend_ConfigUpgrade(t *testing.T) { UsePre111GroupCNBehavior: new(bool), RequestTimeout: cfg.RequestTimeout, UsernameAsAlias: false, + DerefAliases: "never", }, } diff --git a/sdk/helper/ldaputil/client.go b/sdk/helper/ldaputil/client.go index 8a7ac4822c34..770671b5c9df 100644 --- a/sdk/helper/ldaputil/client.go +++ b/sdk/helper/ldaputil/client.go @@ -120,6 +120,7 @@ func (c *Client) makeLdapSearchRequest(cfg *ConfigEntry, conn Connection, userna } ldapRequest := &ldap.SearchRequest{ BaseDN: cfg.UserDN, + DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], Scope: ldap.ScopeWholeSubtree, Filter: renderedFilter, SizeLimit: 2, // Should be only 1 result. Any number larger (2 or more) means access denied. @@ -276,6 +277,7 @@ func (c *Client) GetUserDN(cfg *ConfigEntry, conn Connection, bindDN, username s result, err := conn.Search(&ldap.SearchRequest{ BaseDN: cfg.UserDN, Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], Filter: filter, SizeLimit: math.MaxInt32, }) @@ -337,6 +339,7 @@ func (c *Client) performLdapFilterGroupsSearch(cfg *ConfigEntry, conn Connection result, err := conn.Search(&ldap.SearchRequest{ BaseDN: cfg.GroupDN, Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], Filter: renderedQuery.String(), Attributes: []string{ cfg.GroupAttr, @@ -395,6 +398,7 @@ func (c *Client) performLdapFilterGroupsSearchPaging(cfg *ConfigEntry, conn Pagi result, err := conn.SearchWithPaging(&ldap.SearchRequest{ BaseDN: cfg.GroupDN, Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], Filter: renderedQuery.String(), Attributes: []string{ cfg.GroupAttr, @@ -444,6 +448,7 @@ func (c *Client) performLdapTokenGroupsSearch(cfg *ConfigEntry, conn Connection, result, err := conn.Search(&ldap.SearchRequest{ BaseDN: userDN, Scope: ldap.ScopeBaseObject, + DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], Filter: "(objectClass=*)", Attributes: []string{ "tokenGroups", @@ -472,6 +477,7 @@ func (c *Client) performLdapTokenGroupsSearch(cfg *ConfigEntry, conn Connection, groupResult, err := conn.Search(&ldap.SearchRequest{ BaseDN: fmt.Sprintf("", sidString), Scope: ldap.ScopeBaseObject, + DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], Filter: "(objectClass=*)", Attributes: []string{ "1.1", // RFC no attributes diff --git a/sdk/helper/ldaputil/config.go b/sdk/helper/ldaputil/config.go index 43844da22b13..8da5f2225a9d 100644 --- a/sdk/helper/ldaputil/config.go +++ b/sdk/helper/ldaputil/config.go @@ -13,8 +13,17 @@ import ( "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/errwrap" + + "github.com/go-ldap/ldap/v3" ) +var ldapDerefAliasMap = map[string]int{ + "never": ldap.NeverDerefAliases, + "finding": ldap.DerefFindingBaseObj, + "searching": ldap.DerefInSearching, + "always": ldap.DerefAlways, +} + // ConfigFields returns all the config fields that can potentially be used by the LDAP client. // Not all fields will be used by every integration. func ConfigFields() map[string]*framework.FieldSchema { @@ -226,6 +235,13 @@ Default: ({{.UserAttr}}={{.Username}})`, Description: "Timeout, in seconds, for the connection when making requests against the server before returning back an error.", Default: "90s", }, + + "dereference_aliases": { + Type: framework.TypeString, + Description: "When aliases should be dereferenced on search operations. Accepted values are 'never', 'finding', 'searching', 'always'. Defaults to 'never'.", + Default: "never", + AllowedValues: []interface{}{"never", "finding", "searching", "always"}, + }, } } @@ -392,6 +408,10 @@ func NewConfigEntry(existing *ConfigEntry, d *framework.FieldData) (*ConfigEntry cfg.RequestTimeout = d.Get("request_timeout").(int) } + if _, ok := d.Raw["dereference_aliases"]; ok || !hadExisting { + cfg.DerefAliases = d.Get("dereference_aliases").(string) + } + return cfg, nil } @@ -418,6 +438,7 @@ type ConfigEntry struct { UseTokenGroups bool `json:"use_token_groups"` UsePre111GroupCNBehavior *bool `json:"use_pre111_group_cn_behavior"` RequestTimeout int `json:"request_timeout"` + DerefAliases string `json:"dereference_aliases"` // These json tags deviate from snake case because there was a past issue // where the tag was being ignored, causing it to be jsonified as "CaseSensitiveNames", etc. @@ -456,6 +477,7 @@ func (c *ConfigEntry) PasswordlessMap() map[string]interface{} { "anonymous_group_search": c.AnonymousGroupSearch, "request_timeout": c.RequestTimeout, "username_as_alias": c.UsernameAsAlias, + "dereference_aliases": c.DerefAliases, } if c.CaseSensitiveNames != nil { m["case_sensitive_names"] = *c.CaseSensitiveNames From 79dd9db1d50c6eec1e00ef456e7910a72f900d23 Mon Sep 17 00:00:00 2001 From: Jakob Beckmann Date: Mon, 5 Dec 2022 20:24:27 +0100 Subject: [PATCH 2/7] docs: add documentation for LDAP alias dereferencing --- website/content/docs/auth/ldap.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/content/docs/auth/ldap.mdx b/website/content/docs/auth/ldap.mdx index 2a9573df6cda..738b41357719 100644 --- a/website/content/docs/auth/ldap.mdx +++ b/website/content/docs/auth/ldap.mdx @@ -133,6 +133,10 @@ There are two alternate methods of resolving the user object used to authenticat @include 'ldap-auth-userfilter-warning.mdx' +#### Alias Dereferencing + +- `dereference_aliases` (string, optional) - When to dereference aliases when performing the search. Possible values are: `never`, `finding`, `searching`, and `always`. `finding` will only dereference aliases during name resolution of the base. `searching` will dereference aliases after name resolution. + #### Binding - User Principal Name (AD) - `upndomain` (string, optional) - userPrincipalDomain used to construct the UPN string for the authenticating user. The constructed UPN will appear as `[username]@UPNDomain`. Example: `example.com`, which will cause vault to bind as `username@example.com`. From 78d09933dad2a33be6ac0b2149b9eb0435450cc9 Mon Sep 17 00:00:00 2001 From: Jakob Beckmann Date: Mon, 5 Dec 2022 20:28:04 +0100 Subject: [PATCH 3/7] chore(auth/ldap): add changelog entry for PR 18230 --- changelog/18230.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/18230.txt diff --git a/changelog/18230.txt b/changelog/18230.txt new file mode 100644 index 000000000000..335f9670db2a --- /dev/null +++ b/changelog/18230.txt @@ -0,0 +1,3 @@ +```release-note:improvement +auth/ldap: allow configuration of alias dereferencing in LDAP search +``` From 861c73f31e7ba471fcdd003185168b631fe3e7ec Mon Sep 17 00:00:00 2001 From: Jakob Beckmann Date: Mon, 5 Dec 2022 20:29:55 +0100 Subject: [PATCH 4/7] chore: run formatter --- sdk/helper/ldaputil/client.go | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/sdk/helper/ldaputil/client.go b/sdk/helper/ldaputil/client.go index 770671b5c9df..fab55488faa1 100644 --- a/sdk/helper/ldaputil/client.go +++ b/sdk/helper/ldaputil/client.go @@ -119,11 +119,11 @@ func (c *Client) makeLdapSearchRequest(cfg *ConfigEntry, conn Connection, userna c.Logger.Debug("discovering user", "userdn", cfg.UserDN, "filter", renderedFilter) } ldapRequest := &ldap.SearchRequest{ - BaseDN: cfg.UserDN, + BaseDN: cfg.UserDN, DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], - Scope: ldap.ScopeWholeSubtree, - Filter: renderedFilter, - SizeLimit: 2, // Should be only 1 result. Any number larger (2 or more) means access denied. + Scope: ldap.ScopeWholeSubtree, + Filter: renderedFilter, + SizeLimit: 2, // Should be only 1 result. Any number larger (2 or more) means access denied. Attributes: []string{ cfg.UserAttr, // Return only needed attributes }, @@ -275,11 +275,11 @@ func (c *Client) GetUserDN(cfg *ConfigEntry, conn Connection, bindDN, username s c.Logger.Debug("searching upn", "userdn", cfg.UserDN, "filter", filter) } result, err := conn.Search(&ldap.SearchRequest{ - BaseDN: cfg.UserDN, - Scope: ldap.ScopeWholeSubtree, + BaseDN: cfg.UserDN, + Scope: ldap.ScopeWholeSubtree, DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], - Filter: filter, - SizeLimit: math.MaxInt32, + Filter: filter, + SizeLimit: math.MaxInt32, }) if err != nil { return userDN, fmt.Errorf("LDAP search failed for detecting user: %w", err) @@ -337,10 +337,10 @@ func (c *Client) performLdapFilterGroupsSearch(cfg *ConfigEntry, conn Connection } result, err := conn.Search(&ldap.SearchRequest{ - BaseDN: cfg.GroupDN, - Scope: ldap.ScopeWholeSubtree, + BaseDN: cfg.GroupDN, + Scope: ldap.ScopeWholeSubtree, DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], - Filter: renderedQuery.String(), + Filter: renderedQuery.String(), Attributes: []string{ cfg.GroupAttr, }, @@ -396,10 +396,10 @@ func (c *Client) performLdapFilterGroupsSearchPaging(cfg *ConfigEntry, conn Pagi } result, err := conn.SearchWithPaging(&ldap.SearchRequest{ - BaseDN: cfg.GroupDN, - Scope: ldap.ScopeWholeSubtree, + BaseDN: cfg.GroupDN, + Scope: ldap.ScopeWholeSubtree, DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], - Filter: renderedQuery.String(), + Filter: renderedQuery.String(), Attributes: []string{ cfg.GroupAttr, }, @@ -446,10 +446,10 @@ func sidBytesToString(b []byte) (string, error) { func (c *Client) performLdapTokenGroupsSearch(cfg *ConfigEntry, conn Connection, userDN string) ([]*ldap.Entry, error) { result, err := conn.Search(&ldap.SearchRequest{ - BaseDN: userDN, - Scope: ldap.ScopeBaseObject, + BaseDN: userDN, + Scope: ldap.ScopeBaseObject, DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], - Filter: "(objectClass=*)", + Filter: "(objectClass=*)", Attributes: []string{ "tokenGroups", }, @@ -475,10 +475,10 @@ func (c *Client) performLdapTokenGroupsSearch(cfg *ConfigEntry, conn Connection, } groupResult, err := conn.Search(&ldap.SearchRequest{ - BaseDN: fmt.Sprintf("", sidString), - Scope: ldap.ScopeBaseObject, + BaseDN: fmt.Sprintf("", sidString), + Scope: ldap.ScopeBaseObject, DerefAliases: ldapDerefAliasMap[cfg.DerefAliases], - Filter: "(objectClass=*)", + Filter: "(objectClass=*)", Attributes: []string{ "1.1", // RFC no attributes }, From c106447b266f0c5e12f7b8a8a90dff293adf146a Mon Sep 17 00:00:00 2001 From: Jakob Beckmann Date: Thu, 8 Dec 2022 14:56:41 +0100 Subject: [PATCH 5/7] fix: update default LDAP configuration with new default --- sdk/helper/ldaputil/config_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/helper/ldaputil/config_test.go b/sdk/helper/ldaputil/config_test.go index 32edb5dffaad..fb651345035d 100644 --- a/sdk/helper/ldaputil/config_test.go +++ b/sdk/helper/ldaputil/config_test.go @@ -168,6 +168,7 @@ var jsonConfigDefault = []byte(` "use_pre111_group_cn_behavior": null, "username_as_alias": false, "request_timeout": 90, + "dereference_aliases": "never", "CaseSensitiveNames": false, "ClientTLSCert": "", "ClientTLSKey": "" From 5eb8a404a8a16c3fa026382481b53dac3ef3ef42 Mon Sep 17 00:00:00 2001 From: Jakob Beckmann <32326425+f4z3r@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:15:28 +0100 Subject: [PATCH 6/7] Update website/content/docs/auth/ldap.mdx Co-authored-by: tjperry07 --- website/content/docs/auth/ldap.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/content/docs/auth/ldap.mdx b/website/content/docs/auth/ldap.mdx index 738b41357719..eb8090b47e9b 100644 --- a/website/content/docs/auth/ldap.mdx +++ b/website/content/docs/auth/ldap.mdx @@ -135,7 +135,7 @@ There are two alternate methods of resolving the user object used to authenticat #### Alias Dereferencing -- `dereference_aliases` (string, optional) - When to dereference aliases when performing the search. Possible values are: `never`, `finding`, `searching`, and `always`. `finding` will only dereference aliases during name resolution of the base. `searching` will dereference aliases after name resolution. +- `dereference_aliases` (string, optional) - Control how aliases are dereferenced when performing the search. Possible values are: `never`, `finding`, `searching`, and `always`. `finding` will only dereference aliases during name resolution of the base. `searching` will dereference aliases after name resolution. #### Binding - User Principal Name (AD) From 47d155ab2e332e6329b601a1f34bb1c99682352f Mon Sep 17 00:00:00 2001 From: Jakob Beckmann Date: Thu, 23 Feb 2023 18:08:32 +0100 Subject: [PATCH 7/7] docs(ldap): add alias dereferencing to API docs for LDAP --- website/content/api-docs/auth/ldap.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/content/api-docs/auth/ldap.mdx b/website/content/api-docs/auth/ldap.mdx index e28d0292fb0a..eb2d3023823d 100644 --- a/website/content/api-docs/auth/ldap.mdx +++ b/website/content/api-docs/auth/ldap.mdx @@ -91,6 +91,9 @@ This endpoint configures the LDAP auth method. returning _user_ objects, use: `memberOf`. The default is `cn`. - `username_as_alias` `(bool: false)` - If set to true, forces the auth method to use the username passed by the user as the alias name. +- `dereference_aliases` `(string: never)` - When aliases should be dereferenced + on search operations. Accepted values are 'never', 'finding', 'searching', + 'always'. Defaults to 'never'. @include 'tokenfields.mdx'