diff --git a/cmd/bosun/conf/system.go b/cmd/bosun/conf/system.go index 585372c48d..6803b6671b 100644 --- a/cmd/bosun/conf/system.go +++ b/cmd/bosun/conf/system.go @@ -178,6 +178,8 @@ type AuthConf struct { type LDAPConf struct { // Domain name (used to make domain/username) Domain string + //user base dn (LDAP Auth) + UserBaseDn string // LDAP server LdapAddr string // allow insecure ldap connection? diff --git a/cmd/bosun/web/middlewares.go b/cmd/bosun/web/middlewares.go index d65c91a4af..827aa78f18 100644 --- a/cmd/bosun/web/middlewares.go +++ b/cmd/bosun/web/middlewares.go @@ -100,6 +100,7 @@ func buildAuth(cfg *conf.AuthConf) (easyauth.AuthManager, *token.TokenProvider, func buildLDAPConfig(ld conf.LDAPConf) (*ldap.LdapProvider, error) { l := &ldap.LdapProvider{ Domain: ld.Domain, + UserBaseDn: ld.UserBaseDn, LdapAddr: ld.LdapAddr, AllowInsecure: ld.AllowInsecure, RootSearchPath: ld.RootSearchPath, diff --git a/vendor/github.com/captncraig/easyauth/providers/ldap/ldap.go b/vendor/github.com/captncraig/easyauth/providers/ldap/ldap.go index dbc6677735..9fb19baa87 100644 --- a/vendor/github.com/captncraig/easyauth/providers/ldap/ldap.go +++ b/vendor/github.com/captncraig/easyauth/providers/ldap/ldap.go @@ -14,6 +14,8 @@ import ( type LdapProvider struct { //name of domain Domain string + //user base dn (for LDAP Auth) + UserBaseDn string //server to query LdapAddr string //if untrusted certs should be allowed @@ -103,7 +105,19 @@ func (l *LdapProvider) HandlePost(w http.ResponseWriter, r *http.Request) { } func (l *LdapProvider) Authorize(un, pw string) easyauth.Role { - fullUn := l.Domain + "\\" + un + + var fullUn string + var auth_ldap bool + + if l.UserBaseDn != "" { + // prepare LDAP user bind dn + fullUn = "uid=" + un + "," + l.UserBaseDn + auth_ldap = true + } else { + // prepare AD user domain + fullUn = l.Domain + "\\" + un + } + conn, err := ldap.DialTLS("tcp", l.LdapAddr, &tls.Config{ InsecureSkipVerify: l.AllowInsecure, }) @@ -125,7 +139,7 @@ func (l *LdapProvider) Authorize(un, pw string) easyauth.Role { if group.Path == "*" { role |= group.Role } - isMember, err := checkGroupMembership(un, conn, l.RootSearchPath, map[string]bool{}, []string{group.Path}) + isMember, err := checkGroupMembership(un, conn, l.RootSearchPath, map[string]bool{}, []string{group.Path}, auth_ldap) if err != nil { log.Println("Error checking group membership", err) panic("Error checking group memberships") @@ -140,19 +154,28 @@ func (l *LdapProvider) Authorize(un, pw string) easyauth.Role { return role } -func checkGroupMembership(un string, conn *ldap.Conn, rootSearch string, alreadySearched map[string]bool, groups []string) (isMember bool, err error) { +func checkGroupMembership(un string, conn *ldap.Conn, rootSearch string, alreadySearched map[string]bool, groups []string, auth_ldap bool) (isMember bool, err error) { // Implementation of recursive group membership search. There is a magic AD key that kinda does this, but this may be more reliable. grps := make([]string, len(groups)) for i, g := range groups { alreadySearched[g] = true - grps[i] = fmt.Sprintf("(memberof=%s)", g) + grps[i] = fmt.Sprintf("(memberof=%s)(member=%s)", g, g) + } + + // map user key for LDAP it will be cn + var user_id string + if auth_ldap { + user_id = "cn" + } else { + user_id = "sAMAccountName" } + //find all users or groups that are direct members of ANY of the given groups searchRequest := ldap.NewSearchRequest( rootSearch, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(|(objectClass=user)(objectClass=group))(|%s))", strings.Join(grps, "")), - []string{"dn", "objectClass", "sAMAccountName"}, + fmt.Sprintf("(&(|(objectClass=user)(objectClass=group)(objectClass=groupOfNames))(|%s))", strings.Join(grps, "")), + []string{"dn", "objectClass", user_id}, nil, ) sr, err := conn.Search(searchRequest) @@ -171,9 +194,17 @@ func checkGroupMembership(un string, conn *ldap.Conn, rootSearch string, already if objType == "person" && acctName == un { return true, nil } + + //for LDAP group member check only the name + if auth_ldap { + if acctName == un { + return true, nil + } + } + } if len(nextSearches) > 0 { - return checkGroupMembership(un, conn, rootSearch, alreadySearched, nextSearches) + return checkGroupMembership(un, conn, rootSearch, alreadySearched, nextSearches, auth_ldap) } return false, nil } diff --git a/vendor/vendor.json b/vendor/vendor.json index a179b2b019..1994901040 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -290,26 +290,26 @@ { "checksumSHA1": "2D5vo1CBsOIKPe9B0Phk1+m9uEg=", "path": "github.com/captncraig/easyauth", - "revision": "f8cfe65ef31cd6959a9553175bb6caf1f5af928f", - "revisionTime": "2017-04-17T16:53:34Z" + "revision": "c6de284138cfb3c428a4870ceec7391fddcec2ef", + "revisionTime": "2017-10-13T13:10:59Z" }, { - "checksumSHA1": "N51CVtTE9ZUN1I/+jJzFeb/SuXE=", + "checksumSHA1": "soJm64I35YJZMgxhIhfm86D+Ons=", "path": "github.com/captncraig/easyauth/providers/ldap", - "revision": "f8cfe65ef31cd6959a9553175bb6caf1f5af928f", - "revisionTime": "2017-04-17T16:53:34Z" + "revision": "c6de284138cfb3c428a4870ceec7391fddcec2ef", + "revisionTime": "2017-10-13T13:10:59Z" }, { "checksumSHA1": "z+rzZ/v6jmr+VDDEWxrYbKnZJA4=", "path": "github.com/captncraig/easyauth/providers/token", - "revision": "f8cfe65ef31cd6959a9553175bb6caf1f5af928f", - "revisionTime": "2017-04-17T16:53:34Z" + "revision": "c6de284138cfb3c428a4870ceec7391fddcec2ef", + "revisionTime": "2017-10-13T13:10:59Z" }, { "checksumSHA1": "bG2Nz0IR6+f6Re6IHrZN/yxfm1U=", "path": "github.com/captncraig/easyauth/providers/token/redisStore", - "revision": "f8cfe65ef31cd6959a9553175bb6caf1f5af928f", - "revisionTime": "2017-04-17T16:53:34Z" + "revision": "c6de284138cfb3c428a4870ceec7391fddcec2ef", + "revisionTime": "2017-10-13T13:10:59Z" }, { "path": "github.com/coreos/go-systemd/dbus",