func serveHTTPS(ctx context.Context, httpServer *http.Server) error { log.Printf("Starting TLS server on %s", *httpsAddr) httpsServer := new(http.Server) *httpsServer = *httpServer httpsServer.Addr = *httpsAddr cacheFile := "letsencrypt.cache" if !inProd { if *tlsCertFile != "" && *tlsKeyFile != "" { return httpsServer.ListenAndServeTLS(*tlsCertFile, *tlsKeyFile) } // Otherwise use Let's Encrypt, i.e. same use case as in prod } else { cacheFile = prodLECache if err := os.MkdirAll(filepath.Dir(cacheFile), 0755); err != nil { return err } } var m letsencrypt.Manager if err := m.CacheFile(cacheFile); err != nil { return err } httpsServer.TLSConfig = &tls.Config{ GetCertificate: m.GetCertificate, } log.Printf("Listening for HTTPS on %v", *httpsAddr) ln, err := net.Listen("tcp", *httpsAddr) if err != nil { return err } return httpsServer.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, httpsServer.TLSConfig)) }
func main() { checkConfiguration() setupFacebook() handleRedirections() handleFunc("/send_message", mailHandler) handleFunc("/fr.html", facebookHandler) handleFunc("/photo/", photoHandler) handleFunc("/", indexHandler) log.Printf("Server %v starting…\n", host+port) var m letsencrypt.Manager err := m.CacheFile("/home/martin/ahouhpuc/letsencrypt.cache") if err != nil { panic(err) } go http.ListenAndServe("", http.HandlerFunc(httpsRedirect)) server := &http.Server{ Addr: port, Handler: &logHandler{http.DefaultServeMux}, TLSConfig: &tls.Config{GetCertificate: m.GetCertificate}, } err = server.ListenAndServeTLS("", "") if err != nil { panic(err) } }
func StoreLEState(m *letsencrypt.Manager) { log.Debug("Storing SSL backup") log.Debug("[SSL] --> Connecting to DB") thisStore := &RedisClusterStorageManager{KeyPrefix: LEKeyPrefix, HashKeys: false} connected := thisStore.Connect() log.Debug("--> Connected to DB") if !connected { log.Error("[SSL] --> SSL Backup save failed: redis connection failed") return } state := m.Marshal() secret := rightPad2Len(config.Secret, "=", 32) cryptoText := encrypt([]byte(secret), state) rErr := thisStore.SetKey("cache", cryptoText, -1) if rErr != nil { log.Error("[SSL] --> Failed to store SSL backup: ", rErr) return } }
func StartPeriodicStateBackup(m *letsencrypt.Manager) { for range m.Watch() { // First run will call a cache save that overwrites with null data if LE_FIRSTRUN { log.Info("[SSL] State change detected, storing") StoreLEState(m) } LE_FIRSTRUN = true } }
func main() { flag.Parse() r := mux.NewRouter() // Handler for page URLs r.HandleFunc("/about", aboutHandler).Methods("GET") r.HandleFunc("/about/", aboutHandler).Methods("GET") r.HandleFunc("/blog", blogHandler).Methods("GET") r.HandleFunc("/blog/", blogHandler).Methods("GET") r.HandleFunc("/blog/{name}", singleBlogHandler).Methods("GET") r.HandleFunc("/", indexHandler).Methods("GET") r.NotFoundHandler = http.HandlerFunc(notFound) // Handler for static content (i.e. css, img, js) r.PathPrefix("/static/").Handler( http.StripPrefix( "/static", http.FileServer(http.Dir("content/static")))) // Listen and serve on `port` port_string := strconv.Itoa(*port) log.Printf("Listening on port %s\n", port_string) if *prod { log.Printf("Running TLS") var m letsencrypt.Manager m.Register("*****@*****.**", func(terms string) bool { log.Printf("Agreeing to %s ...", terms) return true }) if err := m.CacheFile("letsencrypt.cache"); err != nil { log.Fatal(err) } srv := &http.Server{ Addr: ":https", TLSConfig: &tls.Config{ GetCertificate: m.GetCertificate, }, // TODO work out this: Handler: r, } go func() { http.ListenAndServe(":http", http.HandlerFunc(letsencrypt.RedirectHTTP)) }() srv.ListenAndServeTLS("", "") } else { log.Printf("Running HTTP") http.ListenAndServe(":"+port_string, r) } }
func main() { flag.Parse() s, err := NewServer(*ruleFile, *pollInterval) if err != nil { log.Fatal(err) } httpFD, _ := strconv.Atoi(os.Getenv("RUNSIT_PORTFD_http")) httpsFD, _ := strconv.Atoi(os.Getenv("RUNSIT_PORTFD_https")) if *letsCacheFile != "" { var m letsencrypt.Manager if err := m.CacheFile(*letsCacheFile); err != nil { log.Fatal(err) } c := tls.Config{GetCertificate: m.GetCertificate} l := tls.NewListener(listen(httpsFD, ":https"), &c) go func() { log.Fatal(http.Serve(l, s)) }() } log.Fatal(http.Serve(listen(httpFD, *httpAddr), s)) }
func GetLEState(m *letsencrypt.Manager) { checkKey := "cache" thisStore := &RedisClusterStorageManager{KeyPrefix: LEKeyPrefix, HashKeys: false} connected := thisStore.Connect() log.Debug("[SSL] --> Connected to DB") if !connected { log.Error("[SSL] --> SSL Backup recovery failed: redis connection failed") return } cryptoText, rErr := thisStore.GetKey(checkKey) if rErr != nil { log.Warning("[SSL] --> No SSL backup: ", rErr) return } secret := rightPad2Len(config.Secret, "=", 32) sslState := decrypt([]byte(secret), cryptoText) m.Unmarshal(sslState) }
// Cache caches letsencrypt data for the given Manager in the Google Cloud // Storage object identified by the getURL and putURL values. // See the package comment for details on obtaining these values. func Cache(m *letsencrypt.Manager, getURL, putURL string) error { var data []byte r, err := http.Get(getURL) if err != nil { return fmt.Errorf("letscloud: reading cache: %v", err) } data, err = ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { return fmt.Errorf("letscloud: reading cache: %v", err) } if r.StatusCode == http.StatusOK && len(data) > 0 { if err := m.Unmarshal(string(data)); err != nil { return fmt.Errorf("letscloud: reading cache: %v", err) } } go func() { for range m.Watch() { req, err := http.NewRequest("PUT", putURL, strings.NewReader(m.Marshal())) if err != nil { log.Printf("letscloud: writing cache: %v", err) continue } r, err := http.DefaultClient.Do(req) if err != nil { log.Printf("letscloud: writing cache: %v", err) continue } if r.StatusCode != http.StatusOK { log.Printf("letscloud: writing cache: %v", r.Status) } } }() return nil }
func main() { // parse command line var configFile string flag.StringVar(&configFile, "config", "/etc/codegrinder/config.json", "Path to the config file") var ta, daycare bool flag.BoolVar(&ta, "ta", true, "Serve the TA role") flag.BoolVar(&daycare, "daycare", true, "Serve the daycare role") flag.Parse() if !ta && !daycare { log.Fatalf("must run at least one role (ta/daycare)") } // set config defaults Config.ToolName = "CodeGrinder" Config.ToolID = "codegrinder" Config.ToolDescription = "Programming exercises with grading" Config.LetsEncryptCache = "/etc/codegrinder/letsencrypt.cache" Config.PostgresHost = "/var/run/postgresql" Config.PostgresPort = "" Config.PostgresUsername = os.Getenv("USER") Config.PostgresPassword = "" Config.PostgresDatabase = os.Getenv("USER") // load config file if raw, err := ioutil.ReadFile(configFile); err != nil { log.Fatalf("failed to load config file %q: %v", configFile, err) } else if err := json.Unmarshal(raw, &Config); err != nil { log.Fatalf("failed to parse config file: %v", err) } Config.SessionSecret = unBase64(Config.SessionSecret) Config.DaycareSecret = unBase64(Config.DaycareSecret) // set up martini r := martini.NewRouter() m := martini.New() m.Logger(log.New(os.Stderr, "", log.LstdFlags)) m.Use(martini.Logger()) m.Use(martini.Recovery()) m.Use(martini.Static(Config.StaticDir, martini.StaticOptions{SkipLogging: true})) m.MapTo(r, (*martini.Routes)(nil)) m.Action(r.Handle) m.Use(render.Renderer(render.Options{IndentJSON: true})) store := sessions.NewCookieStore([]byte(Config.SessionSecret)) m.Use(sessions.Sessions(CookieName, store)) // sessions expire June 30 and December 31 go func() { for { now := time.Now() // expire at the end of the calendar year expires := time.Date(now.Year(), time.December, 31, 23, 59, 59, 0, time.Local) if expires.Sub(now).Hours() < 14*24 { // are we within 2 weeks of the end of the year? probably prepping for spring, // so expire next June 30 instead expires = time.Date(now.Year()+1, time.June, 30, 23, 59, 59, 0, time.Local) } else if expires.Sub(now).Hours() > (365/2+14)*24 { // is it still more than 2 weeks before June 30? probably in spring semester, // so expire this June 30 instead expires = time.Date(now.Year(), time.June, 30, 23, 59, 59, 0, time.Local) } store.Options(sessions.Options{Path: "/", Secure: true, MaxAge: int(expires.Sub(now).Seconds())}) time.Sleep(11 * time.Minute) } }() // set up TA role if ta { // make sure relevant secrets are included in config file if Config.LTISecret == "" { log.Fatalf("cannot run TA role with no LTISecret in the config file") } if Config.SessionSecret == "" { log.Fatalf("cannot run TA role with no SessionSecret in the config file") } if Config.DaycareSecret == "" { log.Fatalf("cannot run with no DaycareSecret in the config file") } // set up the database db := setupDB(Config.PostgresHost, Config.PostgresPort, Config.PostgresUsername, Config.PostgresPassword, Config.PostgresDatabase) // martini service: wrap handler in a transaction withTx := func(c martini.Context, w http.ResponseWriter) { // start a transaction tx, err := db.Begin() if err != nil { loggedHTTPErrorf(w, http.StatusInternalServerError, "db error starting transaction: %v", err) return } // pass it on to the main handler c.Map(tx) c.Next() // was it a successful result? rw := w.(martini.ResponseWriter) if rw.Status() < http.StatusBadRequest { // commit the transaction if err := tx.Commit(); err != nil { loggedHTTPErrorf(w, http.StatusInternalServerError, "db error committing transaction: %v", err) return } } else { // rollback log.Printf("rolling back transaction") if err := tx.Rollback(); err != nil { loggedHTTPErrorf(w, http.StatusInternalServerError, "db error rolling back transaction: %v", err) return } } } // martini service: to require an active logged-in session auth := func(w http.ResponseWriter, session sessions.Session) { if userID := session.Get("id"); userID == nil { loggedHTTPErrorf(w, http.StatusUnauthorized, "authentication: no user ID found in session") return } } // martini service: include the current logged-in user (requires withTx and auth) withCurrentUser := func(c martini.Context, w http.ResponseWriter, tx *sql.Tx, session sessions.Session) { rawID := session.Get("id") if rawID == nil { loggedHTTPErrorf(w, http.StatusInternalServerError, "cannot find user ID in session") return } userID, ok := rawID.(int64) if !ok { session.Clear() loggedHTTPErrorf(w, http.StatusInternalServerError, "error extracting user ID from session") return } // load the user record user := new(User) if err := meddler.Load(tx, "users", user, userID); err != nil { if err == sql.ErrNoRows { loggedHTTPErrorf(w, http.StatusUnauthorized, "user %d not found", userID) return } loggedHTTPErrorf(w, http.StatusInternalServerError, "db error: %v", err) return } // map the current user to the request context c.Map(user) } // martini service: require logged in user to be an administrator (requires withCurrentUser) administratorOnly := func(w http.ResponseWriter, currentUser *User) { if !currentUser.Admin { loggedHTTPErrorf(w, http.StatusUnauthorized, "user %d (%s) is not an administrator", currentUser.ID, currentUser.Email) return } } // martini service: require logged in user to be an author or administrator (requires withCurrentUser) authorOnly := func(w http.ResponseWriter, tx *sql.Tx, currentUser *User) { if currentUser.Admin { return } if !currentUser.Author { loggedHTTPErrorf(w, http.StatusUnauthorized, "user %d (%s) is not an author", currentUser.ID, currentUser.Name) return } } // version r.Get("/v2/version", func(w http.ResponseWriter, render render.Render) { render.JSON(http.StatusOK, &CurrentVersion) }) // LTI r.Get("/v2/lti/config.xml", GetConfigXML) r.Post("/v2/lti/problem_sets", binding.Bind(LTIRequest{}), checkOAuthSignature, withTx, LtiProblemSets) r.Post("/v2/lti/problem_sets/:unique", binding.Bind(LTIRequest{}), checkOAuthSignature, withTx, LtiProblemSet) // problem bundles--for problem creation only r.Post("/v2/problem_bundles/unconfirmed", auth, withTx, withCurrentUser, authorOnly, binding.Json(ProblemBundle{}), PostProblemBundleUnconfirmed) r.Post("/v2/problem_bundles/confirmed", auth, withTx, withCurrentUser, authorOnly, binding.Json(ProblemBundle{}), PostProblemBundleConfirmed) r.Put("/v2/problem_bundles/:problem_id", auth, withTx, withCurrentUser, authorOnly, binding.Json(ProblemBundle{}), PutProblemBundle) // problem set bundles--for problem set creation only r.Post("/v2/problem_set_bundles", auth, withTx, withCurrentUser, authorOnly, binding.Json(ProblemSetBundle{}), PostProblemSetBundle) // problem types r.Get("/v2/problem_types", auth, GetProblemTypes) r.Get("/v2/problem_types/:name", auth, GetProblemType) // problems r.Get("/v2/problems", auth, withTx, withCurrentUser, GetProblems) r.Get("/v2/problems/:problem_id", auth, withTx, withCurrentUser, GetProblem) r.Get("/v2/problems/:problem_id/steps", auth, withTx, withCurrentUser, GetProblemSteps) r.Get("/v2/problems/:problem_id/steps/:step", auth, withTx, withCurrentUser, GetProblemStep) r.Delete("/v2/problems/:problem_id", auth, withTx, withCurrentUser, administratorOnly, DeleteProblem) // problem sets r.Get("/v2/problem_sets", auth, withTx, withCurrentUser, GetProblemSets) r.Get("/v2/problem_sets/:problem_set_id", auth, withTx, withCurrentUser, GetProblemSet) r.Get("/v2/problem_sets/:problem_set_id/problems", auth, withTx, withCurrentUser, GetProblemSetProblems) r.Delete("/v2/problem_sets/:problem_set_id", auth, withTx, withCurrentUser, administratorOnly, DeleteProblemSet) // courses r.Get("/v2/courses", auth, withTx, withCurrentUser, GetCourses) r.Get("/v2/courses/:course_id", auth, withTx, withCurrentUser, GetCourse) r.Delete("/v2/courses/:course_id", auth, withTx, withCurrentUser, administratorOnly, DeleteCourse) // users r.Get("/v2/users", auth, withTx, withCurrentUser, GetUsers) r.Get("/v2/users/me", auth, withTx, withCurrentUser, GetUserMe) r.Get("/v2/users/me/cookie", auth, GetUserMeCookie) r.Get("/v2/users/:user_id", auth, withTx, withCurrentUser, GetUser) r.Get("/v2/courses/:course_id/users", auth, withTx, withCurrentUser, GetCourseUsers) r.Delete("/v2/users/:user_id", auth, withTx, withCurrentUser, administratorOnly, DeleteUser) // assignments r.Get("/v2/users/:user_id/assignments", auth, withTx, withCurrentUser, GetUserAssignments) r.Get("/v2/courses/:course_id/users/:user_id/assignments", auth, withTx, withCurrentUser, GetCourseUserAssignments) r.Get("/v2/assignments/:assignment_id", auth, withTx, withCurrentUser, GetAssignment) r.Delete("/v2/assignments/:assignment_id", auth, withTx, withCurrentUser, administratorOnly, DeleteAssignment) // commits r.Get("/v2/assignments/:assignment_id/problems/:problem_id/commits/last", auth, withTx, withCurrentUser, GetAssignmentProblemCommitLast) r.Get("/v2/assignments/:assignment_id/problems/:problem_id/steps/:step/commits/last", auth, withTx, withCurrentUser, GetAssignmentProblemStepCommitLast) r.Delete("/v2/commits/:commit_id", auth, withTx, withCurrentUser, administratorOnly, DeleteCommit) // commit bundles r.Post("/v2/commit_bundles/unsigned", auth, withTx, withCurrentUser, binding.Json(CommitBundle{}), PostCommitBundlesUnsigned) r.Post("/v2/commit_bundles/signed", auth, withTx, withCurrentUser, binding.Json(CommitBundle{}), PostCommitBundlesSigned) } // set up daycare role if daycare { // make sure relevant secrets are included in config file if Config.DaycareSecret == "" { log.Fatalf("cannot run with no DaycareSecret in the config file") } // attach to docker and try a ping var err error dockerClient, err = docker.NewVersionedClient("unix:///var/run/docker.sock", "1.18") if err != nil { log.Fatalf("NewVersionedClient: %v", err) } if err = dockerClient.Ping(); err != nil { log.Fatalf("Ping: %v", err) } r.Get("/v2/sockets/:problem_type/:action", SocketProblemTypeAction) } // start redirecting http calls to https log.Printf("starting http -> https forwarder") go http.ListenAndServe(":http", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // get the address of the client addr := r.Header.Get("X-Real-IP") if addr == "" { addr = r.Header.Get("X-Forwarded-For") if addr == "" { addr = r.RemoteAddr } } // make sure the request is for the right host name if Config.Hostname != r.Host { loggedHTTPErrorf(w, http.StatusNotFound, "http request to invalid host: %s", r.Host) return } var u url.URL = *r.URL u.Scheme = "https" u.Host = Config.Hostname log.Printf("redirecting http request from %s to %s", addr, u.String()) http.Redirect(w, r, u.String(), http.StatusMovedPermanently) })) // set up letsencrypt lem := letsencrypt.Manager{} if err := lem.CacheFile(Config.LetsEncryptCache); err != nil { log.Fatalf("Setting up LetsEncrypt: %v", err) } lem.SetHosts([]string{Config.Hostname}) if !lem.Registered() { log.Printf("registering with letsencrypt") if err := lem.Register(Config.LetsEncryptEmail, nil); err != nil { log.Fatalf("Registering with LetsEncrypt: %v", err) } } // start the https server log.Printf("accepting https connections") server := &http.Server{ Addr: ":https", Handler: m, TLSConfig: &tls.Config{ MinVersion: tls.VersionTLS10, GetCertificate: lem.GetCertificate, }, } if err := server.ListenAndServeTLS("", ""); err != nil { log.Fatalf("ListenAndServeTLS: %v", err) } }
// ListenAndServe runs the registry's HTTP server. func (registry *Registry) ListenAndServe() error { config := registry.config ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr) if err != nil { return err } if config.HTTP.TLS.Certificate != "" || config.HTTP.TLS.LetsEncrypt.CacheFile != "" { tlsConf := &tls.Config{ ClientAuth: tls.NoClientCert, NextProtos: []string{"http/1.1"}, MinVersion: tls.VersionTLS10, PreferServerCipherSuites: true, CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_AES_256_CBC_SHA, }, } if config.HTTP.TLS.LetsEncrypt.CacheFile != "" { if config.HTTP.TLS.Certificate != "" { return fmt.Errorf("cannot specify both certificate and Let's Encrypt") } var m letsencrypt.Manager if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil { return err } if !m.Registered() { if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil { return err } } tlsConf.GetCertificate = m.GetCertificate } else { tlsConf.Certificates = make([]tls.Certificate, 1) tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key) if err != nil { return err } } if len(config.HTTP.TLS.ClientCAs) != 0 { pool := x509.NewCertPool() for _, ca := range config.HTTP.TLS.ClientCAs { caPem, err := ioutil.ReadFile(ca) if err != nil { return err } if ok := pool.AppendCertsFromPEM(caPem); !ok { return fmt.Errorf("Could not add CA to pool") } } for _, subj := range pool.Subjects() { context.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj)) } tlsConf.ClientAuth = tls.RequireAndVerifyClientCert tlsConf.ClientCAs = pool } ln = tls.NewListener(ln, tlsConf) context.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr()) } else { context.GetLogger(registry.app).Infof("listening on %v", ln.Addr()) } return registry.server.Serve(ln) }
func main() { var configFile string flag.StringVar(&configFile, "config", "", "Configuration file") flag.Parse() if configFile == "" { configFile = findConfig() if configFile == "" { log.Println("no configuration file found") log.Println("should be in the home directory and named any of:") log.Println() log.Println("gomuxserver.json") log.Println(".gomuxserver.json") log.Println(".config/gomuxserver.json") log.Println() log.Println("or in the working directory and named:") log.Println() log.Println("gomuxserver.json") log.Println(".gomuxserver.json") log.Println() log.Fatalln("or passed via the -config flag") } } conf = &config{ LogFile: "", EntryConfigs: make([]map[string]interface{}, 0), entries: make([]*handlerConfig, 0), modTime: time.Unix(0, 0), } initialConf, err := readConfig(configFile) if err != nil { log.Fatalln(err) } loadConfig(initialConf) go monitorConfig(configFile) if conf.LetsEncrypt != nil { go func() { err = http.ListenAndServe(conf.HttpListen, http.HandlerFunc(letsencrypt.RedirectHTTP)) if err != nil { log.Fatalln(err) } }() var m letsencrypt.Manager if conf.LetsEncrypt.Cache != "" { err = m.CacheFile(conf.LetsEncrypt.Cache) if err != nil { log.Fatal(err) } } if conf.LetsEncrypt.Email != "" { m.Register(conf.LetsEncrypt.Email, nil) } srv := &http.Server{ Addr: conf.HttpsListen, TLSConfig: &tls.Config{ GetCertificate: m.GetCertificate, }, Handler: http.HandlerFunc(mainHandler), } err = srv.ListenAndServeTLS("", "") if err != nil { log.Fatalln(err) } } else { err = http.ListenAndServe(conf.HttpListen, http.HandlerFunc(mainHandler)) if err != nil { log.Fatalln(err) } } }