Beispiel #1
0
// Permission is responsable for checking if the user is allowed to send requests to the
// Web client
func (i *Permission) Before(w http.ResponseWriter, r *http.Request) {
	// When there's nobody in the whitelist, everybody is allowed
	if len(ACL) == 0 {
		return
	}

	var clientAddress string

	xff := r.Header.Get("X-Forwarded-For")
	xff = strings.TrimSpace(xff)

	if len(xff) > 0 {
		xffParts := strings.Split(xff, ",")
		if len(xffParts) == 1 {
			clientAddress = strings.TrimSpace(xffParts[0])
		} else if len(xffParts) > 1 {
			clientAddress = strings.TrimSpace(xffParts[len(xffParts)-2])
		}

	} else {
		clientAddress = strings.TrimSpace(r.Header.Get("X-Real-IP"))
	}

	if len(clientAddress) == 0 {
		var err error
		clientAddress, _, err = net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			log.Printf("Error checking CIDR whitelist. Details: Remote IP address '%s' could not be parsed. %s", r.RemoteAddr, err)
			return
		}

	}

	ip := net.ParseIP(clientAddress)
	if ip == nil {
		// Something wrong, because the REST server could not identify the remote address properly. This
		// is really awkward, because this is a responsability of the server, maybe this error will
		// never be throw
		w.WriteHeader(http.StatusInternalServerError)
		log.Printf("Error checking CIDR whitelist. Details: IP address '%s' could not be parsed.", clientAddress)
		return
	}

	for _, cidr := range ACL {
		if cidr.Contains(ip) {
			return
		}
	}

	w.WriteHeader(http.StatusForbidden)
}
Beispiel #2
0
// We don't start scheduler in init function anymore because for tests we want to change
// parameters before the scheduler starts
func Start() {
	ticker := time.NewTicker(SchedulerExecutionInterval)
	go func() {
		defer func() {
			// Something went really wrong while scheduling. Log the error stacktrace and move out
			if r := recover(); r != nil {
				const size = 64 << 10
				buf := make([]byte, size)
				buf = buf[:runtime.Stack(buf, false)]
				log.Printf("Panic detected while scheduling. Details: %v\n%s", r, buf)
			}
		}()

		for {
			select {
			case <-ticker.C:
				jobsMutex.Lock()
				for index, job := range jobs {
					// The execution time is not going to be so exactly
					if time.Now().UTC().After(job.NextExecution) {
						// Next execution time is defined, let's use it as reference so that the job
						// is always executed near the desired time
						jobs[index].NextExecution = job.NextExecution.Add(job.Interval)
						go job.Task()
					}
				}
				jobsMutex.Unlock()
			}
		}
	}()
}
Beispiel #3
0
func Start(listeners []net.Listener) error {
	// Initialize language configuration file
	if err := loadMessages(); err != nil {
		return err
	}

	// Initialize CIDR whitelist
	if err := loadACL(); err != nil {
		return err
	}

	// Handy logger should use the same logger of the Shelter system
	handy.Logger = log.Logger

	mux := handy.NewHandy()
	mux.Recover = func(r interface{}) {
		const size = 64 << 10
		buf := make([]byte, size)
		buf = buf[:runtime.Stack(buf, false)]
		log.Printf("REST panic detected. Details: %v\n%s", r, buf)
	}

	for pattern, handler := range handler.Routes {
		mux.Handle(pattern, handler)
	}

	server := http.Server{
		Handler:      mux,
		ReadTimeout:  time.Duration(config.ShelterConfig.RESTServer.Timeouts.ReadSeconds) * time.Second,
		WriteTimeout: time.Duration(config.ShelterConfig.RESTServer.Timeouts.WriteSeconds) * time.Second,
	}

	for _, v := range listeners {
		// We are not checking the error returned by Serve, because if we check for some
		// reason the HTTP server stop answering the requests
		go server.Serve(v)
	}

	return nil
}
Beispiel #4
0
func Start(listeners []net.Listener) error {
	// Initialize CIDR whitelist
	if err := loadACL(); err != nil {
		return err
	}

	// Static handler must be called directly because it uses configuration file values to
	// configure the root path. For that reason we can't build it in the init function
	handler.StartStaticHandler()

	// Handy logger should use the same logger of the Shelter system
	handy.Logger = log.Logger

	mux := handy.NewHandy()
	mux.Recover = func(r interface{}) {
		const size = 64 << 10
		buf := make([]byte, size)
		buf = buf[:runtime.Stack(buf, false)]
		log.Printf("Web client panic detected. Details: %v\n%s", r, buf)
	}

	for pattern, handler := range handler.Routes {
		mux.Handle(pattern, handler)
	}

	server := http.Server{
		Handler: mux,
	}

	for _, v := range listeners {
		// We are not checking the error returned by Serve, because if we check for some
		// reason the HTTP server stop answering the requests
		go server.Serve(v)
	}

	return nil
}
Beispiel #5
0
// Notify is responsable for selecting the domains that should be notified in the system.
// It will send alert e-mails for each owner of a domain
func Notify() {
	defer func() {
		// Something went really wrong while notifying the owners. Log the error stacktrace
		// and move out
		if r := recover(); r != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			log.Printf("Panic detected while notifying the owners. Details: %v\n%s", r, buf)
		}
	}()

	log.Info("Start notification job")
	defer func() {
		log.Info("End notification job")
	}()

	log.Debugf("Initializing database with the parameters: URIS - %v | Name - %s | Auth - %t | Username - %s",
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
	)

	database, databaseSession, err := mongodb.Open(
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
		config.ShelterConfig.Database.Auth.Password,
	)

	if err != nil {
		log.Println("Error while initializing database. Details:", err)
		return
	}
	defer databaseSession.Close()

	domainDAO := dao.DomainDAO{
		Database: database,
	}

	domainChannel, err := domainDAO.FindAllAsyncToBeNotified(
		config.ShelterConfig.Notification.NameserverErrorAlertDays,
		config.ShelterConfig.Notification.NameserverTimeoutAlertDays,
		config.ShelterConfig.Notification.DSErrorAlertDays,
		config.ShelterConfig.Notification.DSTimeoutAlertDays,

		// TODO: Should we move this configuration parameter to a place were both modules can
		// access it. This sounds better for configuration deployment
		config.ShelterConfig.Scan.VerificationIntervals.MaxExpirationAlertDays,
	)

	if err != nil {
		log.Println("Error retrieving domains to notify. Details:", err)
		return
	}

	// Dispatch the asynchronous part of the method
	for {
		// Get domain from the database (one-by-one)
		domainResult := <-domainChannel

		// Detect errors while retrieving a specific domain. We are not going to stop all the
		// process when only one domain got an error
		if domainResult.Error != nil {
			log.Println("Error retrieving domain to notify. Details:", domainResult.Error)
			continue
		}

		// Problem detected while retrieving a domain or we don't have domains anymore
		if domainResult.Error != nil || domainResult.Domain == nil {
			break
		}

		if err := notifyDomain(domainResult.Domain); err != nil {
			log.Println("Error notifying a domain. Details:", err)
		}
	}
}
Beispiel #6
0
// Function responsible for running the domain scan system, checking the configuration of each
// domain in the database according to an algorithm. This method is synchronous and will return only
// after the scan proccess is done
func ScanDomains() {
	defer func() {
		// Something went really wrong while scanning the domains. Log the error stacktrace
		// and move out
		if r := recover(); r != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			log.Printf("Panic detected while scanning domains. Details: %v\n%s", r, buf)
		}
	}()

	log.Info("Start scan job")
	defer func() {
		log.Info("End scan job")
	}()

	log.Debugf("Initializing database with the parameters: URIS - %v | Name - %s | Auth - %t | Username - %s",
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
	)

	database, databaseSession, err := mongodb.Open(
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
		config.ShelterConfig.Database.Auth.Password,
	)

	if err != nil {
		log.Println("Error while initializing database. Details:", err)
		return
	}
	defer databaseSession.Close()

	injector := NewInjector(
		database,
		config.ShelterConfig.Scan.DomainsBufferSize,
		config.ShelterConfig.Scan.VerificationIntervals.MaxOKDays,
		config.ShelterConfig.Scan.VerificationIntervals.MaxErrorDays,
		config.ShelterConfig.Scan.VerificationIntervals.MaxExpirationAlertDays,
	)

	querierDispatcher := NewQuerierDispatcher(
		config.ShelterConfig.Scan.NumberOfQueriers,
		config.ShelterConfig.Scan.DomainsBufferSize,
		config.ShelterConfig.Scan.UDPMaxSize,
		time.Duration(config.ShelterConfig.Scan.Timeouts.DialSeconds)*time.Second,
		time.Duration(config.ShelterConfig.Scan.Timeouts.ReadSeconds)*time.Second,
		time.Duration(config.ShelterConfig.Scan.Timeouts.WriteSeconds)*time.Second,
		config.ShelterConfig.Scan.ConnectionRetries,
	)

	collector := NewCollector(
		database,
		config.ShelterConfig.Scan.SaveAtOnce,
	)

	// Create a new scan information
	model.StartNewScan()

	var scanGroup sync.WaitGroup
	errorsChannel := make(chan error, config.ShelterConfig.Scan.ErrorsBufferSize)
	domainsToQueryChannel := injector.Start(&scanGroup, errorsChannel)
	domainsToSaveChannel := querierDispatcher.Start(&scanGroup, domainsToQueryChannel)
	collector.Start(&scanGroup, domainsToSaveChannel, errorsChannel)

	// Keep track of errors for the scan information structure
	errorDetected := false

	go func() {
		for {
			select {
			case err := <-errorsChannel:
				// Detect the poison pill to finish the error listener go routine. This poison
				// pill should be sent after all parts of the scan are done and we are sure that
				// we don't have any error to log anymore
				if err == nil {
					return

				} else {
					errorDetected = true
					log.Println("Error detected while executing the scan. Details:", err)
				}
			}
		}
	}()

	// Wait for all parts of the scan to finish their job
	scanGroup.Wait()

	// Finish the error listener sending a poison pill
	errorsChannel <- nil

	scanDAO := dao.ScanDAO{
		Database: database,
	}

	// Save the scan information for future reports
	if err := model.FinishAndSaveScan(errorDetected, scanDAO.Save); err != nil {
		log.Println("Error while saving scan information. Details:", err)
	}
}