crowdsec_cti.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package exprhelpers
  2. import (
  3. "fmt"
  4. "time"
  5. "github.com/bluele/gcache"
  6. "github.com/crowdsecurity/crowdsec/pkg/cticlient"
  7. "github.com/crowdsecurity/crowdsec/pkg/types"
  8. "github.com/pkg/errors"
  9. log "github.com/sirupsen/logrus"
  10. )
  11. var CTIUrl = "https://cti.api.crowdsec.net"
  12. var CTIUrlSuffix = "/v2/smoke/"
  13. var CTIApiKey = ""
  14. // this is set for non-recoverable errors, such as 403 when querying API or empty API key
  15. var CTIApiEnabled = true
  16. // when hitting quotas or auth errors, we temporarily disable the API
  17. var CTIBackOffUntil time.Time
  18. var CTIBackOffDuration time.Duration = 5 * time.Minute
  19. var ctiClient *cticlient.CrowdsecCTIClient
  20. func InitCrowdsecCTI(Key *string, TTL *time.Duration, Size *int, LogLevel *log.Level) error {
  21. if Key != nil {
  22. CTIApiKey = *Key
  23. } else {
  24. CTIApiEnabled = false
  25. return fmt.Errorf("CTI API key not set, CTI will not be available")
  26. }
  27. if Size == nil {
  28. Size = new(int)
  29. *Size = 1000
  30. }
  31. if TTL == nil {
  32. TTL = new(time.Duration)
  33. *TTL = 5 * time.Minute
  34. }
  35. //dedicated logger
  36. clog := log.New()
  37. if err := types.ConfigureLogger(clog); err != nil {
  38. return errors.Wrap(err, "while configuring datasource logger")
  39. }
  40. if LogLevel != nil {
  41. clog.SetLevel(*LogLevel)
  42. }
  43. customLog := log.Fields{
  44. "type": "crowdsec-cti",
  45. }
  46. subLogger := clog.WithFields(customLog)
  47. CrowdsecCTIInitCache(*Size, *TTL)
  48. ctiClient = cticlient.NewCrowdsecCTIClient(cticlient.WithAPIKey(CTIApiKey), cticlient.WithLogger(subLogger))
  49. return nil
  50. }
  51. func ShutdownCrowdsecCTI() {
  52. if CTICache != nil {
  53. CTICache.Purge()
  54. }
  55. CTIApiKey = ""
  56. CTIApiEnabled = true
  57. }
  58. // Cache for responses
  59. var CTICache gcache.Cache
  60. var CacheExpiration time.Duration
  61. func CrowdsecCTIInitCache(size int, ttl time.Duration) {
  62. CTICache = gcache.New(size).LRU().Build()
  63. CacheExpiration = ttl
  64. }
  65. func CrowdsecCTI(ip string) (*cticlient.SmokeItem, error) {
  66. if !CTIApiEnabled {
  67. ctiClient.Logger.Warningf("Crowdsec CTI API is disabled, please check your configuration")
  68. return &cticlient.SmokeItem{}, cticlient.ErrDisabled
  69. }
  70. if CTIApiKey == "" {
  71. ctiClient.Logger.Warningf("CrowdsecCTI : no key provided, skipping")
  72. return &cticlient.SmokeItem{}, cticlient.ErrDisabled
  73. }
  74. if ctiClient == nil {
  75. ctiClient.Logger.Warningf("CrowdsecCTI: no client, skipping")
  76. return &cticlient.SmokeItem{}, cticlient.ErrDisabled
  77. }
  78. if val, err := CTICache.Get(ip); err == nil && val != nil {
  79. ctiClient.Logger.Debugf("cti cache fetch for %s", ip)
  80. ret, ok := val.(*cticlient.SmokeItem)
  81. if !ok {
  82. ctiClient.Logger.Warningf("CrowdsecCTI: invalid type in cache, removing")
  83. CTICache.Remove(ip)
  84. } else {
  85. return ret, nil
  86. }
  87. }
  88. if !CTIBackOffUntil.IsZero() && time.Now().Before(CTIBackOffUntil) {
  89. //ctiClient.Logger.Warningf("Crowdsec CTI client is in backoff mode, ending in %s", time.Until(CTIBackOffUntil))
  90. return &cticlient.SmokeItem{}, cticlient.ErrLimit
  91. }
  92. ctiClient.Logger.Infof("cti call for %s", ip)
  93. before := time.Now()
  94. ctiResp, err := ctiClient.GetIPInfo(ip)
  95. ctiClient.Logger.Debugf("request for %s took %v", ip, time.Since(before))
  96. if err != nil {
  97. if err == cticlient.ErrUnauthorized {
  98. CTIApiEnabled = false
  99. ctiClient.Logger.Errorf("Invalid API key provided, disabling CTI API")
  100. return &cticlient.SmokeItem{}, cticlient.ErrUnauthorized
  101. } else if err == cticlient.ErrLimit {
  102. CTIBackOffUntil = time.Now().Add(CTIBackOffDuration)
  103. ctiClient.Logger.Errorf("CTI API is throttled, will try again in %s", CTIBackOffDuration)
  104. return &cticlient.SmokeItem{}, cticlient.ErrLimit
  105. } else {
  106. ctiClient.Logger.Warnf("CTI API error : %s", err)
  107. return &cticlient.SmokeItem{}, fmt.Errorf("unexpected error : %v", err)
  108. }
  109. }
  110. if err := CTICache.SetWithExpire(ip, ctiResp, CacheExpiration); err != nil {
  111. ctiClient.Logger.Warningf("IpCTI : error while caching CTI : %s", err)
  112. return &cticlient.SmokeItem{}, cticlient.ErrUnknown
  113. }
  114. ctiClient.Logger.Tracef("CTI response : %v", *ctiResp)
  115. return ctiResp, nil
  116. }