func (s *srv) newTLSConfig() (*tls.Config, error) { config, err := s.listener.TLSConfig() if err != nil { return nil, err } if config.NextProtos == nil { config.NextProtos = []string{"http/1.1"} } pairs := map[string]tls.Certificate{} for _, host := range s.mux.hosts { c := host.Settings.KeyPair if c == nil { continue } keyPair, err := tls.X509KeyPair(c.Cert, c.Key) if err != nil { return nil, err } if host.Settings.OCSP.Enabled { log.Infof("%v OCSP is enabled for %v, resolvers: %v", s, host, host.Settings.OCSP.Responders) r, err := s.mux.stapler.StapleHost(&host) if err != nil { log.Warningf("%v failed to staple %v, error %v", s, host, err) } else if r.Response.Status == ocsp.Good || r.Response.Status == ocsp.Revoked { keyPair.OCSPStaple = r.Staple } else { log.Warningf("%s got undefined status from OCSP responder: %v", s, r.Response.Status) } } pairs[host.Name] = keyPair } config.Certificates = make([]tls.Certificate, 0, len(pairs)) if s.defaultHost != "" { keyPair, exists := pairs[s.defaultHost] if !exists { return nil, fmt.Errorf("default host '%s' certificate is not passed", s.defaultHost) } config.Certificates = append(config.Certificates, keyPair) } for h, keyPair := range pairs { if h != s.defaultHost { config.Certificates = append(config.Certificates, keyPair) } } config.BuildNameToCertificate() return config, nil }
// Subscribe watches etcd changes and generates structured events telling vulcand to add or delete frontends, hosts etc. // It is a blocking function. func (n *ng) Subscribe(changes chan interface{}, cancelC chan bool) error { w := n.kapi.Watcher(n.etcdKey, &etcd.WatcherOptions{AfterIndex: 0, Recursive: true}) for { response, err := w.Next(n.context) if err != nil { switch err { case context.Canceled: log.Infof("Stop watching: graceful shutdown") return nil default: log.Errorf("unexpected error: %s, stop watching", err) return err } } log.Infof("%s", responseToString(response)) change, err := n.parseChange(response) if err != nil { log.Warningf("Ignore '%s', error: %s", responseToString(response), err) continue } if change != nil { log.Infof("%v", change) select { case changes <- change: case <-cancelC: return nil } } } }
// Subscribe watches etcd changes and generates structured events telling vulcand to add or delete frontends, hosts etc. // It is a blocking function. func (n *ng) Subscribe(changes chan interface{}, cancelC chan bool) error { // This index helps us to get changes in sequence, as they were performed by clients. waitIndex := uint64(0) for { response, err := n.client.Watch(n.etcdKey, waitIndex, true, nil, cancelC) if err != nil { switch err { case etcd.ErrWatchStoppedByUser: log.Infof("Stop watching: graceful shutdown") return nil default: log.Errorf("unexpected error: %s, stop watching", err) return err } } waitIndex = response.EtcdIndex + 1 log.Infof("%s", responseToString(response)) change, err := n.parseChange(response) if err != nil { log.Warningf("Ignore '%s', error: %s", responseToString(response), err) continue } if change != nil { log.Infof("%v", change) select { case changes <- change: case <-cancelC: return nil } } } }
func (s *stapler) updateStaple(e *stapleFetched) bool { s.mtx.Lock() defer s.mtx.Unlock() hs, ok := s.v[e.hostName] if !ok || hs.id != e.id { log.Infof("%v: %v replaced or removed", s, hs) // the stapler may have been replaced by concurrent call to StapleHost() // we are going to discard this stapler and it's event as it's irrelevant return false } if e.err != nil { log.Errorf("%v failed to fetch staple response for %v, error: %v", s, hs, e.err) if hs.response.Response.NextUpdate.Before(hs.s.clock.UtcNow()) { log.Errorf("%v Now: %v, next: %v retry attempts exceeded, invalidating staple %v", s, hs.s.clock.UtcNow(), hs.response.Response.NextUpdate, hs) delete(s.v, e.hostName) return true } hs.schedule(hs.s.clock.UtcNow().Add(ErrRetryPeriod)) return false } hs.response = e.re switch e.re.Response.Status { case ocsp.Good: log.Infof("%v got good status for %v", s, hs) hs.schedule(hs.userUpdate(e.re.Response.NextUpdate)) case ocsp.Revoked: // no need to reschedule if it's revoked log.Warningf("%v revoked %v", s, hs) case ocsp.Unknown, ocsp.ServerFailed: log.Warningf("%v status: %v for %v", s, e.re.Response.Status, hs) hs.schedule(hs.s.clock.UtcNow().Add(hs.period)) } return true }
func (s *Service) Start() error { log.InitWithConfig(log.Config{ Name: s.options.Log, Severity: s.options.LogSeverity.S.String(), }) log.Infof("Service starts with options: %#v", s.options) if s.options.PidPath != "" { ioutil.WriteFile(s.options.PidPath, []byte(fmt.Sprint(os.Getpid())), 0644) } if s.options.StatsdAddr != "" { var err error s.metricsClient, err = metrics.NewWithOptions(s.options.StatsdAddr, s.options.StatsdPrefix, metrics.Options{UseBuffering: true}) if err != nil { return err } } apiFile, muxFiles, err := s.getFiles() if err != nil { return err } if err := s.newEngine(); err != nil { return err } s.stapler = stapler.New() s.supervisor = supervisor.New( s.newProxy, s.ng, s.errorC, supervisor.Options{Files: muxFiles}) // Tells configurator to perform initial proxy configuration and start watching changes if err := s.supervisor.Start(); err != nil { return err } if err := s.initApi(); err != nil { return err } go func() { s.errorC <- s.startApi(apiFile) }() if s.metricsClient != nil { go s.reportSystemMetrics() } signal.Notify(s.sigC, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGUSR2, syscall.SIGCHLD) // Block until a signal is received or we got an error for { select { case signal := <-s.sigC: switch signal { case syscall.SIGTERM, syscall.SIGINT: log.Infof("Got signal '%s', shutting down gracefully", signal) s.supervisor.Stop(true) log.Infof("All servers stopped") return nil case syscall.SIGKILL: log.Infof("Got signal '%s', exiting now without waiting", signal) s.supervisor.Stop(false) return nil case syscall.SIGUSR2: log.Infof("Got signal '%s', forking a new self", signal) if err := s.startChild(); err != nil { log.Infof("Failed to start self: %s", err) } else { log.Infof("Successfully started self") } case syscall.SIGCHLD: log.Warningf("Child exited, got '%s', collecting status", signal) var wait syscall.WaitStatus syscall.Wait4(-1, &wait, syscall.WNOHANG, nil) log.Warningf("Collected exit status from child") default: log.Infof("Ignoring '%s'", signal) } case err := <-s.errorC: log.Infof("Got request to shutdown with error: %s", err) return err } } }
// initProxy reads the configuration from the engine and configures the server func initProxy(ng engine.Engine, p proxy.Proxy) error { hosts, err := ng.GetHosts() if err != nil { return err } for _, h := range hosts { if err := p.UpsertHost(h); err != nil { return err } } bs, err := ng.GetBackends() if err != nil { return err } for _, b := range bs { if err := p.UpsertBackend(b); err != nil { return err } bk := engine.BackendKey{Id: b.Id} servers, err := ng.GetServers(bk) if err != nil { return err } for _, s := range servers { if err := p.UpsertServer(bk, s); err != nil { return err } } } ls, err := ng.GetListeners() if err != nil { return err } for _, l := range ls { if err := p.UpsertListener(l); err != nil { return err } } fs, err := ng.GetFrontends() if err != nil { return err } if len(fs) == 0 { log.Warningf("No frontends found") } for _, f := range fs { if err := p.UpsertFrontend(f); err != nil { return err } fk := engine.FrontendKey{Id: f.Id} ms, err := ng.GetMiddlewares(fk) if err != nil { return err } for _, m := range ms { if err := p.UpsertMiddleware(fk, m); err != nil { return err } } } return nil }
func (st *stapler) getStaple(s *engine.HostSettings) (*StapleResponse, error) { kp := s.KeyPair cert, err := tls.X509KeyPair(kp.Cert, kp.Key) if err != nil { return nil, err } if len(cert.Certificate) < 2 { return nil, fmt.Errorf("Need at least leaf and peer certificate") } xc, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { return nil, err } xi, err := x509.ParseCertificate(cert.Certificate[1]) if err != nil { return nil, err } data, err := ocsp.CreateRequest(xc, xi, &ocsp.RequestOptions{}) if err != nil { return nil, err } servers := xc.OCSPServer if len(s.OCSP.Responders) != 0 { servers = s.OCSP.Responders } if len(servers) == 0 { return nil, fmt.Errorf("No OCSP responders specified") } var re *ocsp.Response var raw []byte for _, srv := range servers { log.Infof("OCSP about to query: %v for OCSP", srv) issuer := xi if s.OCSP.SkipSignatureCheck { log.Warningf("Bypassing signature check") // this will bypass signature check issuer = nil } re, raw, err = st.getOCSPResponse(srv, data, issuer) if err != nil { log.Errorf("Failed to get OCSP response: %v", err) continue } // it's either server failed or if re.Status != ocsp.Good && re.Status != ocsp.Revoked { log.Warningf("Got unsatisfactiory response: %v, try next server", re.Status) continue } break } if err != nil { log.Infof("OCSP fetch error: %v", err) return nil, err } log.Infof("OCSP Status: %v, this update: %v, next update: %v", re.Status, re.ThisUpdate, re.NextUpdate) return &StapleResponse{Response: re, Staple: raw}, nil }