// Run sets up and runs the proxying HTTP server, then blocks. func (s *srv) Run(cmd *cobra.Command, args []string) { if s.vflag { fmt.Println("pvproxy version", version.Version()) return } setUpLogging(s) mux := web.New() cl := newClient(s.target, 5*time.Second) mux.Use(log.NewHTTPLogger("pvproxy")) if s.key != "" && s.cert == "" { s.cert = s.key + ".crt" } useTLS := s.key != "" && s.cert != "" if useTLS { sec := secure.New(secure.Options{ AllowedHosts: nil, // TODO allow a way to declare these SSLRedirect: false, // we have just one port to work with, so an internal redirect can't work SSLTemporaryRedirect: false, // Use 301, not 302 SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"}, // list of headers that indicate we're using TLS (which would have been set by TLS-terminating proxy) STSSeconds: 315360000, // 1yr HSTS time, as is generally recommended STSIncludeSubdomains: false, // don't include subdomains; it may not be correct in general case TODO allow config STSPreload: false, // can't know if this is correct for general case TODO allow config FrameDeny: true, // proxy is write-only, no reason this should ever happen ContentTypeNosniff: true, // again, write-only BrowserXssFilter: true, // again, write-only }) mux.Use(sec.Handler) } mux.Post("/github/push", githubIngestor(cl, cmd)) var addr string if s.bindAll { addr = ":" + strconv.Itoa(s.port) } else { addr = s.bind + ":" + strconv.Itoa(s.port) } var err error if useTLS { err = graceful.ListenAndServeTLS(addr, s.cert, s.key, mux) } else { err = graceful.ListenAndServe(addr, mux) } if err != nil { logrus.WithFields(logrus.Fields{ "system": "pvproxy", "err": err, }).Fatal("pvproxy httpd terminated") } }
func main() { root := &cobra.Command{ Use: "pvutil", } root.AddCommand(dotDumperCommand()) root.AddCommand(fixrCommand()) root.AddCommand(validateCommand()) var vflag bool root.PersistentFlags().BoolVarP(&vflag, "version", "v", false, "Print version") root.ParseFlags(os.Args) if vflag { fmt.Println("pvutil", root.Name(), "version", version.Version()) os.Exit(0) } root.Execute() }
// ListenAndServe initiates the webapp http listener. // // This blocks on the http listening loop, so it should typically be called in its own goroutine. func (s *WebAppServer) ListenAndServe(addr, pubdir, key, cert string, showVersion bool) { mf := web.New() useTLS := key != "" && cert != "" mf.Use(log.NewHTTPLogger("webapp")) if useTLS { sec := secure.New(secure.Options{ AllowedHosts: nil, // TODO allow a way to declare these SSLRedirect: false, // we have just one port to work with, so an internal redirect can't work SSLTemporaryRedirect: false, // Use 301, not 302 SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"}, // list of headers that indicate we're using TLS (which would have been set by TLS-terminating proxy) STSSeconds: 315360000, // 1yr HSTS time, as is generally recommended STSIncludeSubdomains: false, // don't include subdomains; it may not be correct in general case TODO allow config STSPreload: false, // can't know if this is correct for general case TODO allow config FrameDeny: false, // pipeviz is exactly the kind of thing where embedding is appropriate ContentTypeNosniff: true, // shouldn't be an issue for pipeviz, but doesn't hurt...probably? BrowserXssFilter: false, // really shouldn't be necessary for pipeviz }) mf.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { err := sec.Process(w, r) // If there was an error, do not continue. if err != nil { logrus.WithFields(logrus.Fields{ "system": "webapp", "err": err, }).Warn("Error from security middleware, dropping request") return } h.ServeHTTP(w, r) }) }) } // If showing version, add a middleware to do it automatically for everything if showVersion { mf.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Server", version.Version()) h.ServeHTTP(w, r) }) }) } mf.Get("/sock", s.openSocket) mf.Get("/message/:mid", s.getMessage) mf.Get("/*", http.StripPrefix("/", http.FileServer(http.Dir(pubdir)))) mf.Compile() // kick off a goroutine to grab the latest graph and listen for cancel go func() { var g system.CoreGraph for { select { case <-s.cancel: s.unsub(s.receiver) return case g = <-s.receiver: s.latest = g } } }() var err error if useTLS { err = graceful.ListenAndServeTLS(addr, cert, key, mf) } else { err = graceful.ListenAndServe(addr, mf) } if err != nil { logrus.WithFields(logrus.Fields{ "system": "webapp", "err": err, }).Fatal("ListenAndServe returned with an error") } // TODO allow returning err }
func main() { pflag.Parse() if *vflag { fmt.Println("pipeviz version", version.Version()) return } setUpLogging() // Channel to receive persisted messages from HTTP workers. 1000 cap to allow // some wiggle room if there's a sudden burst of messages and the interpreter // gets behind. interpretChan := make(chan *mlog.Record, 1000) var listenAt string if *bindAll == false { listenAt = "127.0.0.1:" } else { listenAt = ":" } var j mlog.Store var err error switch *mlstore { case "bolt": j, err = boltdb.NewBoltStore(*dbPath + "/mlog.bolt") if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Error while setting up bolt mlog storage, exiting") } case "memory": j = mem.NewMemStore() default: log.WithFields(log.Fields{ "system": "main", "storage": *mlstore, }).Fatal("Invalid storage type requested for mlog, exiting") } // Restore the graph from the mlog (or start from nothing if mlog is empty) // TODO move this down to after ingestor is started g, err := restoreGraph(j) if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Error while rebuilding the graph from the mlog") } // Kick off fanout on the master/singleton graph broker. This will bridge between // the state machine and the listeners interested in the machine's state. brokerChan := make(chan system.CoreGraph, 0) broker.Get().Fanout(brokerChan) brokerChan <- g srv := ingest.New(j, schema.Master(), interpretChan, brokerChan, MaxMessageSize) // Kick off the http message ingestor. // TODO let config/params control address go func() { if *ingestKey != "" && *ingestCert == "" { *ingestCert = *ingestKey + ".crt" } err := srv.RunHTTPIngestor(listenAt+strconv.Itoa(DefaultIngestionPort), *ingestKey, *ingestCert) if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Error while starting the ingestion http server") } }() // Kick off the intermediary interpretation goroutine that receives persisted // messages from the ingestor, merges them into the state graph, then passes // them along to the graph broker. go srv.Interpret(g) // And finally, kick off the webapp. frontend := webapp.New(broker.Get().Subscribe(), broker.Get().Unsubscribe, make(chan struct{}), j.Get) // TODO let config/params control address if *webappKey != "" && *webappCert == "" { *webappCert = *webappKey + ".crt" } go frontend.ListenAndServe(listenAt+strconv.Itoa(DefaultAppPort), *publicDir, *webappKey, *webappCert, *showVersion) // Block on goji's graceful waiter, allowing the http connections to shut down nicely. // FIXME using this should be unnecessary if we're crash-only graceful.Wait() }