From 05f005707ca67757a41ae65742051cae10768321 Mon Sep 17 00:00:00 2001 From: Tiago Augusto Pimenta Date: Tue, 2 Oct 2018 18:54:00 -0300 Subject: [PATCH] Match groups --- k8s.yaml | 12 +++---- src/config.go | 4 +-- src/ldap.go | 89 ++++++++++++++++++++++++++++++++++++++++++--------- src/main.go | 10 +++--- 4 files changed, 85 insertions(+), 30 deletions(-) diff --git a/k8s.yaml b/k8s.yaml index b23c5f4..863e4bb 100644 --- a/k8s.yaml +++ b/k8s.yaml @@ -14,16 +14,14 @@ data: bindDN: uid=seviceaccount,cn=users,dc=example,dc=com bindPW: password user: - baseDN: cn=users,dc=example,dc=com - filter: "(objectClass=person)" - userAttr: uid + baseDN: ou=users,dc=example,dc=com + filter: "(cn={0})" requiredGroups: - appAdmin group: - baseDN: cn=groups,dc=freeipa,dc=example,dc=com - filter: "(objectClass=group)" - userAttr: uid - groupAttr: member + baseDN: ou=groups,dc=example,dc=com + groupAttr: cn + filter: "(member={0})" timeout: success: 24h wrong: 5m diff --git a/src/config.go b/src/config.go index eddc2ab..1fde8e6 100644 --- a/src/config.go +++ b/src/config.go @@ -10,15 +10,13 @@ type AuthConfig struct { type UserConfig struct { BaseDN string `yaml:"baseDN"` Filter string `yaml:"filter"` - UserAttr string `yaml:"userAttr"` RequiredGroups []string `yaml:"requiredGroups"` } type GroupConfig struct { BaseDN string `yaml:"baseDN"` - Filter string `yaml:"filter"` - UserAttr string `yaml:"userAttr"` GroupAttr string `yaml:"groupAttr"` + Filter string `yaml:"filter"` } type TimeoutConfig struct { diff --git a/src/ldap.go b/src/ldap.go index 9f27268..7f3a388 100644 --- a/src/ldap.go +++ b/src/ldap.go @@ -2,22 +2,29 @@ package main import ( "crypto/tls" + "errors" "fmt" "math/rand" "regexp" + "sort" "strconv" "strings" + "sync" "time" ldap "gopkg.in/ldap.v2" ) -var conn *ldap.Conn +var ( + conn *ldap.Conn + admin bool + lock sync.Mutex +) func setupLDAP() error { size := len(config.Servers) if size == 0 { - return fmt.Errorf("No LDAP server available on %+v", config) + return errors.New("No LDAP server available!") } r := rand.New(rand.NewSource(time.Now().Unix())) @@ -79,22 +86,28 @@ func setupLDAP() error { } } - if config.Auth.BindDN != "" || config.Auth.BindPW != "" { - err = conn.Bind(config.Auth.BindDN, config.Auth.BindPW) - if err != nil { - return err - } + admin = false + return auth() +} + +func auth() error { + if admin || config.Auth.BindDN == "" && config.Auth.BindPW == "" { + return nil + } + err := conn.Bind(config.Auth.BindDN, config.Auth.BindPW) + if err == nil { + admin = true } return nil } func ldapLogin(username, password string) (bool, error) { - // TODO: lock - if config.Auth.BindDN != "" || config.Auth.BindPW != "" { - err := conn.Bind(config.Auth.BindDN, config.Auth.BindPW) - if err != nil { - return false, err - } + lock.Lock() + defer lock.Unlock() + + err := auth() + if err != nil { + return false, err } req := ldap.NewSearchRequest( @@ -105,12 +118,11 @@ func ldapLogin(username, password string) (bool, error) { 0, false, strings.Replace(config.User.Filter, "{0}", username, -1), - []string{config.User.UserAttr}, + nil, nil, ) res, err := conn.Search(req) - if err != nil { return false, err } @@ -119,6 +131,51 @@ func ldapLogin(username, password string) (bool, error) { return false, nil } + admin = false err = conn.Bind(res.Entries[0].DN, password) - return err == nil, nil + if err != nil { + return false, nil + } + + err = auth() + if err != nil { + return false, err + } + + if len(config.User.RequiredGroups) == 0 { + return true, nil + } + + req = ldap.NewSearchRequest( + config.Group.BaseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + strings.Replace(config.Group.Filter, "{0}", res.Entries[0].DN, -1), + []string{config.Group.GroupAttr}, + nil, + ) + + res, err = conn.Search(req) + if err != nil { + return false, err + } + + groups := []string{} + for _, entry := range res.Entries { + groups = append(groups, entry.GetAttributeValue(config.Group.GroupAttr)) + } + + sort.Strings(groups) + + for _, group := range config.User.RequiredGroups { + pos := sort.SearchStrings(groups, group) + if pos >= len(groups) || groups[pos] != group { + return false, nil + } + } + + return true, nil } diff --git a/src/main.go b/src/main.go index 534d50a..a2a5990 100644 --- a/src/main.go +++ b/src/main.go @@ -19,11 +19,11 @@ var ( Web: "0.0.0.0:5555", Path: "/", User: UserConfig{ - UserAttr: "uid", + Filter: "(cn={0})", }, Group: GroupConfig{ - UserAttr: "uid", - GroupAttr: "member", + Filter: "(member={0})", + GroupAttr: "cn", }, Timeout: TimeoutConfig{ Success: 24 * time.Hour, @@ -45,7 +45,7 @@ func main() { log.Fatalf("Error on parse config: %v\n", err) } - fmt.Printf("Loaded config: %+v\n", config) + fmt.Printf("Loaded config \"%s\".\n", *configFile) err = setupLDAP() if err != nil { @@ -73,6 +73,8 @@ func handler(w http.ResponseWriter, r *http.Request) { secret := strings.SplitN(string(decoded), ":", 2) if len(secret) == 2 && validate(secret[0], secret[1]) { + // TODO: match by header, e.g: X-Original-URL X-Original-Method X-Sent-From X-Auth-Request-Redirect + w.WriteHeader(http.StatusOK) return }