mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
Identity LDAP: Allow multiple search base DNs (#14191)
This change allows the MinIO server to lookup users in different directory sub-trees by allowing specification of multiple search bases separated by semicolons.
This commit is contained in:
parent
d2e5f01542
commit
7dfa565d00
@ -540,7 +540,7 @@ func (c *SiteReplicationSys) GetIDPSettings(ctx context.Context) madmin.IDPSetti
|
|||||||
s := madmin.IDPSettings{}
|
s := madmin.IDPSettings{}
|
||||||
s.LDAP = madmin.LDAPSettings{
|
s.LDAP = madmin.LDAPSettings{
|
||||||
IsLDAPEnabled: globalLDAPConfig.Enabled,
|
IsLDAPEnabled: globalLDAPConfig.Enabled,
|
||||||
LDAPUserDNSearchBase: globalLDAPConfig.UserDNSearchBaseDN,
|
LDAPUserDNSearchBase: globalLDAPConfig.UserDNSearchBaseDistName,
|
||||||
LDAPUserDNSearchFilter: globalLDAPConfig.UserDNSearchFilter,
|
LDAPUserDNSearchFilter: globalLDAPConfig.UserDNSearchFilter,
|
||||||
LDAPGroupSearchBase: globalLDAPConfig.GroupSearchBaseDistName,
|
LDAPGroupSearchBase: globalLDAPConfig.GroupSearchBaseDistName,
|
||||||
LDAPGroupSearchFilter: globalLDAPConfig.GroupSearchFilter,
|
LDAPGroupSearchFilter: globalLDAPConfig.GroupSearchFilter,
|
||||||
|
@ -37,7 +37,7 @@ ARGS:
|
|||||||
MINIO_IDENTITY_LDAP_SERVER_ADDR* (address) AD/LDAP server address e.g. "myldapserver.com:636"
|
MINIO_IDENTITY_LDAP_SERVER_ADDR* (address) AD/LDAP server address e.g. "myldapserver.com:636"
|
||||||
MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN (string) DN for LDAP read-only service account used to perform DN and group lookups
|
MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN (string) DN for LDAP read-only service account used to perform DN and group lookups
|
||||||
MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD (string) Password for LDAP read-only service account used to perform DN and group lookups
|
MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD (string) Password for LDAP read-only service account used to perform DN and group lookups
|
||||||
MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN (string) Base LDAP DN to search for user DN
|
MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN (string) ";" separated list of user search base DNs e.g. "dc=myldapserver,dc=com"
|
||||||
MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER (string) Search filter to lookup user DN
|
MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER (string) Search filter to lookup user DN
|
||||||
MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER (string) search filter for groups e.g. "(&(objectclass=groupOfNames)(memberUid=%s))"
|
MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER (string) search filter for groups e.g. "(&(objectclass=groupOfNames)(memberUid=%s))"
|
||||||
MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN (list) ";" separated list of group search base DNs e.g. "dc=myldapserver,dc=com"
|
MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN (list) ";" separated list of group search base DNs e.g. "dc=myldapserver,dc=com"
|
||||||
|
@ -51,8 +51,9 @@ type Config struct {
|
|||||||
ServerAddr string `json:"serverAddr"`
|
ServerAddr string `json:"serverAddr"`
|
||||||
|
|
||||||
// User DN search parameters
|
// User DN search parameters
|
||||||
UserDNSearchBaseDN string `json:"userDNSearchBaseDN"`
|
UserDNSearchBaseDistName string `json:"userDNSearchBaseDN"`
|
||||||
UserDNSearchFilter string `json:"userDNSearchFilter"`
|
UserDNSearchBaseDistNames []string `json:"-"`
|
||||||
|
UserDNSearchFilter string `json:"userDNSearchFilter"`
|
||||||
|
|
||||||
// Group search parameters
|
// Group search parameters
|
||||||
GroupSearchBaseDistName string `json:"groupSearchBaseDN"`
|
GroupSearchBaseDistName string `json:"groupSearchBaseDN"`
|
||||||
@ -187,25 +188,32 @@ func (l *Config) lookupBind(conn *ldap.Conn) error {
|
|||||||
// search result in at most one result.
|
// search result in at most one result.
|
||||||
func (l *Config) lookupUserDN(conn *ldap.Conn, username string) (string, error) {
|
func (l *Config) lookupUserDN(conn *ldap.Conn, username string) (string, error) {
|
||||||
filter := strings.ReplaceAll(l.UserDNSearchFilter, "%s", ldap.EscapeFilter(username))
|
filter := strings.ReplaceAll(l.UserDNSearchFilter, "%s", ldap.EscapeFilter(username))
|
||||||
searchRequest := ldap.NewSearchRequest(
|
var foundDistNames []string
|
||||||
l.UserDNSearchBaseDN,
|
for _, userSearchBase := range l.UserDNSearchBaseDistNames {
|
||||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
searchRequest := ldap.NewSearchRequest(
|
||||||
filter,
|
userSearchBase,
|
||||||
[]string{}, // only need DN, so no pass no attributes here
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
nil,
|
filter,
|
||||||
)
|
[]string{}, // only need DN, so no pass no attributes here
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
searchResult, err := conn.Search(searchRequest)
|
searchResult, err := conn.Search(searchRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range searchResult.Entries {
|
||||||
|
foundDistNames = append(foundDistNames, entry.DN)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(searchResult.Entries) == 0 {
|
if len(foundDistNames) == 0 {
|
||||||
return "", fmt.Errorf("User DN for %s not found", username)
|
return "", fmt.Errorf("User DN for %s not found", username)
|
||||||
}
|
}
|
||||||
if len(searchResult.Entries) != 1 {
|
if len(foundDistNames) != 1 {
|
||||||
return "", fmt.Errorf("Multiple DNs for %s found - please fix the search filter", username)
|
return "", fmt.Errorf("Multiple DNs for %s found - please fix the search filter", username)
|
||||||
}
|
}
|
||||||
return searchResult.Entries[0].DN, nil
|
return foundDistNames[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Config) searchForUserGroups(conn *ldap.Conn, username, bindDN string) ([]string, error) {
|
func (l *Config) searchForUserGroups(conn *ldap.Conn, username, bindDN string) ([]string, error) {
|
||||||
@ -375,7 +383,12 @@ func (l Config) testConnection() error {
|
|||||||
|
|
||||||
// IsLDAPUserDN determines if the given string could be a user DN from LDAP.
|
// IsLDAPUserDN determines if the given string could be a user DN from LDAP.
|
||||||
func (l Config) IsLDAPUserDN(user string) bool {
|
func (l Config) IsLDAPUserDN(user string) bool {
|
||||||
return strings.HasSuffix(user, ","+l.UserDNSearchBaseDN)
|
for _, baseDN := range l.UserDNSearchBaseDistNames {
|
||||||
|
if strings.HasSuffix(user, ","+baseDN) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNonEligibleUserDistNames - find user accounts (DNs) that are no longer
|
// GetNonEligibleUserDistNames - find user accounts (DNs) that are no longer
|
||||||
@ -505,15 +518,6 @@ func Lookup(kvs config.KVS, rootCAs *x509.CertPool) (l Config, err error) {
|
|||||||
if lookupBindDN != "" {
|
if lookupBindDN != "" {
|
||||||
l.LookupBindDN = lookupBindDN
|
l.LookupBindDN = lookupBindDN
|
||||||
l.LookupBindPassword = lookupBindPassword
|
l.LookupBindPassword = lookupBindPassword
|
||||||
|
|
||||||
// User DN search configuration
|
|
||||||
userDNSearchBaseDN := env.Get(EnvUserDNSearchBaseDN, kvs.Get(UserDNSearchBaseDN))
|
|
||||||
userDNSearchFilter := env.Get(EnvUserDNSearchFilter, kvs.Get(UserDNSearchFilter))
|
|
||||||
if userDNSearchFilter == "" || userDNSearchBaseDN == "" {
|
|
||||||
return l, errors.New("In lookup bind mode, userDN search base DN and userDN search filter are both required")
|
|
||||||
}
|
|
||||||
l.UserDNSearchBaseDN = userDNSearchBaseDN
|
|
||||||
l.UserDNSearchFilter = userDNSearchFilter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test connection to LDAP server.
|
// Test connection to LDAP server.
|
||||||
@ -521,6 +525,16 @@ func Lookup(kvs config.KVS, rootCAs *x509.CertPool) (l Config, err error) {
|
|||||||
return l, fmt.Errorf("Connection test for LDAP server failed: %w", err)
|
return l, fmt.Errorf("Connection test for LDAP server failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User DN search configuration
|
||||||
|
userDNSearchBaseDN := env.Get(EnvUserDNSearchBaseDN, kvs.Get(UserDNSearchBaseDN))
|
||||||
|
userDNSearchFilter := env.Get(EnvUserDNSearchFilter, kvs.Get(UserDNSearchFilter))
|
||||||
|
if userDNSearchFilter == "" || userDNSearchBaseDN == "" {
|
||||||
|
return l, errors.New("UserDN search base DN and UserDN search filter are both required")
|
||||||
|
}
|
||||||
|
l.UserDNSearchBaseDistName = userDNSearchBaseDN
|
||||||
|
l.UserDNSearchBaseDistNames = strings.Split(userDNSearchBaseDN, dnDelimiter)
|
||||||
|
l.UserDNSearchFilter = userDNSearchFilter
|
||||||
|
|
||||||
// Group search params configuration
|
// Group search params configuration
|
||||||
grpSearchFilter := env.Get(EnvGroupSearchFilter, kvs.Get(GroupSearchFilter))
|
grpSearchFilter := env.Get(EnvGroupSearchFilter, kvs.Get(GroupSearchFilter))
|
||||||
grpSearchBaseDN := env.Get(EnvGroupSearchBaseDN, kvs.Get(GroupSearchBaseDN))
|
grpSearchBaseDN := env.Get(EnvGroupSearchBaseDN, kvs.Get(GroupSearchBaseDN))
|
||||||
|
@ -44,9 +44,9 @@ var (
|
|||||||
},
|
},
|
||||||
config.HelpKV{
|
config.HelpKV{
|
||||||
Key: UserDNSearchBaseDN,
|
Key: UserDNSearchBaseDN,
|
||||||
Description: `Base LDAP DN to search for user DN`,
|
Description: `";" separated list of user search base DNs e.g. "dc=myldapserver,dc=com"`,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Type: "string",
|
Type: "list",
|
||||||
},
|
},
|
||||||
config.HelpKV{
|
config.HelpKV{
|
||||||
Key: UserDNSearchFilter,
|
Key: UserDNSearchFilter,
|
||||||
|
Loading…
Reference in New Issue
Block a user