From 8fc325ab5a996e2674cee162bd4eab7a5e75944b Mon Sep 17 00:00:00 2001 From: haoxingxing Date: Thu, 2 May 2024 16:56:35 +0800 Subject: [PATCH] impl simple ldap backend --- auth/file.go | 14 ------ auth/ldap.go | 129 +++++++++++++++++++++++++++++++++++++++++++++++- scheme.ldif | 17 +++++++ ui/config.go | 13 ++++- ui/myservice.go | 5 ++ 5 files changed, 161 insertions(+), 17 deletions(-) create mode 100644 scheme.ldif diff --git a/auth/file.go b/auth/file.go index 1e9cf75..9e4dc91 100644 --- a/auth/file.go +++ b/auth/file.go @@ -84,20 +84,6 @@ func (mgr *fileBackend) CheckSSHKey(ctx *ssh.Ctx, pubkey gossh.PublicKey) bool { return false } -func (mgr *fileBackend) SSHAuthPwd(ctx *ssh.Ctx, password []byte) bool { - usr, ok := mgr.usrs[ctx.User] - if !ok { - return false - } - if !usr.allowsshpwd { - return false - } - if usr.checkpwd(string(password)) { - return true - } - return false -} - func (mgr *fileBackend) CheckPassword(username string, password string) bool { usr, ok := mgr.usrs[username] if !ok { diff --git a/auth/ldap.go b/auth/ldap.go index 0c4d608..a823e02 100644 --- a/auth/ldap.go +++ b/auth/ldap.go @@ -1,10 +1,137 @@ package auth -type ldapAuth struct { +import ( + "crypto/tls" + "fmt" + "reflect" + "strings" + "sync" + + "github.com/go-ldap/ldap/v3" + "github.com/mrhaoxx/OpenNG/ssh" + gossh "golang.org/x/crypto/ssh" +) + +type ldapBackend struct { url string searchBase string bindDN string bindPW string + + ldapQueryConnPool sync.Pool +} + +func (backend *ldapBackend) tryGetQueryConn() *ldap.Conn { + conn, ok := backend.ldapQueryConnPool.Get().(*ldap.Conn) + if !ok { + return nil + } + for conn.IsClosing() { + if !ok { + return nil + } + + conn, ok = backend.ldapQueryConnPool.Get().(*ldap.Conn) + } + + return conn +} + +func (backend *ldapBackend) CheckPassword(username string, password string) bool { + + conn := backend.tryGetQueryConn() + defer backend.ldapQueryConnPool.Put(conn) + + searchRequest := ldap.NewSearchRequest( + backend.searchBase, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=posixAccount)(uid="+username+"))", + []string{"dn"}, + nil, + ) + if result, err := conn.Search(searchRequest); err == nil { + if len(result.Entries) != 1 { + return false + } + + try, err := ldap.DialURL(backend.url, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true})) + if err != nil { + return false + } + defer try.Close() + + return try.Bind(result.Entries[0].DN, password) == nil + + } + return false } +func (backend *ldapBackend) CheckSSHKey(ctx *ssh.Ctx, pubkey gossh.PublicKey) bool { + + if strings.ContainsAny(ctx.User, "\" ',=+<>#:;\\()") { + return false + } + + conn := backend.tryGetQueryConn() + defer backend.ldapQueryConnPool.Put(conn) + + searchRequest := ldap.NewSearchRequest( + backend.searchBase, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=posixAccount)(uid="+ctx.User+"))", + []string{"sshPublicKey"}, + nil, + ) + if result, err := conn.Search(searchRequest); err == nil { + if len(result.Entries) != 1 { + return false + } + + for _, attr := range result.Entries[0].Attributes { + if attr.Name != "sshPublicKey" { + continue + } + + for _, val := range attr.Values { + got, _, _, _, err := gossh.ParseAuthorizedKey([]byte(val)) + if err != nil { + continue + } + if pubkey.Type() == got.Type() && reflect.DeepEqual(pubkey.Marshal(), got.Marshal()) { + return true + } + } + } + } + return false +} + +func (mgr *ldapBackend) AllowForwardProxy(username string) bool { + return false +} + +func NewLDAPBackend(url, searchBase, bindDN, bindPW string) *ldapBackend { + + back := &ldapBackend{ + url: url, + searchBase: searchBase, + bindDN: bindDN, + bindPW: bindPW, + } + + back.ldapQueryConnPool.New = func() interface{} { + conn, err := ldap.DialURL(back.url, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true})) + if err != nil { + panic(err) + } + err = conn.Bind(back.bindDN, back.bindPW) + if err != nil { + panic(err) + } + fmt.Println("LDAP connection established") + return conn + } + + return back +} diff --git a/scheme.ldif b/scheme.ldif new file mode 100644 index 0000000..c5af580 --- /dev/null +++ b/scheme.ldif @@ -0,0 +1,17 @@ +dn: cn=netgate,cn=scheme,cn=config +objectClass: olcSchemaConfig +cn: netgate +olcAttributeTypes: {0}( 1.3.6.1.4.1.61850.1.1.1.1 + NAME 'allowForwardProxy' + DESC 'Allow forward proxy' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 + ) +olcObjectClasses: {0}( 1.3.6.1.4.1.61850.1.1.2.1 + NAME 'ngUserAccount' + DESC 'Netgate User Account' + SUP top + AUXILIARY + MUST ( uid ) + MAY ( allowForwardProxy $ userPassword $ sshPublicKey ) + ) \ No newline at end of file diff --git a/ui/config.go b/ui/config.go index 943834a..fc158d1 100644 --- a/ui/config.go +++ b/ui/config.go @@ -15,9 +15,18 @@ type Cfg struct { } type authConfig struct { - Users []User `yaml:"Users,flow"` - Policies []Policy `yaml:"Policies,flow"` + Users []User `yaml:"Users,flow"` + Policies []Policy `yaml:"Policies,flow"` + LDAP LDAPConfig `yaml:"LDAP"` } + +type LDAPConfig struct { + Url string `yaml:"Url"` + SearchBase string `yaml:"SearchBase"` + BindDN string `yaml:"BindDN"` + BindPW string `yaml:"BindPW"` +} + type User struct { Username string `yaml:"Username"` PasswordHash string `yaml:"PasswordHash"` diff --git a/ui/myservice.go b/ui/myservice.go index 6dea06e..4036b2c 100644 --- a/ui/myservice.go +++ b/ui/myservice.go @@ -228,6 +228,11 @@ func LoadCfg(cfgs []byte) error { fileBackend.SetUser(u.Username, u.PasswordHash, u.AllowForwardProxy, pks, u.SSHAllowPassword) } + if cfg.Auth.LDAP.Url != "" { + ldapBackend := auth.NewLDAPBackend(cfg.Auth.LDAP.Url, cfg.Auth.LDAP.SearchBase, cfg.Auth.LDAP.BindDN, cfg.Auth.LDAP.BindPW) + pba.AddBackends([]auth.PolicyBackend{ldapBackend}) + log.Println("sys", "auth", "use ldap backend as [1]") + } for _, p := range cfg.Auth.Policies { log.Println("sys", "auth", "Found Policy", p.Name) if err = pba.AddPolicy(p.Name, p.Allowance, p.Users, p.Hosts, p.Paths); err != nil {