Allow alias dereferencing in LDAP searches (#18230)

* impr(auth/ldap): allow to dereference aliases in searches

* docs: add documentation for LDAP alias dereferencing

* chore(auth/ldap): add changelog entry for PR 18230

* chore: run formatter

* fix: update default LDAP configuration with new default

* Update website/content/docs/auth/ldap.mdx

Co-authored-by: tjperry07 <tjperry07@users.noreply.github.com>

* docs(ldap): add alias dereferencing to API docs for LDAP

---------

Co-authored-by: tjperry07 <tjperry07@users.noreply.github.com>
This commit is contained in:
Jakob Beckmann
2023-02-24 19:49:17 +01:00
committed by GitHub
parent 3adb416da1
commit 39f9e5e775
7 changed files with 60 additions and 20 deletions

View File

@@ -1231,6 +1231,7 @@ func TestLdapAuthBackend_ConfigUpgrade(t *testing.T) {
UsePre111GroupCNBehavior: new(bool), UsePre111GroupCNBehavior: new(bool),
RequestTimeout: cfg.RequestTimeout, RequestTimeout: cfg.RequestTimeout,
UsernameAsAlias: false, UsernameAsAlias: false,
DerefAliases: "never",
}, },
} }

3
changelog/18230.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
auth/ldap: allow configuration of alias dereferencing in LDAP search
```

View File

@@ -120,6 +120,7 @@ func (c *Client) makeLdapSearchRequest(cfg *ConfigEntry, conn Connection, userna
} }
ldapRequest := &ldap.SearchRequest{ ldapRequest := &ldap.SearchRequest{
BaseDN: cfg.UserDN, BaseDN: cfg.UserDN,
DerefAliases: ldapDerefAliasMap[cfg.DerefAliases],
Scope: ldap.ScopeWholeSubtree, Scope: ldap.ScopeWholeSubtree,
Filter: renderedFilter, Filter: renderedFilter,
SizeLimit: 2, // Should be only 1 result. Any number larger (2 or more) means access denied. 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{ result, err := conn.Search(&ldap.SearchRequest{
BaseDN: cfg.UserDN, BaseDN: cfg.UserDN,
Scope: ldap.ScopeWholeSubtree, Scope: ldap.ScopeWholeSubtree,
DerefAliases: ldapDerefAliasMap[cfg.DerefAliases],
Filter: filter, Filter: filter,
SizeLimit: math.MaxInt32, SizeLimit: math.MaxInt32,
}) })
@@ -337,6 +339,7 @@ func (c *Client) performLdapFilterGroupsSearch(cfg *ConfigEntry, conn Connection
result, err := conn.Search(&ldap.SearchRequest{ result, err := conn.Search(&ldap.SearchRequest{
BaseDN: cfg.GroupDN, BaseDN: cfg.GroupDN,
Scope: ldap.ScopeWholeSubtree, Scope: ldap.ScopeWholeSubtree,
DerefAliases: ldapDerefAliasMap[cfg.DerefAliases],
Filter: renderedQuery.String(), Filter: renderedQuery.String(),
Attributes: []string{ Attributes: []string{
cfg.GroupAttr, cfg.GroupAttr,
@@ -395,6 +398,7 @@ func (c *Client) performLdapFilterGroupsSearchPaging(cfg *ConfigEntry, conn Pagi
result, err := conn.SearchWithPaging(&ldap.SearchRequest{ result, err := conn.SearchWithPaging(&ldap.SearchRequest{
BaseDN: cfg.GroupDN, BaseDN: cfg.GroupDN,
Scope: ldap.ScopeWholeSubtree, Scope: ldap.ScopeWholeSubtree,
DerefAliases: ldapDerefAliasMap[cfg.DerefAliases],
Filter: renderedQuery.String(), Filter: renderedQuery.String(),
Attributes: []string{ Attributes: []string{
cfg.GroupAttr, cfg.GroupAttr,
@@ -444,6 +448,7 @@ func (c *Client) performLdapTokenGroupsSearch(cfg *ConfigEntry, conn Connection,
result, err := conn.Search(&ldap.SearchRequest{ result, err := conn.Search(&ldap.SearchRequest{
BaseDN: userDN, BaseDN: userDN,
Scope: ldap.ScopeBaseObject, Scope: ldap.ScopeBaseObject,
DerefAliases: ldapDerefAliasMap[cfg.DerefAliases],
Filter: "(objectClass=*)", Filter: "(objectClass=*)",
Attributes: []string{ Attributes: []string{
"tokenGroups", "tokenGroups",
@@ -472,6 +477,7 @@ func (c *Client) performLdapTokenGroupsSearch(cfg *ConfigEntry, conn Connection,
groupResult, err := conn.Search(&ldap.SearchRequest{ groupResult, err := conn.Search(&ldap.SearchRequest{
BaseDN: fmt.Sprintf("<SID=%s>", sidString), BaseDN: fmt.Sprintf("<SID=%s>", sidString),
Scope: ldap.ScopeBaseObject, Scope: ldap.ScopeBaseObject,
DerefAliases: ldapDerefAliasMap[cfg.DerefAliases],
Filter: "(objectClass=*)", Filter: "(objectClass=*)",
Attributes: []string{ Attributes: []string{
"1.1", // RFC no attributes "1.1", // RFC no attributes

View File

@@ -13,8 +13,17 @@ import (
"github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/errwrap" "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. // ConfigFields returns all the config fields that can potentially be used by the LDAP client.
// Not all fields will be used by every integration. // Not all fields will be used by every integration.
func ConfigFields() map[string]*framework.FieldSchema { 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.", Description: "Timeout, in seconds, for the connection when making requests against the server before returning back an error.",
Default: "90s", 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) 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 return cfg, nil
} }
@@ -418,6 +438,7 @@ type ConfigEntry struct {
UseTokenGroups bool `json:"use_token_groups"` UseTokenGroups bool `json:"use_token_groups"`
UsePre111GroupCNBehavior *bool `json:"use_pre111_group_cn_behavior"` UsePre111GroupCNBehavior *bool `json:"use_pre111_group_cn_behavior"`
RequestTimeout int `json:"request_timeout"` RequestTimeout int `json:"request_timeout"`
DerefAliases string `json:"dereference_aliases"`
// These json tags deviate from snake case because there was a past issue // 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. // 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, "anonymous_group_search": c.AnonymousGroupSearch,
"request_timeout": c.RequestTimeout, "request_timeout": c.RequestTimeout,
"username_as_alias": c.UsernameAsAlias, "username_as_alias": c.UsernameAsAlias,
"dereference_aliases": c.DerefAliases,
} }
if c.CaseSensitiveNames != nil { if c.CaseSensitiveNames != nil {
m["case_sensitive_names"] = *c.CaseSensitiveNames m["case_sensitive_names"] = *c.CaseSensitiveNames

View File

@@ -168,6 +168,7 @@ var jsonConfigDefault = []byte(`
"use_pre111_group_cn_behavior": null, "use_pre111_group_cn_behavior": null,
"username_as_alias": false, "username_as_alias": false,
"request_timeout": 90, "request_timeout": 90,
"dereference_aliases": "never",
"CaseSensitiveNames": false, "CaseSensitiveNames": false,
"ClientTLSCert": "", "ClientTLSCert": "",
"ClientTLSKey": "" "ClientTLSKey": ""

View File

@@ -91,6 +91,9 @@ This endpoint configures the LDAP auth method.
returning _user_ objects, use: `memberOf`. The default is `cn`. returning _user_ objects, use: `memberOf`. The default is `cn`.
- `username_as_alias` `(bool: false)` - If set to true, forces the auth method - `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. 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' @include 'tokenfields.mdx'

View File

@@ -133,6 +133,10 @@ There are two alternate methods of resolving the user object used to authenticat
@include 'ldap-auth-userfilter-warning.mdx' @include 'ldap-auth-userfilter-warning.mdx'
#### Alias Dereferencing
- `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) #### 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`. - `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`.