diff --git a/builtin/credential/ldap/backend_test.go b/builtin/credential/ldap/backend_test.go index 820f117a0f..74dfdf99ed 100644 --- a/builtin/credential/ldap/backend_test.go +++ b/builtin/credential/ldap/backend_test.go @@ -1194,6 +1194,7 @@ func TestLdapAuthBackend_ConfigUpgrade(t *testing.T) { "token_period": "5m", "token_explicit_max_ttl": "24h", "request_timeout": cfg.RequestTimeout, + "max_page_size": cfg.MaximumPageSize, "connection_timeout": cfg.ConnectionTimeout, }, Storage: storage, @@ -1238,6 +1239,7 @@ func TestLdapAuthBackend_ConfigUpgrade(t *testing.T) { RequestTimeout: cfg.RequestTimeout, ConnectionTimeout: cfg.ConnectionTimeout, UsernameAsAlias: false, + MaximumPageSize: 1000, }, } diff --git a/changelog/19032.txt b/changelog/19032.txt new file mode 100644 index 0000000000..a474c22ce6 --- /dev/null +++ b/changelog/19032.txt @@ -0,0 +1,3 @@ +```release-note:bug +auth/ldap: Add max_page_size configurable to LDAP configuration +``` diff --git a/helper/testhelpers/ldap/ldaphelper.go b/helper/testhelpers/ldap/ldaphelper.go index b248c0294f..79587ec6d9 100644 --- a/helper/testhelpers/ldap/ldaphelper.go +++ b/helper/testhelpers/ldap/ldaphelper.go @@ -33,6 +33,7 @@ func PrepareTestContainer(t *testing.T, version string) (cleanup func(), cfg *ld cfg.GroupDN = "ou=people,dc=planetexpress,dc=com" cfg.GroupAttr = "cn" cfg.RequestTimeout = 60 + cfg.MaximumPageSize = 1000 svc, err := runner.StartService(context.Background(), func(ctx context.Context, host string, port int) (docker.ServiceConfig, error) { connURL := fmt.Sprintf("ldap://%s:%d", host, port) diff --git a/sdk/helper/ldaputil/client.go b/sdk/helper/ldaputil/client.go index 220a84c0a6..0633cfe21c 100644 --- a/sdk/helper/ldaputil/client.go +++ b/sdk/helper/ldaputil/client.go @@ -417,7 +417,7 @@ func (c *Client) performLdapFilterGroupsSearchPaging(cfg *ConfigEntry, conn Pagi cfg.GroupAttr, }, SizeLimit: math.MaxInt32, - }, math.MaxInt32) + }, uint32(cfg.MaximumPageSize)) if err != nil { return nil, fmt.Errorf("LDAP search failed: %w", err) } @@ -536,7 +536,7 @@ func (c *Client) GetLdapGroups(cfg *ConfigEntry, conn Connection, userDN string, if cfg.UseTokenGroups { entries, err = c.performLdapTokenGroupsSearch(cfg, conn, userDN) } else { - if paging, ok := conn.(PagingConnection); ok { + if paging, ok := conn.(PagingConnection); ok && cfg.MaximumPageSize >= 0 { entries, err = c.performLdapFilterGroupsSearchPaging(cfg, paging, userDN, username) } else { entries, err = c.performLdapFilterGroupsSearch(cfg, conn, userDN, username) diff --git a/sdk/helper/ldaputil/config.go b/sdk/helper/ldaputil/config.go index e860893f7b..80d5c55ea6 100644 --- a/sdk/helper/ldaputil/config.go +++ b/sdk/helper/ldaputil/config.go @@ -6,6 +6,7 @@ import ( "encoding/pem" "errors" "fmt" + "math" "strings" "text/template" @@ -232,6 +233,12 @@ Default: ({{.UserAttr}}={{.Username}})`, Description: "Timeout, in seconds, when attempting to connect to the LDAP server before trying the next URL in the configuration.", Default: "30s", }, + + "max_page_size": { + Type: framework.TypeInt, + Description: "The maximum number of results to return for a single paged query. If not set, the server default will be used for paged searches. A requested max_page_size of 0 is interpreted as no limit by LDAP servers. If set to a negative value, search requests will not be paged.", + Default: math.MaxInt32, + }, } } @@ -402,6 +409,10 @@ func NewConfigEntry(existing *ConfigEntry, d *framework.FieldData) (*ConfigEntry cfg.ConnectionTimeout = d.Get("connection_timeout").(int) } + if _, ok := d.Raw["max_page_size"]; ok || !hadExisting { + cfg.MaximumPageSize = d.Get("max_page_size").(int) + } + return cfg, nil } @@ -429,6 +440,7 @@ type ConfigEntry struct { UsePre111GroupCNBehavior *bool `json:"use_pre111_group_cn_behavior"` RequestTimeout int `json:"request_timeout"` ConnectionTimeout int `json:"connection_timeout"` + MaximumPageSize int `json:"max_page_size"` // 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. @@ -468,6 +480,7 @@ func (c *ConfigEntry) PasswordlessMap() map[string]interface{} { "request_timeout": c.RequestTimeout, "connection_timeout": c.ConnectionTimeout, "username_as_alias": c.UsernameAsAlias, + "max_page_size": c.MaximumPageSize, } if c.CaseSensitiveNames != nil { m["case_sensitive_names"] = *c.CaseSensitiveNames diff --git a/sdk/helper/ldaputil/config_test.go b/sdk/helper/ldaputil/config_test.go index 4e02fb757c..3a169ef816 100644 --- a/sdk/helper/ldaputil/config_test.go +++ b/sdk/helper/ldaputil/config_test.go @@ -171,6 +171,7 @@ var jsonConfigDefault = []byte(` "username_as_alias": false, "request_timeout": 90, "connection_timeout": 30, + "max_page_size": 2147483647, "CaseSensitiveNames": false, "ClientTLSCert": "", "ClientTLSKey": "" diff --git a/website/content/api-docs/auth/ldap.mdx b/website/content/api-docs/auth/ldap.mdx index 41225e220b..808b6ecc13 100644 --- a/website/content/api-docs/auth/ldap.mdx +++ b/website/content/api-docs/auth/ldap.mdx @@ -35,8 +35,8 @@ This endpoint configures the LDAP auth method. names will be normalized to lower case. Case will still be preserved when sending the username to the LDAP server at login time; this is only for matching local user/group definitions. -- `connection_timeout` `(integer: 30 or string: "30s")` - Timeout, in seconds, - when attempting to connect to the LDAP server before trying the next URL in +- `connection_timeout` `(integer: 30 or string: "30s")` - Timeout, in seconds, + when attempting to connect to the LDAP server before trying the next URL in the configuration. - `request_timeout` `(integer: 90 or string: "90s")` - Timeout, in seconds, for the connection when making requests against the server before returning back @@ -94,6 +94,12 @@ 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. +- `max_page_size` `(int: math.MaxInt32)` - If set to a value greater than 0, the LDAP + backend will use the LDAP server's paged search control to request pages of + up to the given size. This can be used to avoid hitting the LDAP server's + maximum result size limit. A value of 0 will be interpreted by the LDAP + server as unlimited. If set to -1, the LDAP backend will not use the + paged search control. @include 'tokenfields.mdx' @@ -126,7 +132,8 @@ $ curl \ "url": "ldaps://ldap.myorg.com:636", "username_as_alias": false, "userattr": "samaccountname", - "userdn": "ou=Users,dc=example,dc=com" + "userdn": "ou=Users,dc=example,dc=com", + "max_page_size": 1000 } ``` diff --git a/website/content/docs/auth/ldap.mdx b/website/content/docs/auth/ldap.mdx index 82641bf3d5..3ae5000403 100644 --- a/website/content/docs/auth/ldap.mdx +++ b/website/content/docs/auth/ldap.mdx @@ -152,7 +152,7 @@ Use `vault path-help` for more details. ### Other - `username_as_alias` (bool, optional) - If set to true, forces the auth method to use the username passed by the user as the alias name. - +- `max_page_size` (int, optional) - The maximum number of results to return for a single LDAP query. This is useful for preventing large queries from being run against the LDAP server. The default is the maximum value for an int32. ## Examples: