bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/cmd/synsec/output.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"sync"
     8  	"time"
     9  
    10  	"bitbucket.org/Aishee/synsec/pkg/apiclient"
    11  	"bitbucket.org/Aishee/synsec/pkg/csconfig"
    12  	"bitbucket.org/Aishee/synsec/pkg/cwhub"
    13  	"bitbucket.org/Aishee/synsec/pkg/cwversion"
    14  	leaky "bitbucket.org/Aishee/synsec/pkg/leakybucket"
    15  	"bitbucket.org/Aishee/synsec/pkg/models"
    16  	"bitbucket.org/Aishee/synsec/pkg/parser"
    17  	"bitbucket.org/Aishee/synsec/pkg/types"
    18  	"github.com/go-openapi/strfmt"
    19  	"github.com/pkg/errors"
    20  	log "github.com/sirupsen/logrus"
    21  )
    22  
    23  func dedupAlerts(alerts []types.RuntimeAlert) ([]*models.Alert, error) {
    24  
    25  	var dedupCache []*models.Alert
    26  
    27  	for idx, alert := range alerts {
    28  		log.Tracef("alert %d/%d", idx, len(alerts))
    29  		/*if we have more than one source, we need to dedup */
    30  		if len(alert.Sources) == 0 || len(alert.Sources) == 1 {
    31  			dedupCache = append(dedupCache, alert.Alert)
    32  			continue
    33  		}
    34  		for k, src := range alert.Sources {
    35  			refsrc := *alert.Alert //copy
    36  			log.Tracef("source[%s]", k)
    37  			refsrc.Source = &src
    38  			dedupCache = append(dedupCache, &refsrc)
    39  		}
    40  	}
    41  	if len(dedupCache) != len(alerts) {
    42  		log.Tracef("went from %d to %d alerts", len(alerts), len(dedupCache))
    43  	}
    44  	return dedupCache, nil
    45  }
    46  
    47  func PushAlerts(alerts []types.RuntimeAlert, client *apiclient.ApiClient) error {
    48  	ctx := context.Background()
    49  	alertsToPush, err := dedupAlerts(alerts)
    50  
    51  	if err != nil {
    52  		return errors.Wrap(err, "failed to transform alerts for api")
    53  	}
    54  	_, _, err = client.Alerts.Add(ctx, alertsToPush)
    55  	if err != nil {
    56  		return errors.Wrap(err, "failed sending alert to LAPI")
    57  	}
    58  	return nil
    59  }
    60  
    61  func runOutput(input chan types.Event, overflow chan types.Event, buckets *leaky.Buckets,
    62  	postOverflowCTX parser.UnixParserCtx, postOverflowNodes []parser.Node, apiConfig csconfig.ApiCredentialsCfg) error {
    63  
    64  	var err error
    65  	ticker := time.NewTicker(1 * time.Second)
    66  
    67  	var cache []types.RuntimeAlert
    68  	var cacheMutex sync.Mutex
    69  
    70  	scenarios, err := cwhub.GetUpstreamInstalledScenariosAsString()
    71  	if err != nil {
    72  		return errors.Wrapf(err, "loading list of installed hub scenarios: %s", err)
    73  	}
    74  
    75  	apiURL, err := url.Parse(apiConfig.URL)
    76  	if err != nil {
    77  		return errors.Wrapf(err, "parsing api url ('%s'): %s", apiConfig.URL, err)
    78  	}
    79  
    80  	password := strfmt.Password(apiConfig.Password)
    81  
    82  	Client, err := apiclient.NewClient(&apiclient.Config{
    83  		MachineID:      apiConfig.Login,
    84  		Password:       password,
    85  		Scenarios:      scenarios,
    86  		UserAgent:      fmt.Sprintf("synsec/%s", cwversion.VersionStr()),
    87  		URL:            apiURL,
    88  		VersionPrefix:  "v1",
    89  		UpdateScenario: cwhub.GetUpstreamInstalledScenariosAsString,
    90  	})
    91  	if err != nil {
    92  		return errors.Wrapf(err, "new client api: %s", err)
    93  	}
    94  	if _, err = Client.Auth.AuthenticateWatcher(context.Background(), models.WatcherAuthRequest{
    95  		MachineID: &apiConfig.Login,
    96  		Password:  &password,
    97  		Scenarios: scenarios,
    98  	}); err != nil {
    99  		return errors.Wrapf(err, "authenticate watcher (%s)", apiConfig.Login)
   100  	}
   101  LOOP:
   102  	for {
   103  		select {
   104  		case <-ticker.C:
   105  			if len(cache) > 0 {
   106  				cacheMutex.Lock()
   107  				cachecopy := cache
   108  				newcache := make([]types.RuntimeAlert, 0)
   109  				cache = newcache
   110  				cacheMutex.Unlock()
   111  				if err := PushAlerts(cachecopy, Client); err != nil {
   112  					log.Errorf("while pushing to api : %s", err)
   113  					//just push back the events to the queue
   114  					cacheMutex.Lock()
   115  					cache = append(cache, cachecopy...)
   116  					cacheMutex.Unlock()
   117  				}
   118  			}
   119  		case <-outputsTomb.Dying():
   120  			if len(cache) > 0 {
   121  				cacheMutex.Lock()
   122  				cachecopy := cache
   123  				newcache := make([]types.RuntimeAlert, 0)
   124  				cache = newcache
   125  				cacheMutex.Unlock()
   126  				if err := PushAlerts(cachecopy, Client); err != nil {
   127  					log.Errorf("while pushing leftovers to api : %s", err)
   128  				}
   129  			}
   130  			break LOOP
   131  		case event := <-overflow:
   132  
   133  			/*if alert is empty and mapKey is present, the overflow is just to cleanup bucket*/
   134  			if event.Overflow.Alert == nil && event.Overflow.Mapkey != "" {
   135  				buckets.Bucket_map.Delete(event.Overflow.Mapkey)
   136  				break
   137  			}
   138  			if event.Overflow.Reprocess {
   139  				log.Debugf("Overflow being reprocessed.")
   140  				input <- event
   141  			}
   142  			/* process post overflow parser nodes */
   143  			event, err := parser.Parse(postOverflowCTX, event, postOverflowNodes)
   144  			if err != nil {
   145  				return fmt.Errorf("postoverflow failed : %s", err)
   146  			}
   147  			log.Printf("%s", *event.Overflow.Alert.Message)
   148  			if event.Overflow.Whitelisted {
   149  				log.Printf("[%s] is whitelisted, skip.", *event.Overflow.Alert.Message)
   150  				continue
   151  			}
   152  			cacheMutex.Lock()
   153  			cache = append(cache, event.Overflow)
   154  			cacheMutex.Unlock()
   155  
   156  		}
   157  	}
   158  
   159  	ticker.Stop()
   160  	return nil
   161  
   162  }