utils.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package wafacquisition
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "time"
  6. "github.com/crowdsecurity/coraza/v3/collection"
  7. "github.com/crowdsecurity/coraza/v3/types/variables"
  8. "github.com/crowdsecurity/crowdsec/pkg/models"
  9. "github.com/crowdsecurity/crowdsec/pkg/types"
  10. "github.com/crowdsecurity/crowdsec/pkg/waf"
  11. "github.com/crowdsecurity/go-cs-lib/ptr"
  12. "github.com/prometheus/client_golang/prometheus"
  13. log "github.com/sirupsen/logrus"
  14. )
  15. func WaapEventGeneration(inEvt types.Event) (types.Event, error) {
  16. evt := types.Event{}
  17. evt.Type = types.WAAP
  18. evt.Process = true
  19. source := models.Source{
  20. Value: ptr.Of(inEvt.Parsed["source_ip"]),
  21. IP: inEvt.Parsed["source_ip"],
  22. Scope: ptr.Of(types.Ip),
  23. }
  24. evt.Overflow.Sources = make(map[string]models.Source)
  25. evt.Overflow.Sources["ip"] = source
  26. alert := models.Alert{}
  27. alert.Capacity = ptr.Of(int32(1))
  28. alert.Events = make([]*models.Event, 0) //@tko -> URI, method, UA, param name
  29. alert.Meta = make(models.Meta, 0) //@tko -> URI, method, UA, param name
  30. for _, key := range []string{"target_uri", "method"} {
  31. valueByte, err := json.Marshal([]string{inEvt.Parsed[key]})
  32. if err != nil {
  33. log.Debugf("unable to serialize key %s", key)
  34. continue
  35. }
  36. meta := models.MetaItems0{
  37. Key: key,
  38. Value: string(valueByte),
  39. }
  40. alert.Meta = append(alert.Meta, &meta)
  41. log.Infof("adding Meta - %s = %s", key, inEvt.Parsed[key])
  42. }
  43. alert.EventsCount = ptr.Of(int32(1))
  44. alert.Labels = []string{"waf"} //don't know what to do about this
  45. alert.Leakspeed = ptr.Of("")
  46. msg := fmt.Sprintf("WAF alert: %s", inEvt.Waap.MatchedRules.GetName())
  47. alert.Message = &msg
  48. alert.Scenario = ptr.Of(inEvt.Waap.MatchedRules.GetName()) // @sbl : should we be able to do inEvt.Waap.MatchedRules.GetHash()
  49. alert.ScenarioHash = ptr.Of(inEvt.Waap.MatchedRules.GetHash()) // @sbl : should we be able to do inEvt.Waap.MatchedRules.GetHash()
  50. alert.ScenarioVersion = ptr.Of(inEvt.Waap.MatchedRules.GetVersion()) // @sbl : should we be able to do inEvt.Waap.MatchedRules.GetVersion()
  51. alert.Simulated = ptr.Of(false)
  52. alert.Source = &source
  53. alert.StartAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
  54. alert.StopAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
  55. evt.Overflow.APIAlerts = []models.Alert{alert}
  56. evt.Overflow.Alert = &alert
  57. return evt, nil
  58. }
  59. func EventFromRequest(r waf.ParsedRequest) (types.Event, error) {
  60. evt := types.Event{}
  61. //we might want to change this based on in-band vs out-of-band ?
  62. evt.Type = types.LOG
  63. evt.ExpectMode = types.LIVE
  64. //def needs fixing
  65. evt.Stage = "s00-raw"
  66. evt.Parsed = map[string]string{
  67. "source_ip": r.ClientIP,
  68. "target_host": r.Host,
  69. "target_uri": r.URI,
  70. "method": r.Method,
  71. "req_uuid": r.Tx.ID(),
  72. "source": "coraza",
  73. //TBD:
  74. //http_status
  75. //user_agent
  76. }
  77. evt.Line = types.Line{
  78. Time: time.Now(),
  79. //should we add some info like listen addr/port/path ?
  80. Labels: map[string]string{"type": "coraza-waf"},
  81. Process: true,
  82. Module: "waf",
  83. Src: "waf",
  84. Raw: "dummy-waf-data", //we discard empty Line.Raw items :)
  85. }
  86. evt.Waap = types.WaapEvent{}
  87. return evt, nil
  88. }
  89. func LogWaapEvent(evt *types.Event, logger *log.Entry) {
  90. req := evt.Parsed["target_uri"]
  91. if len(req) > 12 {
  92. req = req[:10] + ".."
  93. }
  94. if evt.Meta["waap_interrupted"] == "true" {
  95. logger.WithFields(log.Fields{
  96. "module": "waf",
  97. "source": evt.Parsed["source_ip"],
  98. "target_uri": req,
  99. }).Infof("%s blocked on %s (%d rules) [%v]", evt.Parsed["source_ip"], req, len(evt.Waap.MatchedRules), evt.Waap.GetRuleIDs())
  100. } else if evt.Parsed["outofband_interrupted"] == "true" {
  101. logger.WithFields(log.Fields{
  102. "module": "waf",
  103. "source": evt.Parsed["source_ip"],
  104. "target_uri": req,
  105. }).Infof("%s out-of-band blocking rules on %s (%d rules) [%v]", evt.Parsed["source_ip"], req, len(evt.Waap.MatchedRules), evt.Waap.GetRuleIDs())
  106. } else {
  107. logger.WithFields(log.Fields{
  108. "module": "waf",
  109. "source": evt.Parsed["source_ip"],
  110. "target_uri": req,
  111. }).Debugf("%s triggerd non-blocking rules on %s (%d rules) [%v]", evt.Parsed["source_ip"], req, len(evt.Waap.MatchedRules), evt.Waap.GetRuleIDs())
  112. }
  113. }
  114. func (r *WaapRunner) AccumulateTxToEvent(evt *types.Event, req waf.ParsedRequest) error {
  115. if evt == nil {
  116. //an error was already emitted, let's not spam the logs
  117. return nil
  118. }
  119. if !req.Tx.IsInterrupted() {
  120. //if the phase didn't generate an interruption, we don't have anything to add to the event
  121. return nil
  122. }
  123. //if one interruption was generated, event is good for processing :)
  124. evt.Process = true
  125. if evt.Meta == nil {
  126. evt.Meta = map[string]string{}
  127. }
  128. if req.IsInBand {
  129. evt.Meta["waap_interrupted"] = "true"
  130. evt.Meta["waap_action"] = req.Tx.Interruption().Action
  131. evt.Parsed["inband_interrupted"] = "true"
  132. evt.Parsed["inband_action"] = req.Tx.Interruption().Action
  133. } else {
  134. evt.Parsed["outofband_interrupted"] = "true"
  135. evt.Parsed["outofband_action"] = req.Tx.Interruption().Action
  136. }
  137. if evt.Waap.Vars == nil {
  138. evt.Waap.Vars = map[string]string{}
  139. }
  140. req.Tx.Variables().All(func(v variables.RuleVariable, col collection.Collection) bool {
  141. for _, variable := range col.FindAll() {
  142. key := ""
  143. if variable.Key() == "" {
  144. key = variable.Variable().Name()
  145. } else {
  146. key = variable.Variable().Name() + "." + variable.Key()
  147. }
  148. if variable.Value() == "" {
  149. continue
  150. }
  151. for _, collectionToKeep := range r.WaapRuntime.CompiledVariablesTracking {
  152. match := collectionToKeep.MatchString(key)
  153. if match {
  154. evt.Waap.Vars[key] = variable.Value()
  155. r.logger.Debugf("%s.%s = %s", variable.Variable().Name(), variable.Key(), variable.Value())
  156. } else {
  157. r.logger.Debugf("%s.%s != %s (%s) (not kept)", variable.Variable().Name(), variable.Key(), collectionToKeep, variable.Value())
  158. }
  159. }
  160. }
  161. return true
  162. })
  163. for _, rule := range req.Tx.MatchedRules() {
  164. if rule.Message() == "" {
  165. r.logger.Tracef("discarding rule %d", rule.Rule().ID())
  166. continue
  167. }
  168. kind := "outofband"
  169. if req.IsInBand {
  170. kind = "inband"
  171. }
  172. WafRuleHits.With(prometheus.Labels{"rule_id": fmt.Sprintf("%d", rule.Rule().ID()), "type": kind}).Inc()
  173. corazaRule := map[string]interface{}{
  174. "id": rule.Rule().ID(),
  175. "uri": evt.Parsed["uri"],
  176. "rule_type": kind,
  177. "method": evt.Parsed["method"],
  178. "disruptive": rule.Disruptive(),
  179. "tags": rule.Rule().Tags(),
  180. "file": rule.Rule().File(),
  181. "file_line": rule.Rule().Line(),
  182. "revision": rule.Rule().Revision(),
  183. "secmark": rule.Rule().SecMark(),
  184. "accuracy": rule.Rule().Accuracy(),
  185. "msg": rule.Message(),
  186. "severity": rule.Rule().Severity().String(),
  187. "name": "FIXFIXFIXFIXFIX",
  188. "hash": "FIXIFIX",
  189. "version": "FIXFIXFIX",
  190. }
  191. evt.Waap.MatchedRules = append(evt.Waap.MatchedRules, corazaRule)
  192. }
  193. return nil
  194. }