Match groups
This commit is contained in:
parent
551ee05177
commit
05f005707c
12
k8s.yaml
12
k8s.yaml
|
@ -14,16 +14,14 @@ data:
|
||||||
bindDN: uid=seviceaccount,cn=users,dc=example,dc=com
|
bindDN: uid=seviceaccount,cn=users,dc=example,dc=com
|
||||||
bindPW: password
|
bindPW: password
|
||||||
user:
|
user:
|
||||||
baseDN: cn=users,dc=example,dc=com
|
baseDN: ou=users,dc=example,dc=com
|
||||||
filter: "(objectClass=person)"
|
filter: "(cn={0})"
|
||||||
userAttr: uid
|
|
||||||
requiredGroups:
|
requiredGroups:
|
||||||
- appAdmin
|
- appAdmin
|
||||||
group:
|
group:
|
||||||
baseDN: cn=groups,dc=freeipa,dc=example,dc=com
|
baseDN: ou=groups,dc=example,dc=com
|
||||||
filter: "(objectClass=group)"
|
groupAttr: cn
|
||||||
userAttr: uid
|
filter: "(member={0})"
|
||||||
groupAttr: member
|
|
||||||
timeout:
|
timeout:
|
||||||
success: 24h
|
success: 24h
|
||||||
wrong: 5m
|
wrong: 5m
|
||||||
|
|
|
@ -10,15 +10,13 @@ type AuthConfig struct {
|
||||||
type UserConfig struct {
|
type UserConfig struct {
|
||||||
BaseDN string `yaml:"baseDN"`
|
BaseDN string `yaml:"baseDN"`
|
||||||
Filter string `yaml:"filter"`
|
Filter string `yaml:"filter"`
|
||||||
UserAttr string `yaml:"userAttr"`
|
|
||||||
RequiredGroups []string `yaml:"requiredGroups"`
|
RequiredGroups []string `yaml:"requiredGroups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupConfig struct {
|
type GroupConfig struct {
|
||||||
BaseDN string `yaml:"baseDN"`
|
BaseDN string `yaml:"baseDN"`
|
||||||
Filter string `yaml:"filter"`
|
|
||||||
UserAttr string `yaml:"userAttr"`
|
|
||||||
GroupAttr string `yaml:"groupAttr"`
|
GroupAttr string `yaml:"groupAttr"`
|
||||||
|
Filter string `yaml:"filter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimeoutConfig struct {
|
type TimeoutConfig struct {
|
||||||
|
|
83
src/ldap.go
83
src/ldap.go
|
@ -2,22 +2,29 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ldap "gopkg.in/ldap.v2"
|
ldap "gopkg.in/ldap.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var conn *ldap.Conn
|
var (
|
||||||
|
conn *ldap.Conn
|
||||||
|
admin bool
|
||||||
|
lock sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
func setupLDAP() error {
|
func setupLDAP() error {
|
||||||
size := len(config.Servers)
|
size := len(config.Servers)
|
||||||
if size == 0 {
|
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()))
|
r := rand.New(rand.NewSource(time.Now().Unix()))
|
||||||
|
@ -79,23 +86,29 @@ func setupLDAP() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Auth.BindDN != "" || config.Auth.BindPW != "" {
|
admin = false
|
||||||
err = conn.Bind(config.Auth.BindDN, config.Auth.BindPW)
|
return auth()
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ldapLogin(username, password string) (bool, error) {
|
func ldapLogin(username, password string) (bool, error) {
|
||||||
// TODO: lock
|
lock.Lock()
|
||||||
if config.Auth.BindDN != "" || config.Auth.BindPW != "" {
|
defer lock.Unlock()
|
||||||
err := conn.Bind(config.Auth.BindDN, config.Auth.BindPW)
|
|
||||||
|
err := auth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
req := ldap.NewSearchRequest(
|
req := ldap.NewSearchRequest(
|
||||||
config.User.BaseDN,
|
config.User.BaseDN,
|
||||||
|
@ -105,12 +118,11 @@ func ldapLogin(username, password string) (bool, error) {
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
strings.Replace(config.User.Filter, "{0}", username, -1),
|
strings.Replace(config.User.Filter, "{0}", username, -1),
|
||||||
[]string{config.User.UserAttr},
|
nil,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
res, err := conn.Search(req)
|
res, err := conn.Search(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -119,6 +131,51 @@ func ldapLogin(username, password string) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
admin = false
|
||||||
err = conn.Bind(res.Entries[0].DN, password)
|
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
|
||||||
}
|
}
|
||||||
|
|
10
src/main.go
10
src/main.go
|
@ -19,11 +19,11 @@ var (
|
||||||
Web: "0.0.0.0:5555",
|
Web: "0.0.0.0:5555",
|
||||||
Path: "/",
|
Path: "/",
|
||||||
User: UserConfig{
|
User: UserConfig{
|
||||||
UserAttr: "uid",
|
Filter: "(cn={0})",
|
||||||
},
|
},
|
||||||
Group: GroupConfig{
|
Group: GroupConfig{
|
||||||
UserAttr: "uid",
|
Filter: "(member={0})",
|
||||||
GroupAttr: "member",
|
GroupAttr: "cn",
|
||||||
},
|
},
|
||||||
Timeout: TimeoutConfig{
|
Timeout: TimeoutConfig{
|
||||||
Success: 24 * time.Hour,
|
Success: 24 * time.Hour,
|
||||||
|
@ -45,7 +45,7 @@ func main() {
|
||||||
log.Fatalf("Error on parse config: %v\n", err)
|
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()
|
err = setupLDAP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -73,6 +73,8 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
secret := strings.SplitN(string(decoded), ":", 2)
|
secret := strings.SplitN(string(decoded), ":", 2)
|
||||||
|
|
||||||
if len(secret) == 2 && validate(secret[0], secret[1]) {
|
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)
|
w.WriteHeader(http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue