From 8d4ab002d82a20be07d75db9eb23bce59aa8b946 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Feb 2020 00:18:56 +0100 Subject: [PATCH] login-validator: refactor auth methods --- pkg/login-validator/cmd/auth.go | 11 ++++ pkg/login-validator/cmd/auth_ldap.go | 77 ++++++++++++++++++++++++ pkg/login-validator/cmd/login.go | 89 ++++------------------------ pkg/login-validator/cmd/main.go | 34 ++++++++--- server.yml | 6 +- 5 files changed, 126 insertions(+), 91 deletions(-) create mode 100644 pkg/login-validator/cmd/auth.go create mode 100644 pkg/login-validator/cmd/auth_ldap.go diff --git a/pkg/login-validator/cmd/auth.go b/pkg/login-validator/cmd/auth.go new file mode 100644 index 0000000..b6608f0 --- /dev/null +++ b/pkg/login-validator/cmd/auth.go @@ -0,0 +1,11 @@ +package main + +type AuthMethod interface { + checkAuth(username, password string) (bool, error) +} + +type NoAuth struct {} + +func (NoAuth) checkAuth(username, password string) (res bool, err error) { + return true, nil +} diff --git a/pkg/login-validator/cmd/auth_ldap.go b/pkg/login-validator/cmd/auth_ldap.go new file mode 100644 index 0000000..c5c80a0 --- /dev/null +++ b/pkg/login-validator/cmd/auth_ldap.go @@ -0,0 +1,77 @@ +package main + +import ( + "crypto/tls" + "errors" + "fmt" + + "gopkg.in/ldap.v2" +) + +type LDAPAuth struct { + Addr string + Port int + IsTLS bool + Base string + BindUsername string + BindPassword string +} + +func (l LDAPAuth) checkAuth(username, password string) (res bool, err error) { + tlsCnf := tls.Config{InsecureSkipVerify: true} + + var c *ldap.Conn + + if l.IsTLS { + c, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", l.Addr, l.Port), &tlsCnf) + if err != nil { + return false, err + } + } else { + c, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", l.Addr, l.Port)) + if err != nil { + return false, err + } + + // Reconnect with TLS + err = c.StartTLS(&tlsCnf) + if err != nil { + return false, err + } + } + defer c.Close() + + if l.BindUsername != "" { + err = c.Bind(l.BindUsername, l.BindPassword) + if err != nil { + return false, err + } + } + + // Search for the given username + searchRequest := ldap.NewSearchRequest( + l.Base, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + fmt.Sprintf("(&(objectClass=person)(uid=%s))", username), + []string{"dn"}, + nil, + ) + + sr, err := c.Search(searchRequest) + if err != nil { + return false, err + } + + if len(sr.Entries) != 1 { + return false, errors.New("User does not exist or too many entries returned") + } + + userdn := sr.Entries[0].DN + + err = c.Bind(userdn, password) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/login-validator/cmd/login.go b/pkg/login-validator/cmd/login.go index a949166..b060aae 100644 --- a/pkg/login-validator/cmd/login.go +++ b/pkg/login-validator/cmd/login.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/hmac" "crypto/sha512" - "crypto/tls" "encoding/base64" "encoding/json" "errors" @@ -14,21 +13,13 @@ import ( "net/http" "strings" "time" - - "gopkg.in/ldap.v2" ) var loginSalt string type loginChecker struct { - students []Student - noAuth bool - ldapAddr string - ldapPort int - ldapIsTLS bool - ldapBase string - ldapBindUsername string - ldapBindPassword string + students []Student + authMethod AuthMethod } type loginUpload struct { @@ -36,64 +27,6 @@ type loginUpload struct { Password string } -func (l loginChecker) ldapAuth(username, password string) (res bool, err error) { - tlsCnf := tls.Config{InsecureSkipVerify: true} - - var c *ldap.Conn - - if l.ldapIsTLS { - c, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", l.ldapAddr, l.ldapPort), &tlsCnf) - if err != nil { - return false, err - } - } else { - c, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", l.ldapAddr, l.ldapPort)) - if err != nil { - return false, err - } - - // Reconnect with TLS - err = c.StartTLS(&tlsCnf) - if err != nil { - return false, err - } - } - defer c.Close() - - if l.ldapBindUsername != "" { - err = c.Bind(l.ldapBindUsername, l.ldapBindPassword) - if err != nil { - return false, err - } - } - - // Search for the given username - searchRequest := ldap.NewSearchRequest( - l.ldapBase, - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=person)(uid=%s))", username), - []string{"dn"}, - nil, - ) - - sr, err := c.Search(searchRequest) - if err != nil { - return false, err - } - - if len(sr.Entries) != 1 { - return false, errors.New("User does not exist or too many entries returned") - } - - userdn := sr.Entries[0].DN - - err = c.Bind(userdn, password) - if err != nil { - return false, err - } - - return true, nil -} func (l loginChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) { if addr := r.Header.Get("X-Forwarded-For"); addr != "" { @@ -139,16 +72,14 @@ func (l loginChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - if ! l.noAuth { - if ok, err := l.ldapAuth(lu.Username, lu.Password); err != nil { - log.Println("Unable to perform authentication for", lu.Username, ":", err, "at", r.RemoteAddr) - http.Error(w, err.Error(), http.StatusUnauthorized) - return - } else if !ok { - log.Println("Login failed:", lu.Username, "at", r.RemoteAddr) - http.Error(w, "Invalid password", http.StatusUnauthorized) - return - } + if ok, err := l.authMethod.checkAuth(lu.Username, lu.Password); err != nil { + log.Println("Unable to perform authentication for", lu.Username, ":", err, "at", r.RemoteAddr) + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } else if !ok { + log.Println("Login failed:", lu.Username, "at", r.RemoteAddr) + http.Error(w, "Invalid password", http.StatusUnauthorized) + return } // Find corresponding MAC diff --git a/pkg/login-validator/cmd/main.go b/pkg/login-validator/cmd/main.go index 1fe4239..d704e37 100644 --- a/pkg/login-validator/cmd/main.go +++ b/pkg/login-validator/cmd/main.go @@ -17,21 +17,20 @@ var tftpDir string func main() { var studentsFile string - var lc loginChecker - var bind = flag.String("bind", ":8081", "Bind port/socket") flag.StringVar(&studentsFile, "students", "./students.csv", "Path to a CSV file containing students list") flag.StringVar(&ARPTable, "arp", ARPTable, "Path to ARP table") flag.StringVar(&tftpDir, "tftpdir", "/var/tftp/", "Path to TFTPd directory") flag.StringVar(&loginSalt, "loginsalt", "adelina", "secret used in login HMAC") - flag.BoolVar(&lc.noAuth, "noauth", false, "don't perform password check") - flag.StringVar(&lc.ldapAddr, "ldaphost", "auth.cri.epita.fr", "LDAP host") - flag.IntVar(&lc.ldapPort, "ldapport", 636, "LDAP port") - flag.BoolVar(&lc.ldapIsTLS, "ldaptls", false, "Is LDAP connection LDAPS?") - flag.StringVar(&lc.ldapBase, "ldapbase", "dc=epita,dc=net", "LDAP base") - flag.StringVar(&lc.ldapBindUsername, "ldapbindusername", "", "LDAP user to use in order to perform bind (optional if search can be made anonymously)") - flag.StringVar(&lc.ldapBindPassword, "ldapbindpassword", "", "Password for the bind user") + var auth = flag.String("auth", "none", "Auth method: none, ldap") + + var ldapAddr = flag.String("ldaphost", "auth.cri.epita.fr", "LDAP host") + var ldapPort = flag.Int("ldapport", 636, "LDAP port") + var ldaptls = flag.Bool("ldaptls", false, "Is LDAP connection LDAPS?") + var ldapbase = flag.String("ldapbase", "dc=epita,dc=net", "LDAP base") + var ldapbindusername = flag.String("ldapbindusername", "", "LDAP user to use in order to perform bind (optional if search can be made anonymously)") + var ldapbindpassword = flag.String("ldapbindpassword", "", "Password for the bind user") flag.Parse() var err error @@ -42,6 +41,23 @@ func main() { log.Fatal(err) } + var lc loginChecker + + if auth != nil && *auth == "ldap" { + log.Printf("Auth method: LDAP(%s@%s:%d?%s)", *ldapbindusername, *ldapAddr, *ldapPort, *ldapbase) + lc.authMethod = LDAPAuth{ + Addr: *ldapAddr, + Port: *ldapPort, + IsTLS: *ldaptls, + Base: *ldapbase, + BindUsername: *ldapbindusername, + BindPassword: *ldapbindpassword, + } + } else { + log.Println("No auth method selected: all access will be granted") + lc.authMethod = NoAuth{} + } + lc.students, err = readStudentsList(studentsFile) if err != nil { log.Fatal(err) diff --git a/server.yml b/server.yml index 047d5e1..8b639b4 100644 --- a/server.yml +++ b/server.yml @@ -153,9 +153,9 @@ services: - /var/lib/adlin/pxelinux.cfg:/srv/tftp/pxelinux.cfg - name: login-validator - image: nemunaire/adlin-login-validator:a5fee7db6c578a6d698983be8e74e7ce7420791e -# command: ["/bin/login-validator", "-bind=:8081", "-ldaphost=auth.cri.epita.net", "-ldapport=636", "-ldaptls", "-ldapbase=dc=epita,dc=net"] - command: ["/bin/login-validator", "-bind=:8081", "-noauth"] + image: nemunaire/adlin-login-validator:87f1cf05e8037b934d293a48704bd3f8ee678d41 +# command: ["/bin/login-validator", "-bind=:8081", "-auth=ldap", "-ldaphost=auth.cri.epita.net", "-ldapport=636", "-ldaptls", "-ldapbase=dc=epita,dc=net"] + command: ["/bin/login-validator", "-bind=:8081", "-auth=none"] net: /run/netns/login binds: - /etc/resolv.conf:/etc/resolv.conf:ro