예제 #1
0
파일: srv.go 프로젝트: huhoo/vulcand
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
}
예제 #2
0
파일: etcd.go 프로젝트: huhoo/vulcand
// 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
			}
		}
	}
}
예제 #3
0
파일: etcd.go 프로젝트: huhoo/vulcand
// 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
			}
		}
	}
}
예제 #4
0
파일: stapler.go 프로젝트: huhoo/vulcand
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
}
예제 #5
0
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
		}
	}
}
예제 #6
0
파일: supervisor.go 프로젝트: huhoo/vulcand
// 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
}
예제 #7
0
파일: stapler.go 프로젝트: huhoo/vulcand
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
}