package main import ( "context" "flag" "fmt" "log" "net/http" "net/url" "os" "os/signal" "path/filepath" "syscall" ) var tftpDir string func main() { var studentsFile string if v, exists := os.LookupEnv("ADLIN_SHARED_SECRET"); exists { loginSalt = v } flag.BoolVar(&justLogin, "just-login", justLogin, "Don't perform MAC assignation and remote registration") 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", loginSalt, "secret used in login HMAC") var auth = flag.String("auth", "none", "Auth method: none, fwd, ldap, krb5") var fwdURI = flag.String("fwduri", "https://srs.epita.fr:443/", "URI to forward auth requests") var krb5Realm = flag.String("krb5realm", "CRI.EPITA.FR", "Kerberos Realm") 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 // Sanitize options log.Println("Checking paths...") if tftpDir, err = filepath.Abs(tftpDir); err != nil { 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 if auth != nil && *auth == "krb5" && krb5Realm != nil { log.Printf("Auth method: KRB5(%s)", *krb5Realm) lc.authMethod = &Krb5Auth{ Realm: *krb5Realm, } } else if auth != nil && *auth == "fwd" && fwdURI != nil { if uri, err := url.Parse(*fwdURI); err != nil { log.Fatal("Unable to parse FWD URL:", err) } else { log.Printf("Auth method: HTTP_FWD(%s)", uri) lc.authMethod = FWDAuth{ URI: uri, } } } 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) } // Prepare graceful shutdown interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGHUP) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) signal.Notify(interrupt, os.Interrupt, syscall.SIGINT) srv := &http.Server{ Addr: *bind, } log.Println("Registering handlers...") mux := http.NewServeMux() mux.Handle("/login", lc) mux.HandleFunc("/logout", logout) http.HandleFunc("/", mux.ServeHTTP) // Serve content go func() { log.Fatal(srv.ListenAndServe()) }() log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) // Wait shutdown signal mloop: for { switch <-interrupt { case syscall.SIGHUP: log.Println("Reloading students files...") if lc.students, err = readStudentsList(studentsFile); err != nil { log.Println("Error during students.csv reload:", err) } case syscall.SIGTERM, syscall.SIGINT: break mloop } } log.Print("The service is shutting down...") srv.Shutdown(context.Background()) log.Println("done") }