overflows.go 9.7 KB

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