// InitServerCert initializes a PK + cert for use by a server proxy, signed by // the CA certificate. We always generate a new certificate just in case. func (ctx *CertContext) InitServerCert(host string) (err error) { if ctx.PK, err = keyman.LoadPKFromFile(ctx.PKFile); err != nil { if os.IsNotExist(err) { log.Debugf("Creating new PK at: %s", ctx.PKFile) if ctx.PK, err = keyman.GeneratePK(2048); err != nil { return } if err = ctx.PK.WriteToFile(ctx.PKFile); err != nil { return fmt.Errorf("Unable to save private key: %s", err) } } else { return fmt.Errorf("Unable to read private key, even though it exists: %s", err) } } log.Debugf("Creating new server cert at: %s", ctx.ServerCertFile) ctx.ServerCert, err = ctx.PK.TLSCertificateFor("Lantern", host, TEN_YEARS_FROM_TODAY, true, nil) if err != nil { return } err = ctx.ServerCert.WriteToFile(ctx.ServerCertFile) if err != nil { return } return nil }
func postStats(accumulators map[string]map[string]int64) error { report := map[string]interface{}{ "dims": map[string]string{ "country": country, }, } for category, accum := range accumulators { report[category] = accum } jsonBytes, err := json.Marshal(report) if err != nil { return fmt.Errorf("Unable to marshal json for stats: %s", err) } url := fmt.Sprintf(StatshubUrlTemplate, addr, id) resp, err := http.Post(url, "application/json", bytes.NewReader(jsonBytes)) if err != nil { return fmt.Errorf("Unable to post stats to statshub: %s", err) } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("Unexpected response status posting stats to statshub: %d", resp.StatusCode) } log.Debugf("Reported %s to statshub", string(jsonBytes)) return nil }
// UpdatedFrom creates a new Config by merging the given yaml into this Config. // Any servers in the updated yaml replace ones in the original Config and any // masquerade sets in the updated yaml replace ones in the original Config. func (orig *Config) UpdatedFrom(updateBytes []byte) (*Config, error) { origCopy := orig.deepCopy() updated := orig.deepCopy() err := yaml.Unmarshal(updateBytes, updated) if err != nil { return nil, fmt.Errorf("Unable to unmarshal YAML for update: %s", err) } // Need to de-duplicate servers, since yaml appends them servers := make(map[string]*client.ServerInfo) for _, server := range updated.Client.Servers { servers[server.Host] = server } updated.Client.Servers = make([]*client.ServerInfo, len(servers)) i := 0 for _, server := range servers { updated.Client.Servers[i] = server i = i + 1 } origCopy.Client.SortServers() updated.Client.SortServers() if !reflect.DeepEqual(origCopy, updated) { log.Debugf("Saving updated") err = updated.SaveToDisk() if err != nil { return nil, err } } return updated, nil }
// ServeHTTP implements the method from interface http.Handler func (client *Client) ServeHTTP(resp http.ResponseWriter, req *http.Request) { server := client.randomServer(req) log.Debugf("Using server %s to handle request for %s", server.info.Host, req.RequestURI) if req.Method == CONNECT { server.enproxyConfig.Intercept(resp, req) } else { server.reverseProxy.ServeHTTP(resp, req) } }
// Start runs a goroutine that periodically coalesces the collected statistics // and reports them to statshub via HTTP post func Start(reportingPeriod time.Duration, statshubAddr string, instanceId string, countryCode string) { alreadyStarted := !atomic.CompareAndSwapInt32(&started, 0, 1) if alreadyStarted { log.Debugf("statreporter already started, not starting again") return } period = reportingPeriod addr = statshubAddr id = instanceId country = strings.ToLower(countryCode) // We buffer the updates channel to be able to continue accepting updates while we're posting a report updatesCh = make(chan *update, 10000) accumulators = make(map[string]map[string]int64) timer := time.NewTimer(timeToNextReport()) for { select { case update := <-updatesCh: // Coalesce accum := accumulators[update.category] if accum == nil { accum = make(map[string]int64) accumulators[update.category] = accum } switch update.action { case set: accum[update.key] = update.val case add: accum[update.key] = accum[update.key] + update.val } case <-timer.C: if len(accumulators) == 0 { log.Debugf("No stats to report") } else { err := postStats(accumulators) if err != nil { log.Errorf("Error on posting stats: %s", err) } accumulators = make(map[string]map[string]int64) } timer.Reset(timeToNextReport()) } } }
func (server *Server) startServingStatsIfNecessary() bool { if server.StatServer != nil { log.Debugf("Serving stats at address: %s", server.StatServer.Addr) go server.StatServer.ListenAndServe() return true } else { log.Debug("Not serving stats (no statsaddr specified)") return false } }
// ListenAndServe makes the client listen for HTTP connections func (client *Client) ListenAndServe() error { httpServer := &http.Server{ Addr: client.Addr, ReadTimeout: client.ReadTimeout, WriteTimeout: client.WriteTimeout, Handler: client, } log.Debugf("About to start client (http) proxy at %s", client.Addr) return httpServer.ListenAndServe() }
// On windows, make sure that flashlight stops running if its parent // process has stopped. This is necessary on Windows, where child processes // don't tend to get terminated it the parent process dies unexpectedly. func init() { go func() { if *parentPID == 0 { log.Errorf("No parent PID specified, not terminating when orphaned") } parent, _ := os.FindProcess(*parentPID) if parent == nil { log.Errorf("No parent, not terminating when orphaned") return } log.Debugf("Waiting for parent %d to terminate", *parentPID) parent.Wait() log.Debug("Parent no longer running, terminating") os.Exit(0) }() }
// doVerify does the verification and returns a boolean indicating whether or // not to continue processing more verifications. func (vms *verifiedMasqueradeSet) doVerify(masquerade *Masquerade) bool { errCh := make(chan error, 2) go func() { // Limit amount of time we'll wait for a response time.Sleep(30 * time.Second) errCh <- fmt.Errorf("Timed out verifying %s", masquerade.Domain) }() go func() { start := time.Now() httpClient := HttpClient(vms.testServer, masquerade) req, _ := http.NewRequest("HEAD", "http://www.google.com/humans.txt", nil) resp, err := httpClient.Do(req) if err != nil { errCh <- fmt.Errorf("HTTP ERROR FOR MASQUERADE %v: %v", masquerade.Domain, err) return } else { body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { errCh <- fmt.Errorf("HTTP Body Error: %s", body) } else { delta := time.Now().Sub(start) log.Debugf("SUCCESSFUL CHECK FOR: %s IN %s, %s", masquerade.Domain, delta, body) errCh <- nil } } }() err := <-errCh if err != nil { log.Error(err) return true } if vms.incrementVerifiedCount() { vms.verifiedCh <- masquerade return true } return false }
// LoadFromDisk loads a Config from flashlight.yaml inside the configured // configDir with default values populated as necessary. If the file couldn't be // loaded for some reason, this function returns a new default Config. This // function assumes that flag.Parse() has already been called. func LoadFromDisk() (*Config, error) { filename := InConfigDir("flashlight.yaml") log.Debugf("Loading config from: %s", filename) cfg := &Config{filename: filename} fileInfo, err := os.Stat(filename) if err != nil { err = fmt.Errorf("Unable to stat config file %s: %s", filename, err) } else { cfg.lastFileInfo = fileInfo bytes, err := ioutil.ReadFile(filename) if err != nil { err = fmt.Errorf("Error reading config from %s: %s", filename, err) } else { err = yaml.Unmarshal(bytes, cfg) if err != nil { err = fmt.Errorf("Error unmarshaling config yaml from file %s: %s", filename, err) } } } cfg.applyDefaults() return cfg, err }
func (server *Server) ListenAndServe() error { err := server.CertContext.InitServerCert(strings.Split(server.Addr, ":")[0]) if err != nil { return fmt.Errorf("Unable to init server cert: %s", err) } // Set up an enproxy Proxy proxy := &enproxy.Proxy{ Dial: server.dialDestination, Host: server.Host, } if server.Host != "" { log.Debugf("Running as host %s", server.Host) } // Hook into stats reporting if necessary servingStats := server.startServingStatsIfNecessary() // Add callbacks to track bytes given proxy.OnBytesReceived = func(ip string, bytes int64) { statreporter.OnBytesGiven(ip, bytes) if servingStats { server.StatServer.OnBytesReceived(ip, bytes) } } proxy.OnBytesSent = func(ip string, bytes int64) { statreporter.OnBytesGiven(ip, bytes) if servingStats { server.StatServer.OnBytesSent(ip, bytes) } } proxy.Start() httpServer := &http.Server{ Handler: proxy, ReadTimeout: server.ReadTimeout, WriteTimeout: server.WriteTimeout, } log.Debugf("About to start server (https) proxy at %s", server.Addr) tlsConfig := server.TLSConfig if server.TLSConfig == nil { tlsConfig = DEFAULT_TLS_SERVER_CONFIG } cert, err := tls.LoadX509KeyPair(server.CertContext.ServerCertFile, server.CertContext.PKFile) if err != nil { return fmt.Errorf("Unable to load certificate and key from %s and %s: %s", server.CertContext.ServerCertFile, server.CertContext.PKFile, err) } tlsConfig.Certificates = []tls.Certificate{cert} listener, err := tls.Listen("tcp", server.Addr, tlsConfig) if err != nil { return fmt.Errorf("Unable to listen for tls connections at %s: %s", server.Addr, err) } // We use an idle timing listener to time out idle HTTP connections, since // the CDNs seem to like keeping lots of connections open indefinitely. idleTimingListener := idletiming.Listener(listener, httpIdleTimeout, nil) return httpServer.Serve(idleTimingListener) }
// DumpHeaders logs the given headers (request or response). func DumpHeaders(category string, headers *http.Header) { log.Debugf("%s Headers\n%s\n%s\n%s\n\n", category, HR, spew.Sdump(headers), HR) }
// Configure updates the client's configuration. Configure can be called // before or after ListenAndServe, and can be called multiple times. The // optional enproxyConfigs parameter allows explicitly specifying enproxy // configurations for the servers in ClientConfig in lieu of building them based // on the ServerInfo in ClientConfig (mostly useful for testing). func (client *Client) Configure(cfg *ClientConfig, enproxyConfigs []*enproxy.Config) { client.cfgMutex.Lock() defer client.cfgMutex.Unlock() log.Debug("Configure() called") if client.cfg != nil { if reflect.DeepEqual(client.cfg, cfg) { log.Debugf("Client configuration unchanged") return } else { log.Debugf("Client configuration changed") } } else { log.Debugf("Client configuration initialized") } client.cfg = cfg if client.verifiedSets != nil { // Stop old verifications for _, verifiedSet := range client.verifiedSets { go verifiedSet.stop() } } // Set up new verified masquerade sets client.verifiedSets = make(map[string]*verifiedMasqueradeSet) for key, masqueradeSet := range cfg.MasqueradeSets { testServer := cfg.highestQosServer(key) if testServer != nil { client.verifiedSets[key] = newVerifiedMasqueradeSet(testServer, masqueradeSet) } } // Close existing servers if client.servers != nil { for _, server := range client.servers { server.close() } } // Configure servers client.servers = make([]*server, len(cfg.Servers)) i := 0 for _, serverInfo := range cfg.Servers { var enproxyConfig *enproxy.Config if enproxyConfigs != nil { enproxyConfig = enproxyConfigs[i] } client.servers[i] = serverInfo.buildServer( cfg.DumpHeaders, client.verifiedSets[serverInfo.MasqueradeSet], enproxyConfig) i = i + 1 } // Calculate total server weights client.totalServerWeights = 0 for _, server := range client.servers { client.totalServerWeights = client.totalServerWeights + server.info.Weight } }
func (serverInfo *ServerInfo) dialerFor(masqueradeSource func() *Masquerade) func() (net.Conn, error) { dialTimeout := time.Duration(serverInfo.DialTimeoutMillis) * time.Millisecond if dialTimeout == 0 { dialTimeout = 20 * time.Second } // Note - we need to suppress the sending of the ServerName in the client // handshake to make host-spoofing work with Fastly. If the client Hello // includes a server name, Fastly checks to make sure that this matches the // Host header in the HTTP request and if they don't match, it returns // a 400 Bad Request error. sendServerNameExtension := false return func() (net.Conn, error) { masquerade := masqueradeSource() cwt, err := tlsdialer.DialForTimings( &net.Dialer{ Timeout: dialTimeout, }, "tcp", serverInfo.addressForServer(masquerade), sendServerNameExtension, serverInfo.tlsConfig(masquerade)) domain := "" if masquerade != nil { domain = masquerade.Domain } resultAddr := "" if err == nil { resultAddr = cwt.Conn.RemoteAddr().String() } if cwt.ResolutionTime > 0 { serverInfo.recordTiming("DNSLookup", cwt.ResolutionTime) if cwt.ResolutionTime > 1*time.Second { log.Debugf("DNS lookup for %s (%s) took %s", domain, resultAddr, cwt.ResolutionTime) } } if cwt.ConnectTime > 0 { serverInfo.recordTiming("TCPConnect", cwt.ConnectTime) if cwt.ConnectTime > 5*time.Second { log.Debugf("TCP connecting to %s (%s) took %s", domain, resultAddr, cwt.ConnectTime) } } if cwt.HandshakeTime > 0 { serverInfo.recordTiming("TLSHandshake", cwt.HandshakeTime) if cwt.HandshakeTime > 5*time.Second { log.Debugf("TLS handshake to %s (%s) took %s", domain, resultAddr, cwt.HandshakeTime) } } if err != nil && masquerade != nil { err = fmt.Errorf("Unable to dial masquerade %s: %s", masquerade.Domain, err) } return cwt.Conn, err } }