overflows.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package leakybucket
  2. import (
  3. "fmt"
  4. "net"
  5. "sort"
  6. "strconv"
  7. "github.com/crowdsecurity/crowdsec/pkg/models"
  8. "github.com/crowdsecurity/crowdsec/pkg/types"
  9. "github.com/davecgh/go-spew/spew"
  10. "github.com/go-openapi/strfmt"
  11. "github.com/pkg/errors"
  12. log "github.com/sirupsen/logrus"
  13. "github.com/antonmedv/expr"
  14. "github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
  15. )
  16. //SourceFromEvent extracts and formats a valid models.Source object from an Event
  17. func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, error) {
  18. srcs := make(map[string]models.Source)
  19. /*if it's already an overflow, we have properly formatted sources.
  20. we can just twitch them to reflect the requested scope*/
  21. if evt.Type == types.OVFLW {
  22. for k, v := range evt.Overflow.Sources {
  23. /*the scopes are already similar, nothing to do*/
  24. if leaky.scopeType.Scope == *v.Scope {
  25. srcs[k] = v
  26. continue
  27. }
  28. /*The bucket requires a decision on scope Range */
  29. if leaky.scopeType.Scope == types.Range {
  30. /*the original bucket was target IPs, check that we do have range*/
  31. if *v.Scope == types.Ip {
  32. if v.Range != "" {
  33. src := models.Source{}
  34. src.AsName = v.AsName
  35. src.AsNumber = v.AsNumber
  36. src.Cn = v.Cn
  37. src.Latitude = v.Latitude
  38. src.Longitude = v.Longitude
  39. src.Range = v.Range
  40. src.Value = new(string)
  41. src.Scope = new(string)
  42. *src.Value = v.Range
  43. *src.Scope = leaky.scopeType.Scope
  44. srcs[*src.Value] = src
  45. } else {
  46. log.Warningf("bucket %s requires scope Range, but none was provided. It seems that the %s wasn't enriched to include its range.", leaky.Name, *v.Value)
  47. }
  48. } else {
  49. log.Warningf("bucket %s requires scope Range, but can't extrapolate from %s (%s)",
  50. leaky.Name, *v.Scope, *v.Value)
  51. }
  52. }
  53. }
  54. return srcs, nil
  55. }
  56. src := models.Source{}
  57. switch leaky.scopeType.Scope {
  58. case types.Range, types.Ip:
  59. if v, ok := evt.Meta["source_ip"]; ok {
  60. if net.ParseIP(v) == nil {
  61. return srcs, fmt.Errorf("scope is %s but '%s' isn't a valid ip", leaky.scopeType.Scope, v)
  62. } else {
  63. src.IP = v
  64. }
  65. } else {
  66. return srcs, fmt.Errorf("scope is %s but Meta[source_ip] doesn't exist", leaky.scopeType.Scope)
  67. }
  68. src.Scope = &leaky.scopeType.Scope
  69. if v, ok := evt.Enriched["ASNumber"]; ok {
  70. src.AsNumber = v
  71. } else if v, ok := evt.Enriched["ASNNumber"]; ok {
  72. src.AsNumber = v
  73. }
  74. if v, ok := evt.Enriched["IsoCode"]; ok {
  75. src.Cn = v
  76. }
  77. if v, ok := evt.Enriched["ASNOrg"]; ok {
  78. src.AsName = v
  79. }
  80. if v, ok := evt.Enriched["Latitude"]; ok {
  81. l, err := strconv.ParseFloat(v, 32)
  82. if err != nil {
  83. log.Warningf("bad latitude %s : %s", v, err)
  84. }
  85. src.Latitude = float32(l)
  86. }
  87. if v, ok := evt.Enriched["Longitude"]; ok {
  88. l, err := strconv.ParseFloat(v, 32)
  89. if err != nil {
  90. log.Warningf("bad longitude %s : %s", v, err)
  91. }
  92. src.Longitude = float32(l)
  93. }
  94. if v, ok := evt.Meta["SourceRange"]; ok && v != "" {
  95. _, ipNet, err := net.ParseCIDR(v)
  96. if err != nil {
  97. return srcs, fmt.Errorf("Declared range %s of %s can't be parsed", v, src.IP)
  98. } else if ipNet != nil {
  99. src.Range = ipNet.String()
  100. leaky.logger.Tracef("Valid range from %s : %s", src.IP, src.Range)
  101. }
  102. }
  103. if leaky.scopeType.Scope == types.Ip {
  104. src.Value = &src.IP
  105. } else if leaky.scopeType.Scope == types.Range {
  106. src.Value = &src.Range
  107. }
  108. srcs[*src.Value] = src
  109. default:
  110. if leaky.scopeType.RunTimeFilter != nil {
  111. retValue, err := expr.Run(leaky.scopeType.RunTimeFilter, exprhelpers.GetExprEnv(map[string]interface{}{"evt": &evt}))
  112. if err != nil {
  113. return srcs, errors.Wrapf(err, "while running scope filter")
  114. }
  115. value, ok := retValue.(string)
  116. if !ok {
  117. value = ""
  118. }
  119. src.Value = &value
  120. src.Scope = new(string)
  121. *src.Scope = leaky.scopeType.Scope
  122. srcs[*src.Value] = src
  123. } else {
  124. return srcs, fmt.Errorf("empty scope information")
  125. }
  126. }
  127. return srcs, nil
  128. }
  129. //EventsFromQueue iterates the queue to collect & prepare meta-datas from alert
  130. func EventsFromQueue(queue *Queue) []*models.Event {
  131. events := []*models.Event{}
  132. for _, evt := range queue.Queue {
  133. if evt.Meta == nil {
  134. continue
  135. }
  136. meta := models.Meta{}
  137. //we want consistence
  138. skeys := make([]string, 0, len(evt.Meta))
  139. for k := range evt.Meta {
  140. skeys = append(skeys, k)
  141. }
  142. sort.Strings(skeys)
  143. for _, k := range skeys {
  144. v := evt.Meta[k]
  145. subMeta := models.MetaItems0{Key: k, Value: v}
  146. meta = append(meta, &subMeta)
  147. }
  148. /*check which date to use*/
  149. ovflwEvent := models.Event{
  150. Meta: meta,
  151. }
  152. //either MarshaledTime is present and is extracted from log
  153. if evt.MarshaledTime != "" {
  154. ovflwEvent.Timestamp = &evt.MarshaledTime
  155. } else if !evt.Time.IsZero() { //or .Time has been set during parse as time.Now().UTC()
  156. ovflwEvent.Timestamp = new(string)
  157. raw, err := evt.Time.MarshalText()
  158. if err != nil {
  159. log.Warningf("while marshaling time '%s' : %s", evt.Time.String(), err)
  160. } else {
  161. *ovflwEvent.Timestamp = string(raw)
  162. }
  163. } else {
  164. log.Warningf("Event has no parsed time, no runtime timestamp")
  165. }
  166. events = append(events, &ovflwEvent)
  167. }
  168. return events
  169. }
  170. //alertFormatSource iterates over the queue to collect sources
  171. func alertFormatSource(leaky *Leaky, queue *Queue) (map[string]models.Source, string, error) {
  172. var sources map[string]models.Source = make(map[string]models.Source)
  173. var source_type string
  174. log.Debugf("Formatting (%s) - scope Info : scope_type:%s / scope_filter:%s", leaky.Name, leaky.scopeType.Scope, leaky.scopeType.Filter)
  175. for _, evt := range queue.Queue {
  176. srcs, err := SourceFromEvent(evt, leaky)
  177. if err != nil {
  178. return nil, "", errors.Wrapf(err, "while extracting scope from bucket %s", leaky.Name)
  179. }
  180. for key, src := range srcs {
  181. if source_type == types.Undefined {
  182. source_type = *src.Scope
  183. }
  184. if *src.Scope != source_type {
  185. return nil, "",
  186. fmt.Errorf("event has multiple source types : %s != %s", *src.Scope, source_type)
  187. }
  188. sources[key] = src
  189. }
  190. }
  191. return sources, source_type, nil
  192. }
  193. //NewAlert will generate a RuntimeAlert and its APIAlert(s) from a bucket that overflowed
  194. func NewAlert(leaky *Leaky, queue *Queue) (types.RuntimeAlert, error) {
  195. var runtimeAlert types.RuntimeAlert
  196. leaky.logger.Tracef("Overflow (start: %s, end: %s)", leaky.First_ts, leaky.Ovflw_ts)
  197. /*
  198. Craft the models.Alert that is going to be duplicated for each source
  199. */
  200. start_at, err := leaky.First_ts.MarshalText()
  201. if err != nil {
  202. log.Warningf("failed to marshal start ts %s : %s", leaky.First_ts.String(), err)
  203. }
  204. stop_at, err := leaky.Ovflw_ts.MarshalText()
  205. if err != nil {
  206. log.Warningf("failed to marshal ovflw ts %s : %s", leaky.First_ts.String(), err)
  207. }
  208. capacity := int32(leaky.Capacity)
  209. EventsCount := int32(leaky.Total_count)
  210. leakSpeed := leaky.Leakspeed.String()
  211. startAt := string(start_at)
  212. stopAt := string(stop_at)
  213. apiAlert := models.Alert{
  214. Scenario: &leaky.Name,
  215. ScenarioHash: &leaky.hash,
  216. ScenarioVersion: &leaky.scenarioVersion,
  217. Capacity: &capacity,
  218. EventsCount: &EventsCount,
  219. Leakspeed: &leakSpeed,
  220. Message: new(string),
  221. StartAt: &startAt,
  222. StopAt: &stopAt,
  223. Simulated: &leaky.Simulated,
  224. }
  225. if leaky.BucketConfig == nil {
  226. return runtimeAlert, fmt.Errorf("leaky.BucketConfig is nil")
  227. }
  228. //give information about the bucket
  229. runtimeAlert.Mapkey = leaky.Mapkey
  230. //Get the sources from Leaky/Queue
  231. sources, source_scope, err := alertFormatSource(leaky, queue)
  232. if err != nil {
  233. return runtimeAlert, errors.Wrap(err, "unable to collect sources from bucket")
  234. }
  235. runtimeAlert.Sources = sources
  236. //Include source info in format string
  237. sourceStr := ""
  238. if len(sources) > 1 {
  239. sourceStr = fmt.Sprintf("%d sources", len(sources))
  240. } else if len(sources) == 1 {
  241. for k, _ := range sources {
  242. sourceStr = k
  243. break
  244. }
  245. } else {
  246. sourceStr = "UNKNOWN"
  247. }
  248. *apiAlert.Message = fmt.Sprintf("%s %s performed '%s' (%d events over %s) at %s", source_scope, sourceStr, leaky.Name, leaky.Total_count, leaky.Ovflw_ts.Sub(leaky.First_ts), leaky.Last_ts)
  249. //Get the events from Leaky/Queue
  250. apiAlert.Events = EventsFromQueue(queue)
  251. //Loop over the Sources and generate appropriate number of ApiAlerts
  252. for _, srcValue := range sources {
  253. newApiAlert := apiAlert
  254. srcCopy := srcValue
  255. newApiAlert.Source = &srcCopy
  256. if v, ok := leaky.BucketConfig.Labels["remediation"]; ok && v == "true" {
  257. newApiAlert.Remediation = true
  258. }
  259. if err := newApiAlert.Validate(strfmt.Default); err != nil {
  260. log.Errorf("Generated alerts isn't valid")
  261. log.Errorf("->%s", spew.Sdump(newApiAlert))
  262. log.Fatalf("error : %s", err)
  263. }
  264. runtimeAlert.APIAlerts = append(runtimeAlert.APIAlerts, newApiAlert)
  265. }
  266. if len(runtimeAlert.APIAlerts) > 0 {
  267. runtimeAlert.Alert = &runtimeAlert.APIAlerts[0]
  268. }
  269. if leaky.Reprocess {
  270. runtimeAlert.Reprocess = true
  271. }
  272. return runtimeAlert, nil
  273. }