package main import ( "flag" "github.com/gorilla/mux" "net/http" "fmt" "strings" "io/ioutil" "time" "log" "encoding/json" "strconv" ) var ( port *int ) func registerHTTPFlags() { port = flag.Int("port", 8080, "The port for HTTP") } func setupHTTPServer() (http.Server) { r := mux.NewRouter() r.HandleFunc("/", indexHandler) r.HandleFunc("/files/{file}", fileHandler) r.HandleFunc("/login", loginGetHandler).Methods("GET") r.HandleFunc("/login", loginPostHandler).Methods("POST") r.HandleFunc("/logout", logoutHandler).Methods("POST") r.HandleFunc("/challenges", challengesHandler).Methods("GET") r.HandleFunc("/access", accessHandler).Methods("GET") r.HandleFunc("/api/getChallenges", getChallengesHandler).Methods("GET") r.HandleFunc("/api/submitFlag", submitFlagHandler).Methods("POST") r.HandleFunc("/api/startContainer", startContainerHandler).Methods("POST") r.HandleFunc("/api/stopContainer", stopContainerHandler).Methods("POST") r.HandleFunc("/api/getAccess", getAccessHandler).Methods("GET") return http.Server{ Addr: fmt.Sprintf("0.0.0.0:%d", *port), Handler: r, } } // Host the index file func indexHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, redirect to frontpage readFileToResponse(w, "/index.html") } } // Static host files func fileHandler(w http.ResponseWriter, r *http.Request) { readFileToResponse(w, mux.Vars(r)["file"]) } // Helper function to host files off of "hosted/" directory func readFileToResponse(w http.ResponseWriter, path string) { requestedFile := strings.Replace(path, "..", "", -1) contents, readError := ioutil.ReadFile(fmt.Sprintf("hosted/%s", requestedFile)) if readError != nil { w.Write([]byte(fmt.Sprintf("unable to read %s", requestedFile))) } else { w.Write([]byte(contents)) } } // Read login page func loginGetHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") // we need to verify that the client doesn't already have a valid session if cookieNotFoundError == nil && isValidSession(session.Value) { // session already valid, redirect the user http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } else { readFileToResponse(w, "/login.html") } } // Process login data func loginPostHandler(w http.ResponseWriter, r *http.Request) { r.ParseForm() username := r.Form.Get("username") accessCode := r.Form.Get("accesscode") session, cookieNotFoundError := r.Cookie("session") validRedirect := false // we need to verify that the client doesn't already have a valid session if cookieNotFoundError == nil && isValidSession(session.Value) { // session already valid, redirect the user validRedirect = true } else { // no valid session present, check the given credentials if verifyCredentials(username, accessCode) { // credentials valid, create session newSessionToken := createSession() http.SetCookie(w, &http.Cookie{ Name: "session", Value: newSessionToken, Path: "/", Expires: time.Now().Add(time.Hour * 24), }) validRedirect = true } } if validRedirect { // Redirect to frontpage http.Redirect(w, r, "/", http.StatusTemporaryRedirect) return } else { // credentials invalid or database error occured w.Write([]byte("Wrong credentials")) return } // Something else weird happened. w.WriteHeader(500) w.Write([]byte("Server Error")) } // Process logout and deletion of cookie func logoutHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError == nil { // cookie was found if isValidSession(session.Value) { // and it contains a valid session. Delete the session... destroySession(session.Value) } // and delete the cookie http.SetCookie(w, &http.Cookie{ Name: "session", Value: "", Path: "/", Expires: time.Unix(0, 0), }) } http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } // Host the challenges index file func challengesHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, redirect to frontpage readFileToResponse(w, "/challenges.html") } } // Host the access file func accessHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, redirect to frontpage readFileToResponse(w, "/access.html") } } func getChallengesHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, send out JSON array containing all challenges json, jsonErr := generateJSONFromChallenges() if jsonErr == nil { w.Write([]byte(json)) } else { log.Println(jsonErr) w.WriteHeader(500) } } } func submitFlagHandler(w http.ResponseWriter, r *http.Request) { r.ParseForm() challengeName := r.Form.Get("challengeName") flag := r.Form.Get("flag") session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, now search for the requested challenge foundChallenge := false correctFlag := false // try to find our challenge for index, challenge := range challenges { if challenge.Name == challengeName { // found challenge, check flags foundChallenge = true if challenge.Flag == flag { // our user found the flag \o/ challenges[index].FoundFlag = true correctFlag = true } else { // ow, bummer :( challenge.FlagTries++ } break } } // if we didn't find the challenge, write an error message errorString := "" if !foundChallenge { errorString = "no such challenge" } // inform our client jsonAnswer, _ := json.Marshal(map[string]string{ "correctFlag": strconv.FormatBool(correctFlag), "error": errorString, }) w.Write([]byte(jsonAnswer)) } } // Starts the given container and return its IP address if successful func startContainerHandler(w http.ResponseWriter, r *http.Request) { r.ParseForm() challengeName := r.Form.Get("challengeName") session, cookieNotFoundError := r.Cookie("session") errorString := "" addressString := "" if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, now search for the requested challenge for _, challenge := range challenges { if challenge.Name == challengeName { // found challenge, start container cc, err := startChallengeContainer(challenge) if err != nil { log.Println(err.Error()) errorString = "Server error." } else { addressString = cc.IP } break } } // inform our client jsonAnswer, _ := json.Marshal(map[string]string{ "error": errorString, "address": addressString, }) w.Write([]byte(jsonAnswer)) } } // Stops the given container and returns if it stopped func stopContainerHandler(w http.ResponseWriter, r *http.Request) { r.ParseForm() challengeName := r.Form.Get("challengeName") session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, now search for the requested challenge stopChallengeContainer(challengeName) } } // Returns the configuration for the VPN func getAccessHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") if cookieNotFoundError != nil || !isValidSession(session.Value) { // either no session cookie found, or it contains an invalid session token. Redirect. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } else { // valid session token found, get credentials credentials, err := getCertificate() errorString := "" if err != nil { errorString = err.Error() } // Check if we are asked to prepare the config file as download... if errorString == "" && r.URL.Query().Get("download") == "true" { // We are asked to do so, and we didn't encounter an error either w.Header().Set("Content-Disposition", "attachment; filename=config.ovpn") w.Header().Set("Content-Type", "x-openvpn-config") w.Write([]byte(credentials)) } else { // Normal operation - provide JSON formatted data jsonAnswer, _ := json.Marshal(map[string]string{ "error": errorString, "credentials": credentials, }) w.Write([]byte(jsonAnswer)) } } }