login-validator: refactor auth methods

This commit is contained in:
nemunaire 2020-02-21 00:18:56 +01:00
parent 1d2199aaef
commit 8d4ab002d8
5 changed files with 126 additions and 91 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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