Skip to content

Commit 75ee729

Browse files
committed
Support multiple LDAP servers in a auth source (#6898)
Signed-off-by: abhishek818 <[email protected]>
1 parent de1a550 commit 75ee729

File tree

5 files changed

+49
-29
lines changed

5 files changed

+49
-29
lines changed

cmd/admin_auth_ldap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
207207
config.Name = c.String("name")
208208
}
209209
if c.IsSet("host") {
210-
config.Host = c.String("host")
210+
config.HostList = c.String("hostlist")
211211
}
212212
if c.IsSet("port") {
213213
config.Port = c.Int("port")

cmd/admin_auth_ldap_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func TestAddLdapBindDn(t *testing.T) {
5959
IsSyncEnabled: true,
6060
Cfg: &ldap.Source{
6161
Name: "ldap (via Bind DN) source full",
62-
Host: "ldap-bind-server full",
62+
HostList: "ldap-bind-server full",
6363
Port: 9876,
6464
SecurityProtocol: ldap.SecurityProtocol(1),
6565
SkipVerify: true,
@@ -99,7 +99,7 @@ func TestAddLdapBindDn(t *testing.T) {
9999
IsActive: true,
100100
Cfg: &ldap.Source{
101101
Name: "ldap (via Bind DN) source min",
102-
Host: "ldap-bind-server min",
102+
HostList: "ldap-bind-server min",
103103
Port: 1234,
104104
SecurityProtocol: ldap.SecurityProtocol(0),
105105
UserBase: "ou=Users,dc=min-domain-bind,dc=org",
@@ -280,7 +280,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
280280
IsActive: false,
281281
Cfg: &ldap.Source{
282282
Name: "ldap (simple auth) source full",
283-
Host: "ldap-simple-server full",
283+
HostList: "ldap-simple-server full",
284284
Port: 987,
285285
SecurityProtocol: ldap.SecurityProtocol(2),
286286
SkipVerify: true,
@@ -317,7 +317,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
317317
IsActive: true,
318318
Cfg: &ldap.Source{
319319
Name: "ldap (simple auth) source min",
320-
Host: "ldap-simple-server min",
320+
HostList: "ldap-simple-server min",
321321
Port: 123,
322322
SecurityProtocol: ldap.SecurityProtocol(0),
323323
UserDN: "cn=%s,ou=Users,dc=min-domain-simple,dc=org",
@@ -526,7 +526,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
526526
IsSyncEnabled: true,
527527
Cfg: &ldap.Source{
528528
Name: "ldap (via Bind DN) source full",
529-
Host: "ldap-bind-server full",
529+
HostList: "ldap-bind-server full",
530530
Port: 9876,
531531
SecurityProtocol: ldap.SecurityProtocol(1),
532532
SkipVerify: true,
@@ -630,7 +630,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
630630
authSource: &auth.Source{
631631
Type: auth.LDAP,
632632
Cfg: &ldap.Source{
633-
Host: "ldap-server",
633+
HostList: "ldap-server",
634634
},
635635
},
636636
},
@@ -978,7 +978,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
978978
IsActive: false,
979979
Cfg: &ldap.Source{
980980
Name: "ldap (simple auth) source full",
981-
Host: "ldap-simple-server full",
981+
HostList: "ldap-simple-server full",
982982
Port: 987,
983983
SecurityProtocol: ldap.SecurityProtocol(2),
984984
SkipVerify: true,
@@ -1078,7 +1078,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
10781078
authSource: &auth.Source{
10791079
Type: auth.DLDAP,
10801080
Cfg: &ldap.Source{
1081-
Host: "ldap-server",
1081+
HostList: "ldap-server",
10821082
},
10831083
},
10841084
},

routers/web/admin/auths.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
121121
}
122122
return &ldap.Source{
123123
Name: form.Name,
124-
Host: form.Host,
124+
HostList: form.Host,
125125
Port: form.Port,
126126
SecurityProtocol: ldap.SecurityProtocol(form.SecurityProtocol),
127127
SkipVerify: form.SkipVerify,

services/auth/source/ldap/source.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
// Source Basic LDAP authentication service
2626
type Source struct {
2727
Name string // canonical name (ie. corporate.ad)
28-
Host string // LDAP host
28+
HostList string // list containing LDAP host(s)
2929
Port int // port number
3030
SecurityProtocol SecurityProtocol
3131
SkipVerify bool

services/auth/source/ldap/source_search.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"net"
1111
"strconv"
1212
"strings"
13+
"time"
1314

1415
"code.gitea.io/gitea/modules/container"
1516
"code.gitea.io/gitea/modules/log"
@@ -111,28 +112,47 @@ func (source *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
111112
func dial(source *Source) (*ldap.Conn, error) {
112113
log.Trace("Dialing LDAP with security protocol (%v) without verifying: %v", source.SecurityProtocol, source.SkipVerify)
113114

114-
tlsConfig := &tls.Config{
115-
ServerName: source.Host,
116-
InsecureSkipVerify: source.SkipVerify,
117-
}
115+
ldap.DefaultTimeout = time.Second * 15
116+
// HostList is a list of hosts separated by commas
117+
hostList := strings.Split(source.HostList, ",")
118118

119-
if source.SecurityProtocol == SecurityProtocolLDAPS {
120-
return ldap.DialTLS("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)), tlsConfig)
121-
}
119+
for _, host := range hostList {
120+
tlsConfig := &tls.Config{
121+
ServerName: host,
122+
InsecureSkipVerify: source.SkipVerify,
123+
}
122124

123-
conn, err := ldap.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)))
124-
if err != nil {
125-
return nil, fmt.Errorf("error during Dial: %w", err)
126-
}
125+
if source.SecurityProtocol == SecurityProtocolLDAPS {
126+
conn, err := ldap.DialTLS("tcp", net.JoinHostPort(host, strconv.Itoa(source.Port)), tlsConfig)
127+
128+
if err != nil {
129+
// Connection failed, try again with the next host.
130+
log.Trace("error during Dial for host %s: %w", host, err)
131+
continue
132+
}
133+
conn.SetTimeout(time.Second * 10)
127134

128-
if source.SecurityProtocol == SecurityProtocolStartTLS {
129-
if err = conn.StartTLS(tlsConfig); err != nil {
130-
conn.Close()
131-
return nil, fmt.Errorf("error during StartTLS: %w", err)
135+
return conn, err
136+
}
137+
138+
conn, err := ldap.Dial("tcp", net.JoinHostPort(host, strconv.Itoa(source.Port)))
139+
if err != nil {
140+
log.Trace("error during Dial for host %s: %w", host, err)
141+
continue
142+
}
143+
conn.SetTimeout(time.Second * 10)
144+
145+
if source.SecurityProtocol == SecurityProtocolStartTLS {
146+
if err = conn.StartTLS(tlsConfig); err != nil {
147+
conn.Close()
148+
log.Trace("error during StartTLS for host %s: %w", host, err)
149+
continue
150+
}
132151
}
133152
}
134153

135-
return conn, nil
154+
// All servers were unreachable
155+
return nil, fmt.Errorf("dial failed for all provided servers: %s", hostList)
136156
}
137157

138158
func bindUser(l *ldap.Conn, userDN, passwd string) error {
@@ -257,7 +277,7 @@ func (source *Source) SearchEntry(name, passwd string, directBind bool) *SearchR
257277
}
258278
l, err := dial(source)
259279
if err != nil {
260-
log.Error("LDAP Connect error, %s:%v", source.Host, err)
280+
log.Error("LDAP Connect error, %s:%v", source.HostList, err)
261281
source.Enabled = false
262282
return nil
263283
}
@@ -421,7 +441,7 @@ func (source *Source) UsePagedSearch() bool {
421441
func (source *Source) SearchEntries() ([]*SearchResult, error) {
422442
l, err := dial(source)
423443
if err != nil {
424-
log.Error("LDAP Connect error, %s:%v", source.Host, err)
444+
log.Error("LDAP Connect error, %s:%v", source.HostList, err)
425445
source.Enabled = false
426446
return nil, err
427447
}

0 commit comments

Comments
 (0)