Beispiel #1
0
// generateRandomBytes returns securely generated random bytes. It will return
// an error if the system's secure random number generator fails to function
// correctly, in which case the caller should not continue.
func generateRandomBytes(n int) []byte {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		lg.Warningln("could not generate secure random key, using non secure random instead")
		lg.Warningln(err)
		for i := 0; i < n; i++ {
			b[i] = byte(mrand.Intn(256))
		}
	}
	return b
}
Beispiel #2
0
func startAnalysis(clients db.Clients) {
	go func() {
		tick := time.NewTicker(10 * time.Second)

		lastID, err := clients.DB.GetLastProcessedSampleID()
		if err != nil {
			lg.Warningln(err)
		}
		lg.Infof("starting analysis from sample ID %d", lastID)

		lastPersistedID := lastID

		for range tick.C {
			results, err := clients.DB.GetSamples(uint64(lastID), "")
			if err != nil {
				lg.Fatal(err)
			}
			n := 0
			start := time.Now()
		loop:
			for s := range results {
				n++
				if s.ID > lastID {
					lastID = s.ID
				}
				if s.Type == "NewClientToken" {
					if !shared.AcceptedHost(s.Host) {
						lg.Warningln("not accepted host id:", s.ID, s.Host)
						continue loop
					}
					err := clients.DB.PublishHost(s)
					if err != nil {
						lg.Warning(err)
					}
				}
			}
			if n != 0 && lg.V(15) {
				lg.Infof("processed %d samples in %s", n, time.Since(start).String())
			}
			if lastID != lastPersistedID {
				err = clients.DB.SetLastProcessedSampleID(lastID)
				if err != nil {
					lg.Errorln(err)
				} else {
					lastPersistedID = lastID
				}
			}
		}
	}()
}
Beispiel #3
0
// errToAPIStatus converts an error to an api.Status object.
func ErrToAPIStatus(err error) *api.Status {
	switch t := err.(type) {
	case statusError:
		status := t.Status()
		if len(status.Status) == 0 {
			status.Status = api.StatusFailure
		}
		if status.Code == 0 {
			switch status.Status {
			case api.StatusSuccess:
				status.Code = http.StatusOK
			case api.StatusFailure:
				status.Code = http.StatusInternalServerError
			}
		}
		//TODO: check for invalid responses
		return &status
	default:
		status := http.StatusInternalServerError

		// Log errors that were not converted to an error status
		// by REST storage - these typically indicate programmer
		// error by not using pkg/api/errors, or unexpected failure
		// cases.
		lg.Warningln(fmt.Errorf("apiserver received an error that is not an api.Status: %v", err))

		return &api.Status{
			Status:  api.StatusFailure,
			Code:    status,
			Reason:  api.StatusReasonUnknown,
			Message: err.Error(),
		}
	}
}
Beispiel #4
0
func (s *singleUseAuthKeyStore) New() string {

	b := make([]byte, 10)
	_, err := rand.Read(b)
	if err != nil {
		lg.Warningln("could not generate secure random key, using non secure random instead")
		lg.Warningln(err)
		for i := 0; i < len(b); i++ {
			b[i] = byte(mrand.Intn(256))
		}
	}
	key := hex.EncodeToString(b)

	s.Lock()
	defer s.Unlock()
	s.entries[key] = time.Now()
	return key
}
Beispiel #5
0
func (d *DB) SetLastProcessedSampleID(id uint64) error {
	psql := squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)
	idstr := strconv.FormatUint(id, 10)

	s := psql.
		Update("central_state").
		Set("value", idstr).
		Where(squirrel.Eq{"name": "last_processed_sample_id"})
	sr, err := s.RunWith(d.cache).Exec()
	if err != nil {
		logSQLErr(err, &s)
		log.Fatal(err)
	}
	sn, err := sr.RowsAffected()
	if err != nil {
		lg.Warningln(err)
		return err
	}
	if sn > 0 {
		return nil
	}

	i := psql.
		Insert("central_state").
		Columns("name", "value").
		Values("last_processed_sample_id", idstr)

	ir, err := i.RunWith(d.cache).Exec()
	if err != nil {
		logSQLErr(err, &i)
		log.Fatal(err)
	}
	in, err := ir.RowsAffected()
	if err != nil {
		lg.Warningln(err)
		return err
	}
	if in > 0 {
		return nil
	}
	log.Fatal("unknown error")
	return nil
}
Beispiel #6
0
// Wait blocks until the underlying process is stopped
func (s *Service) Wait() {
	if s.cmd != nil {
		lg.V(10).Infof("Waiting for process %s to exit", s.ID)
		err := s.cmd.Wait()
		if err != nil {
			lg.Warningln(err)
		}
		lg.V(10).Infof("%s exited", s.ID)
	}
	s.waiter.Wait()

}
Beispiel #7
0
// AtexitKillCmd takes care of killing a command on application exit.
//
// TODO: currently this does not clean up references to dead processes, it just
// adds forever.
func AtexitKillCmd(cmd *exec.Cmd) {
	Atexit(func() {
		lg.V(10).Info("Atexit kill ", cmd.Path, cmd.Args)
		err := cmd.Process.Kill()
		if err != nil {
			lg.V(5).Info("kill failed:", err)
		}
		// TODO: possible deadlock?
		if err := cmd.Wait(); err != nil {
			lg.Warningln(err)
		}
	})
}
Beispiel #8
0
func StartAnalysis(clients db.Clients) {

	tick := time.NewTicker(5 * time.Second)
	lastID, err := clients.DB.GetLastProcessedSampleID()
	if err != nil {
		lg.Warningln(err)
	}

	for n := 0; n < 4; n++ {
		go sessionFetcher(clients)
		go samplesAnalyzer()
		go hostPublisher(clients)
	}

	lg.Infof("starting analysis from sample ID %d", lastID)

	lastPersistedID := lastID

	for range tick.C {
		results, err := clients.DB.GetSamples(uint64(lastID), "")
		if err != nil {
			lg.Errorf("database err (skipping): %v", err)
			continue
		}
		n := 0
		start := time.Now()
		for s := range results {
			n++
			if s.ID > lastID {
				lastID = s.ID
			}

			if s.Origin == "Central" && s.Type == "HTTPHeader" {
				sessionFetchC <- s.Token
			}
		}
		if n != 0 && lg.V(15) {
			lg.Infof("processed %d samples in %s", n, time.Since(start).String())
		}
		if lastID != lastPersistedID {
			err = clients.DB.SetLastProcessedSampleID(lastID)
			if err != nil {
				lg.Errorln(err)
			} else {
				lastPersistedID = lastID
			}
		}
	}

}
Beispiel #9
0
// NewUpdateChecker creates and returns an UpdateChecker instance.
// The caller should then listen on the RequestC channel for UpdateRequests.
func NewUpdateChecker(name string) (*UpdateChecker, error) {

	c := &UpdateChecker{
		Interval: time.Duration(1*time.Hour + (time.Minute * (time.Duration(rand.Intn(120))))),
	}
	c.response = make(chan UpdateResult)
	c.RequestC = make(chan UpdateRequest)
	c.forceRequestC = make(chan bool)

	lg.Infof("Setting up update timer for %s every %f minute(s) ",
		name, c.Interval.Minutes())
	ticker := time.NewTicker(c.Interval)
	go func() {
		for {
			select {
			case <-c.forceRequestC:
				if !c.active {
					continue
				}
				c.RequestC <- UpdateRequest{
					ResponseC: c.response,
				}
			case <-ticker.C:
				if !c.active {
					continue
				}
				c.RequestC <- UpdateRequest{
					ResponseC: c.response,
				}
			case response := <-c.response:
				c.LastCheck = time.Now()
				switch response {
				case UpdateSuccess:
					lg.V(5).Infoln("UpdateSuccess")
					c.LastUpdate = c.LastCheck
					c.LastFailedCheck = time.Time{}
				case UpdateError:
					lg.Warningln("update check failed")
					c.LastFailedCheck = c.LastCheck
					<-time.After(3*time.Second + time.Duration(rand.Intn(5)))
					go func() {
						c.forceRequestC <- true
					}()
				}
			}

		}
	}()
	return c, nil
}
Beispiel #10
0
// Wait blocks until the underlying process is stopped
func (s *Service) wait() {
	if s.isCopy {
		lg.Fatal("wait called on copy of service!")
	}
	if s.cmd != nil {
		lg.V(10).Infof("Waiting for process %s to exit", s.ID)
		err := s.cmd.Wait()
		if err != nil {
			lg.Warningln(err)
		}
		lg.V(10).Infof("%s exited", s.ID)
	}
	s.waiter.Wait()

}
Beispiel #11
0
func connect(connection shared.Connection, authKey string) {

	defaultTransportM.RLock()
	if defaultTransport != nil {
		err := defaultTransport.Remove()
		if err != nil {
			lg.Warningln(err)
		}
	}
	defaultTransportM.RUnlock()

	event := newConnectionEventhistory(connection)

	event.newState(ServiceInit)
	ts, err := NewTransportService(connection)
	ts.authSecret = authKey
	if lg.V(6) {
		ts.SetVerbose()
	}
	ts.SetBindaddr(DefaultProxyBindAddr)

	if err != nil {
		event.newState(Failed)
		event.newState(Ended)
		return
	}
	event.newState(ServiceStart)
	event.ServiceID = ts.Service.ID
	err = ts.Start()
	if err != nil {
		event.newState(Failed)
		event.newState(Ended)
		return
	}
	response := ts.Service.Response
	if response["protocol"] != "socks5" {
		event.newState(WrongProtocol)
		event.newState(Failed)
	}

	event.newState(Test)
	go testConn(&event)

	defaultTransportM.Lock()
	defaultTransport = ts
	defaultTransportM.Unlock()

}
Beispiel #12
0
// NewDebugResponse creates a filled DebugResponse struct
func NewDebugResposne(version string, config interface{}) *DebugResponse {
	ID, err := shared.SecureRandomString(12)
	if err != nil {
		panic("could not generate random number")
	}
	response := &DebugResponse{
		Header: DebugHeader{
			Cmd:       filepath.Base(os.Args[0]),
			ID:        ID,
			Version:   version,
			CreatedAt: time.Now(),
			OS:        runtime.GOOS,
			Arch:      runtime.GOARCH,
			GoVersion: runtime.Version(),
		},
		Config: config,
	}

	getProfile := func(name string) []string {
		buf := bytes.NewBuffer(nil)
		err := pprof.Lookup(name).WriteTo(buf, 2)
		if err != nil {
			lg.Errorln(err)
		} else {
			return strings.Split(
				buf.String(),
				"\n")
		}
		return []string{}
	}

	response.Heap = getProfile("heap")
	response.GoRoutines = getProfile("goroutine")
	response.ThreadCreate = getProfile("threadcreate")
	response.Block = getProfile("block")
	// memlog should be last so that it can catch errors up to the point of
	// collection.
	response.Log = lg.Memlog()

	if PublicKey != "" {
		response.Encrypt(PublicKey)
	} else {
		lg.Warningln("PublicKey not set, exporting unencrypted debug log")
	}

	return response

}
Beispiel #13
0
// GetSuggestions returns all local/remote suggestion sessions, can be filtered
// by time of creation.
func GetSuggestions(w rest.ResponseWriter, r *rest.Request) {
	var res []client.Suggestion
	all := client.AllSuggestions()
	after := r.URL.Query().Get("after")

	if after == "" {
		res = all
	} else {
		after, err := time.Parse(time.RFC3339, after)
		if err != nil {
			lg.Warningln("Wrong time format: %s", after)
			return
		}
		for _, v := range all {
			if v.CreatedAt.After(after) {
				res = append(res, v)
			}
		}
	}
	w.WriteJson(res)
}
Beispiel #14
0
// StartBinaryUpgradeChecker checks for binary upgrades when the connection is
// up and on a schedule.
//
// This function runs in it's own goroutine.
func StartBinaryUpgradeChecker(diffsBaseURL string) {
	if !upgradeEnabled {
		lg.Infoln("binary upgrades are disabled using the command line flag")
		return
	}
	if VERSION == "" {
		lg.Warningln("VERSION not set, binary upgrades are disabled")
		return
	}
	_, err := version.NewVersion(VERSION)
	if err != nil {
		lg.Warningf("VERSION '%s' is not a valid semver version, binary upgrades are disabled: %v", VERSION, err)
		return
	}

	connectionEventListener := make(chan service.ConnectionHistory)
	uChecker, _ := NewUpdateChecker("binary")
	service.AddListener(connectionEventListener)
	for {
		select {
		// Update when the transport connection comes up
		case event := <-connectionEventListener:
			if event.IsUp() {
				uChecker.Activate()
				uChecker.UpdateNow()
			}

		// Update by request of the update checker
		case request := <-uChecker.RequestC:
			err := upgradeBinaryCheck(diffsBaseURL)
			if err != nil {
				lg.Errorln(err)
				request.ResponseC <- UpdateError
			} else {
				request.ResponseC <- UpdateSuccess
			}
		}
	}
}
Beispiel #15
0
func StartConnectionManager(authKey string) {
	go updateTransportOkLoop()

	listeners := make([]chan ConnectionHistory, 0)

	// TODO: Test on irregular intervals
	reverifyTicker := time.NewTicker(connectionManagerTimings.TestTransportTicker)

	// the key is Connection.UUID
	histories := make(map[string][]ConnectionEvent)
	currents := make(map[string]*ConnectionEvent)

	currentConnectionID := ""
	currentConnectionsMu.Lock()
	currentConnIdx := 0
	if len(currents) < 1 {
		go connect(currentConnections[currentConnIdx], authKey)
		currentConnectionID = currentConnections[currentConnIdx].ID
	}
	currentConnectionsMu.Unlock()

	firstUpNoProblems := true // no need to spam the user with popups

	var reconnectTimer *time.Timer
	var connectionTestRunning bool
loop:

	for {
	s:
		select {
		case <-stopCh:
			lg.Infoln("connection manager shut down")
			break loop

		case <-reconnectCh:
			currentConnectionsMu.Lock()
			if len(currentConnections) < 1 {
				currentConnectionsMu.Unlock()
				lg.Warningln("No connections enabled")
				reconnectTimer = time.AfterFunc(connectionManagerTimings.ReconnectTransportDelay, func() {
					reconnectCh <- true
				})
				break s
			}
			currentConnIdx = (currentConnIdx + 1) % (len(currentConnections))
			c := currentConnections[currentConnIdx]
			lg.V(10).Infof("reconnecting to transport %v", c)
			go connect(currentConnections[currentConnIdx], authKey)
			currentConnectionID = c.ID
			currentConnectionsMu.Unlock()

		case listener := <-addNetworkStateListener:
			listeners = append(listeners, listener)

		case event := <-connectionEvents:
			if _, v := histories[event.Connection.ID]; !v {
				histories[event.Connection.ID] = make([]ConnectionEvent, 0)
			} else if len(histories[event.Connection.ID]) > 20 {
				lg.V(5).Infoln("trimming connection history")
				histories[event.Connection.ID] = histories[event.Connection.ID][:20]
			}
			histories[event.Connection.ID] = append(histories[event.Connection.ID], event)
			currents[event.Connection.ID] = &event
			emitEvent := ConnectionHistory{
				History: histories[event.Connection.ID],
			}

			if lg.V(3) {
				switch event.State {
				case Failed, TestFailed:
					lg.Warningln("event  ", event.Connection.ID, ": ", event.State)
				default:
					lg.Infoln("event  ", event.Connection.ID, ": ", event.State)
				}
			}
			switch event.State {
			case Up:
				if firstUpNoProblems {
					firstUpNoProblems = false
				} else {
					ui.Notify("transport_connected_message")
				}
			case Failed:
				firstUpNoProblems = false
				ui.Notify("transport_error_message")
			case TestFailed:
				firstUpNoProblems = false
				ui.Notify("transport_retry")
			case Ended:
				delete(currents, event.Connection.ID)
				lg.V(15).Infoln("waiting before sending reconnect")
				if reconnectTimer != nil {
					reconnectTimer.Stop()
				}
				reconnectTimer = time.AfterFunc(connectionManagerTimings.ReconnectTransportDelay, func() {
					reconnectCh <- true
				})
			}
			lg.V(7).Infoln("Forwarding connection event to listeners", emitEvent.Current())
			for _, l := range listeners {
				l <- emitEvent
			}

		case <-reverifyTicker.C:
			if !connectionTestRunning {
				conn, ok := currents[currentConnectionID]
				if ok && conn.State == Up {
					connectionTestRunning = true
					go func() {
						err := testConn(conn)
						if err != nil {
							lg.Warningln(err)
							connectionTestedCh <- false
						}
						connectionTestedCh <- true
					}()
				}
			}

		case <-connectionTestedCh:
			connectionTestRunning = false
		}
	}
}
Beispiel #16
0
func samplesAnalyzer() {
loop:
	for samples := range sampleAnalysisC {
		var (
			newTokenSample db.Sample
			clientSamples  = make(map[string]db.Sample, 0)
			centralSamples = make(map[string]db.Sample, 0)
		)

		// organize input data
		for _, s := range samples {
			switch s.Type {
			case "NewClientToken":
				if newTokenSample.Token != "" {
					lg.Error("got more than one newTokenSample, aborting")
					continue loop
				}
				newTokenSample = s
			case "HTTPHeader", "DNSQuery":
				switch s.Origin {
				case "Central":
					centralSamples[s.Type] = s
				case "Client":
					clientSamples[s.Type] = s
				}
			default:
				lg.Errorf("dont know how to handle %d %s, skipping", s.ID, s.Type)
			}
		}

		// validate that wanted data types are available
		if newTokenSample.Token == "" {
			lg.Errorln("No newTokenSample, aborting")
			continue loop
		}
		if !shared.AcceptedHost(newTokenSample.Host) {
			lg.Warningln("not accepted host id:", newTokenSample.ID, newTokenSample.Host)
			continue loop
		}
		for _, smap := range []map[string]db.Sample{clientSamples, centralSamples} {
			for _, stype := range []string{"HTTPHeader"} {
				if _, ok := smap[stype]; !ok {
					lg.Errorf("missing %s, cannot analyse", stype)
					continue loop
				}
			}
		}

		// parse data
		clientSample := clientSamples["HTTPHeader"]
		var clientHeader measure.HTTPHeaderResult
		centralSample := centralSamples["HTTPHeader"]
		var centralHeader measure.HTTPHeaderResult

		if err := json.Unmarshal(clientSample.Data, &clientHeader); err != nil {
			lg.Errorln(err)
			continue loop
		}
		if err := json.Unmarshal(centralSample.Data, &centralHeader); err != nil {
			lg.Error(err)
			continue loop
		}

		// score data
		score := scoreHTTPHeaders(clientHeader, centralHeader)
		lg.V(10).Infof("session:%s %s", newTokenSample.Token, score)

		if score.Score() >= 0.5 {
			lg.Infoln("publishing session", newTokenSample.Token)
			hostPublishC <- newTokenSample
		} else {
			lg.Infoln("not publishing session", newTokenSample.Token)
		}
	}
}
Beispiel #17
0
// SubmitSuggestion initiates the comminication with Central for a Submission
// session.
func SubmitSuggestion(w rest.ResponseWriter, r *rest.Request) {

	// // TODO This is the response that must be forwarded from central/api and parsed by client and passed on to the browser.
	// apiutils.WriteRestError(w,
	// 	apierrors.NewInvalid("object", "suggestion",
	// 		fielderrors.ValidationErrorList{
	// 			fielderrors.NewFieldValueNotSupported("URL", "...", []string{})}))
	// return

	ID := r.PathParam("id")
	suggestion, ok := client.GetSuggestion(ID)
	if !ok {
		apiutils.WriteRestError(w, apierrors.NewNotFound("suggestion", ID))
		return
	}
	wanip := shared.GetPublicIPAddr()
	if wanip == nil {
		lg.Warning("could not resolve public ip addr")
	}

	conf := clientconfig.Get()
	restclient, err := NewRestClient()
	if err != nil {
		apiutils.WriteRestError(w, apierrors.NewInternalError(err))
		return
	}

	tokenResp, err := suggestion.RequestToken(
		restclient, wanip, conf.Settings.Local.CountryCode)
	if err != nil {
		if apiutils.IsNetError(err) {
			apiutils.WriteRestError(w, apierrors.NewServerTimeout("alkasir-central", "request-submission-token", 0))
		} else {
			apiutils.WriteRestError(w, apierrors.NewInternalError(err))
		}
		return
	}
	n, err := suggestion.SendSamples(restclient)
	if err != nil {
		lg.Warningln("error sending samples", err.Error())
	}
	lg.V(5).Infof("sent %d samples", n)

	// continue sending samples if future measuremetns are expected to come
	prepared, err := suggestion.Prepared()
	if err != nil {
		lg.Errorln(err)
	} else if !prepared {
		lg.V(5).Infof("all samples not collected, will try to send the rest when they are done")
		go func(s client.Suggestion) {
			start := time.Now()
			t := time.NewTicker(30 * time.Second)
			defer t.Stop()

		submitSamples:
			for range t.C {
				if time.Now().After(start.Add(15 * time.Minute)) {
					lg.Errorln("Stopping trying to send additional samples")
					return
				}

				prepared, err := suggestion.Prepared()
				if err != nil {
					lg.Errorln(err)
					return
				}
				if prepared {
					restclient, err := NewRestClient()
					if err != nil {
						continue submitSamples
					}
					n, err := suggestion.SendSamples(restclient)
					lg.V(5).Infof("sent %d samples", n)
					if err != nil {
						lg.Warningln("error sending samples", err.Error())
						continue submitSamples
					}
					return
				}
			}
		}(suggestion)
	}

	u, err := url.Parse(suggestion.URL)
	if err != nil {
		lg.Errorln(err)
	} else {
		err := clientconfig.Update(func(conf *clientconfig.Config) error {
			conf.BlockedHosts.Add(u.Host)
			lastBlocklistChange = time.Now()

			pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts,
				conf.BlockedHosts.Hosts)
			return nil
		})
		if err != nil {
			lg.Errorln(err)
		}
	}
	w.WriteJson(tokenResp)
}
Beispiel #18
0
// Initialize service
func (s *Service) initService() error {
	cmd := s.cmd
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	s.stdout = stdout
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	s.stderr = stderr

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	s.stdin = stdin

	if lg.V(5) {
		alkasirEnv := ""
		for _, v := range s.cmd.Env {
			if strings.HasPrefix(v, "ALKASIR_") {
				alkasirEnv += v + " "
			}
		}
		lg.Infof("Starting service: %s %s", alkasirEnv, cmd.Path)
	}
	err = cmd.Start()
	if err != nil {
		return err
	}

	scanner := bufio.NewScanner(stdout)
	var transportErrMsg string
	transportErr := false
	var line string
	for scanner.Scan() {
		line = scanner.Text()

		lg.V(5).Infoln("DBG: ", line)

		if errorM.MatchString(line) {
			transportErr = true
			transportErrMsg = line
			return errors.New("error: " + transportErrMsg)
		} else if doneM.MatchString(line) {
			break
		} else if exposeM.MatchString(line) {
			match := exposeM.FindStringSubmatch(line)
			s.Response["bindaddr"] = match[3]
			s.Response["protocol"] = match[2]
			s.registerMethod(match[1], match[2], match[3])
		} else if versionM.MatchString(line) {
		} else if parentM.MatchString(line) {
			match := parentM.FindStringSubmatch(line)
			s.Response["parentaddr"] = match[3]
		} else {
			lg.Infoln("not handled line:", line)
			return errors.New("unhandeled line")
		}
	}
	if transportErr {
		err := cmd.Wait()
		if err != nil {
			lg.Warningln(err)
		}
		lg.Fatal(transportErrMsg)
		return errors.New("transport err")
	}
	return err
}
Beispiel #19
0
// Run runs the initialized server.
func Run() {
	var wg sync.WaitGroup

	// start monitor server
	go startMonitoring(*monitorBindAddr)

	// start the getpublic ip updater.
	go func() {
		_ = shared.GetPublicIPAddr()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		lg.V(2).Infoln("Loading recent sessions from postgres...")
		recents, err := sqlDB.RecentSuggestionSessions(20000)
		if err != nil {
			lg.Fatal(err)
		}
		db.SessionTokens.Reset(recents)
		lg.V(2).Infof("Loaded %d sessions from postgres...", len(recents))
		lg.Flush()

	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		conn := redisPool.Get()
		defer conn.Close()
		lg.V(2).Infoln("BGPDump refresh started...")
		n, err := internet.RefreshBGPDump(conn)
		lg.V(2).Infof("BGPDump refresh ended, %d items added.", n)
		lg.Flush()
		if err != nil {
			if *offline {
				lg.Infoln("offline", err)
			} else {
				lg.Fatal(err)
			}
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		conn := redisPool.Get()
		defer conn.Close()
		lg.V(2).Infoln("CIDRReport refresh started...")
		n, err := internet.RefreshCIDRReport(conn)
		lg.V(2).Infof("CIDRReport refresh ended, %d items added", n)
		if err != nil {
			if *offline {
				lg.Infoln("offline", err)
			} else {
				lg.Fatal(err)
			}
		}
	}()
	wg.Wait()

	// start signal handling
	wg.Add(1)
	go func() {
		ch := make(chan os.Signal)
		signal.Notify(ch, syscall.SIGINT)
		lg.Infoln(<-ch)
		wg.Done()
	}()

	internetClient := db.NewInternetClient(redisPool)
	maxmindClient := db.NewMaxmindClient(mmCountryDB, mmCityDB)

	clients := db.Clients{
		DB:       sqlDB,
		Internet: internetClient,
		Maxmind:  maxmindClient,
	}

	// start http json api server
	go func(addr string, dba db.Clients) {
		mux, err := apiMux(dba)
		lg.Info("Starting http server", addr)
		err = http.ListenAndServe(addr, mux)
		if err != nil {
			lg.Fatal(err)
		}
	}(*apiBindAddr, clients)

	// start http export api server
	go func(addr string, dba db.Clients) {
		if *exportApiSecretKey == "" {
			lg.Warningln("exportApiSecretKey flag/env not set, will not start export api server")
			b := make([]byte, 32)
			_, err := rand.Read(b)
			if err != nil {
				lg.Fatalf("random generator not functioning...")
				return
			}
			suggestedkey := base64.StdEncoding.EncodeToString(b)
			lg.Infoln("suggested export key:", suggestedkey)
			return
		}
		key, err := base64.StdEncoding.DecodeString(*exportApiSecretKey)
		if err != nil {
			lg.Fatalf("could not decode export api secret key: %s", *exportApiSecretKey)
		}

		mux, err := apiMuxExport(dba, key)
		lg.Info("Starting export api server", addr)
		err = http.ListenAndServe(addr, mux)
		if err != nil {
			lg.Fatal(err)
		}
	}(*exportApiBindAddr, clients)

	go analysis.StartAnalysis(clients)
	startMeasurer(clients)

	wg.Wait()
}
Beispiel #20
0
// initDone means handing off the service process output to it's own goroutine.
func (s *Service) initDone() error {

	lg.V(5).Infof("s.request: %+v", s.Request)
	lg.V(5).Infof("s.response: %+v", s.Response)
	s.waiter.Add(1)
	s.running = true
	go func() {
		defer func() {
			s.running = false
		}()

		go func() {
			scanner := bufio.NewScanner(s.stdout)
			for scanner.Scan() {
				if scanner.Err() != nil {
					lg.Warningln("service stdout", scanner.Err())
					break
				}
				line := scanner.Text()
				lg.Infoln(s.ID, "stdout:", line)
			}
			lg.V(20).Infof("service outch closed %s", s.ID)
		}()

		go func() {
			scanner := bufio.NewScanner(s.stderr)
			for scanner.Scan() {
				if scanner.Err() != nil {
					lg.Warningln("service stderr", scanner.Err())
					break
				}
				line := scanner.Text()
				lg.Infoln(s.ID, "stderr:", line)
			}
			lg.V(20).Infof("service stderr closed %s", s.ID)
		}()

		s.quit = make(chan bool)
		defer close(s.quit)

		select {
		case <-s.quit:
			lg.V(6).Infof("stopping service %s", s.ID)

			if err := s.stdin.Close(); err != nil {
				lg.Errorf("could not close stdin for %s: %s", s.ID, err.Error())
			}

			if err := s.stdout.Close(); err != nil {
				lg.Errorf("could not close stdout for %s: %s", s.ID, err.Error())
			}

			if err := s.stderr.Close(); err != nil {
				lg.Errorf("could not close stderr for %s: %s", s.ID, err.Error())
			}

			lg.V(10).Infof("Killing process service %s", s.ID)

		}

		lg.V(10).Infof("stopped service %s", s.ID)
		if s.removeOnStop {
			ManagedServices.remove(s)
		}
		s.waiter.Done()
	}()
	return nil
}
Beispiel #21
0
// UpgradeConfig updates the config, if needed.
func UpgradeConfig() (bool, error) {
	currentConrfigMu.Lock()

	if centralAddr == "" {
		centralAddr = "https://localhost:8080"
		lg.Warningln("WARNING centralAddr not set, defaulting to dev value: ", centralAddr)
	}

	defer currentConrfigMu.Unlock()
	if !currentConfig.configRead {
		return false, errors.New("config not read")
	}
	prevVer := currentConfig.Settings.Version

	switch currentConfig.Settings.Version {
	case 0: // when config file was created from template
		currentConfig.Settings.Version = 1
		currentConfig.Settings.Local.CentralAddr = centralAddr
		fallthrough
	case 1:
		lg.Infoln("updating configuration to v1")
		m := &modifyConnections{
			Connections: currentConfig.Settings.Connections,
			Add: []string{
				"aieyJ0Ijoib2JmczQiLCJzIjoiXCJjZXJ0PUdzVFAxVmNwcjBJeE9aUkNnUHZ0Z1JsZVJWQzFTRmpYWVhxSDVvSEhYVFJ4M25QVnZXN2xHK2RkREhKWmw4YjBOVFZ1VGc7aWF0LW1vZGU9MFwiIiwiYSI6IjEzOS4xNjIuMjIxLjEyMzo0NDMifQ==",
				"aieyJ0Ijoib2JmczQiLCJzIjoiXCJjZXJ0PTMzdXNkSUVDemFyRUpsWFpQM0w0Y2x0bi9vNXdhVUlOcHRUU0JSYk5BQVpVcVlsajhiZm5MWGYyb3BFNHE2c0NlYzY3Ync7aWF0LW1vZGU9MFwiIiwiYSI6IjQ2LjIxLjEwMi4xMDk6NDQzIn0=",
			},
			Remove: []string{
				// NOTE: old hash function used, not a current format ID
				"xFa9T1i6bJMJIvK6kxFA1xvQGfW58BY3OLkrPXbpvAY=",
			},
		}
		currentConfig.Settings.Connections = m.Update()
		currentConfig.Settings.Version = 2
		fallthrough
	case 2:
		m := &modifyConnections{
			Connections: currentConfig.Settings.Connections,
			Protect: []string{
				"z0VZS-Kx9tMfoBEyX6br19GaJgKDe0IK0i6JKyhKp2s",
				"ipJ2oW8xr9TFDvfU92qGvDaPwZttf_GSjGZ4KW7inBI",
			},
			Remove: []string{
				"Ul3D2G1dI3_Z4sLXQ9IUQdIFH4pSDyNjTwf_auy93Os",
			},
		}
		currentConfig.Settings.Connections = m.Update()
		currentConfig.Settings.Version = 3
		fallthrough
	case 3:
		key := browsercode.NewKey()
		currentConfig.Settings.Local.ClientAuthKey = key
		currentConfig.Settings.Local.ClientBindAddr = "127.0.0.1:8899"
		currentConfig.Settings.Version = 4
		fallthrough
	case 4:
		// If the beta url to central server was set, replace it with the default value
		s := md5.New()
		s.Write([]byte(currentConfig.Settings.Local.CentralAddr))
		hsum := hex.EncodeToString(s.Sum(nil))
		if hsum == "796547bd38f0d8722c2e0802de34b53f" {
			currentConfig.Settings.Local.CentralAddr = centralAddr
		}
		currentConfig.Settings.Version = 5
		fallthrough
	case 5:
		m := &modifyConnections{
			Connections: currentConfig.Settings.Connections,
			Remove: []string{
				"ipJ2oW8xr9TFDvfU92qGvDaPwZttf_GSjGZ4KW7inBI",
			},
			Add: []string{
				"aieyJ0Ijoib2JmczQiLCJzIjoiXCJjZXJ0PWpvaUcwTFdzYmNDUWIvSG5zOWlWKytvUzMvSDBORGZ0RlJDRnZNbEpiYjRNZXByYThLZ2tLS2tMWDVaK3ZIV3M4ZWliQmc7aWF0LW1vZGU9MFwiIiwiYSI6IjEyOC4xOTkuOTkuMTY0OjQ0MyJ9",
			},
		}
		currentConfig.Settings.Connections = m.Update()
		currentConfig.Settings.Version = 6
		fallthrough
	case 6:
		lg.Infoln("Settings version", currentConfig.Settings.Version)
	default:
		lg.Errorln("Future configuration version!", currentConfig.Settings.Version)
	}
	return currentConfig.Settings.Version != prevVer, nil
}
Beispiel #22
0
// Update list of blocked hosts for an IP address.
func GetUpgrade(dbclients db.Clients) func(w rest.ResponseWriter, r *rest.Request) {
	return func(w rest.ResponseWriter, r *rest.Request) {
		req := shared.BinaryUpgradeRequest{}
		err := r.DecodeJsonPayload(&req)
		// TODO: proper validation response
		if err != nil {
			apiError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if req.FromVersion == "" {
			apiError(w, "fromversion required", http.StatusInternalServerError)
			return
		}
		v, err := version.NewVersion(req.FromVersion)
		if err != nil {
			lg.Warningln(err)
			apiError(w, fmt.Sprintf("invalid fromVersion %s", req.FromVersion), http.StatusInternalServerError)
			return
		}

		if req.Artifact == "" {
			apiError(w, "artifact required", http.StatusInternalServerError)
			return

		}
		res, found, err := dbclients.DB.GetUpgrade(
			db.GetUpgradeQuery{
				Artifact: req.Artifact,
			})
		if err != nil {
			apiError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if !found {
			apiutils.WriteRestError(w,
				apierrors.NewNotFound(
					"upgrade", fmt.Sprintf("%s-%s", req.Artifact, req.FromVersion)))
			return
		}

		serverVersion, err := version.NewVersion(res.Version)
		if err != nil {
			apiError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if serverVersion.LessThan(v) || serverVersion.Equal(v) {
			apiutils.WriteRestError(w,
				apierrors.NewNotFound(
					"upgrade", fmt.Sprintf("%s-%s", req.Artifact, req.FromVersion)))
			return

		}
		w.WriteJson(shared.BinaryUpgradeResponse{
			Artifact:         res.Artifact,
			Version:          res.Version,
			CreatedAt:        res.CreatedAt,
			SHA256Sum:        res.SHA256Sum,
			ED25519Signature: res.ED25519Signature,
		})

	}
}
Beispiel #23
0
// Init does precondition check if the application can/should be started.
// Init will return an error message with reason for exit printed.
func Run() {
	if debugEnabled {
		log.Println("ALKASIR_DEBUG ENABLED!")
		err := os.Setenv("ALKASIR_DEBUG", "1")
		if err != nil {
			log.Fatal(err)
		}
	}
	if hotEnabled {
		log.Println("ALKASIR_HOT ENABLED!")
		err := os.Setenv("ALKASIR_HOT", "1")
		if err != nil {
			log.Fatal(err)
		}
	}

	// the darwin systray does not exit the main loop
	if runtime.GOOS != "darwin" {
		uiRunning.Add(1)
	}

	err := ui.Run(func() {
		Atexit(ui.Done)

		// start the getpublic ip updater.
		go func() {
			_ = shared.GetPublicIPAddr()
		}()

		if debugEnabled {
			go func() {
				err := http.ListenAndServe(
					fmt.Sprintf("localhost:%d", debugPort), nil)
				if err != nil {
					panic(err)
				}
			}()
		}

		// wipe user data
		if wipeData {
			settingsdir := clientconfig.ConfigPath()
			if settingsdir == "" {
				log.Println("[wipe] Configdir not set")
				os.Exit(1)
			}
			settingsfile := clientconfig.ConfigPath("settings.json")
			if _, err := os.Stat(settingsfile); os.IsNotExist(err) {
				log.Println("[wipe] No settings.json in configdir, will NOT wipe data")
				os.Exit(1)
			}
			log.Println("Wiping all user data")
			if err := os.RemoveAll(settingsdir); err != nil {
				log.Println(err)
			}
		}

		// Prepare logging
		logdir := clientconfig.ConfigPath("log")
		err := os.MkdirAll(logdir, 0775)
		if err != nil {
			log.Println("Could not create logging directory")
			os.Exit(1)
		}
		err = flag.Set("log_dir", logdir)
		if err != nil {
			panic(err)
		}
		lg.SetSrcHighlight("alkasir/cmd", "alkasir/pkg")
		lg.CopyStandardLogTo("INFO")

		// Start init
		if VERSION != "" {
			lg.Infoln("Alkasir v" + VERSION)
		} else {
			lg.Warningln("Alkasir dev version (VERSION not set)")
		}

		lg.V(1).Info("Log v-level:", lg.Verbosity())
		_, err = clientconfig.Read()
		if err != nil {
			lg.Infoln("Could not read config")
			exit()
		}
		lg.V(30).Infoln("settings", clientconfig.Get().Settings)

		if saveChromeExt {
			err := saveChromeExtension()
			if err != nil {
				lg.Fatal(err)
			}
		}

		{
			configChanged, err := clientconfig.UpgradeConfig()
			if err != nil {
				lg.Fatalln("Could not upgrade config", err)

			}
			clientconfig.Update(func(conf *clientconfig.Config) error {

				if clientAuthKeyFlag != "" {
					lg.Warningln("Overriding generated authKey with", clientAuthKeyFlag)
					conf.Settings.Local.ClientAuthKey = clientAuthKeyFlag
					configChanged = true
				}

				if bindAddrFlag != "" {
					lg.Warningln("Overriding configured bindAddr with", bindAddrFlag)
					conf.Settings.Local.ClientBindAddr = bindAddrFlag
					configChanged = true
				}

				if centralAddrFlag != "" {
					lg.Warningln("Overriding central server addr with", centralAddrFlag)
					conf.Settings.Local.CentralAddr = centralAddrFlag
					configChanged = true
				}

				return nil
			})

			if configChanged {
				if err := clientconfig.Write(); err != nil {
					lg.Warning(err)
				}
			}

		}
		conf := clientconfig.Get()
		loadTranslations(LanguageOptions...)
		if err := ui.Language(conf.Settings.Local.Language); err != nil {
			lg.Warningln(err)
		}

		go func() {
			select {
			case <-sigIntC:
				exit()
			case <-ui.Actions.Quit:
				exit()
			}
		}()

		for _, e := range []error{
			mime.AddExtensionType(".json", "application/json"),
			mime.AddExtensionType(".js", "application/javascript"),
			mime.AddExtensionType(".css", "text/css"),
			mime.AddExtensionType(".md", "text/plain"),
		} {
			if e != nil {
				lg.Warning(e)
			}
		}

		err = startInternalHTTPServer(conf.Settings.Local.ClientAuthKey)
		if err != nil {
			lg.Fatal("could not start internal http services")
		}

		// Connect the default transport
		service.UpdateConnections(conf.Settings.Connections)
		service.UpdateTransports(conf.Settings.Transports)
		go service.StartConnectionManager(conf.Settings.Local.ClientAuthKey)

		// TODO: async
		pac.UpdateDirectList(conf.DirectHosts.Hosts)
		pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts, conf.BlockedHosts.Hosts)
		lastBlocklistChange = time.Now()

		go StartBlocklistUpgrader()
		if upgradeDiffsBaseURL != "" {
			lg.V(19).Infoln("upgradeDiffsBaseURL is ", upgradeDiffsBaseURL)
			go StartBinaryUpgradeChecker(upgradeDiffsBaseURL)
		} else {
			lg.Warningln("empty upgradeDiffsBaseURL, disabling upgrade checks")
		}

		lg.V(5).Info("Alkasir has started")

	})
	// the darwin systray does not exit the main loop
	if runtime.GOOS != "darwin" {
		uiRunning.Done()
	}

	lg.Infoln("ui.Run ended")
	if err != nil {
		log.Println("client.Run error:", err)
	}
}
Beispiel #24
0
// SubmitSuggestion initiates the comminication with Central for a Submission
// session.
func SubmitSuggestion(w rest.ResponseWriter, r *rest.Request) {

	// // TODO This is the response that must be forwarded from central/api and parsed by client and passed on to the browser.
	// apiutils.WriteRestError(w,
	// 	apierrors.NewInvalid("object", "suggestion",
	// 		fielderrors.ValidationErrorList{
	// 			fielderrors.NewFieldValueNotSupported("URL", "...", []string{})}))
	// return

	ID := r.PathParam("id")
	suggestion, ok := client.GetSuggestion(ID)
	if !ok {
		apiutils.WriteRestError(w, apierrors.NewNotFound("suggestion", ID))
		return
	}
	wanip := shared.GetPublicIPAddr()
	if wanip == nil {
		lg.Warning("could not resolve public ip addr")
	}

	conf := clientconfig.Get()
	restclient, err := NewRestClient()
	if err != nil {
		apiutils.WriteRestError(w, apierrors.NewInternalError(err))
		return
	}

	tokenResp, err := suggestion.RequestToken(
		restclient, wanip, conf.Settings.Local.CountryCode)
	if err != nil {
		if apiutils.IsNetError(err) {
			apiutils.WriteRestError(w, apierrors.NewServerTimeout("alkasir-central", "request-submission-token", 0))
		} else {
			apiutils.WriteRestError(w, apierrors.NewInternalError(err))
		}
		return
	}
	n, err := suggestion.SendSamples(restclient)
	if err != nil {
		lg.Warningln("error sending samples", err.Error())
	}

	lg.V(5).Infoln("sent ", n)
	// FIXME PRESENTATION: just add the url locally
	u, err := url.Parse(suggestion.URL)
	if err != nil {
		lg.Errorln(err)
	} else {
		err := clientconfig.Update(func(conf *clientconfig.Config) error {
			conf.BlockedHosts.Add(u.Host)
			lastBlocklistChange = time.Now()

			pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts,
				conf.BlockedHosts.Hosts)
			return nil
		})
		if err != nil {
			lg.Errorln(err)
		}
	}
	w.WriteJson(tokenResp)
}