papi_cmd.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package apiserver
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "time"
  6. log "github.com/sirupsen/logrus"
  7. "github.com/crowdsecurity/go-cs-lib/ptr"
  8. "github.com/crowdsecurity/crowdsec/pkg/apiclient"
  9. "github.com/crowdsecurity/crowdsec/pkg/models"
  10. "github.com/crowdsecurity/crowdsec/pkg/modelscapi"
  11. "github.com/crowdsecurity/crowdsec/pkg/types"
  12. )
  13. type deleteDecisions struct {
  14. UUID string `json:"uuid"`
  15. Decisions []string `json:"decisions"`
  16. }
  17. type blocklistLink struct {
  18. // blocklist name
  19. Name string `json:"name"`
  20. // blocklist url
  21. Url string `json:"url"`
  22. // blocklist remediation
  23. Remediation string `json:"remediation"`
  24. // blocklist scope
  25. Scope string `json:"scope,omitempty"`
  26. // blocklist duration
  27. Duration string `json:"duration,omitempty"`
  28. }
  29. type forcePull struct {
  30. Blocklist *blocklistLink `json:"blocklist,omitempty"`
  31. }
  32. func DecisionCmd(message *Message, p *Papi, sync bool) error {
  33. switch message.Header.OperationCmd {
  34. case "delete":
  35. data, err := json.Marshal(message.Data)
  36. if err != nil {
  37. return err
  38. }
  39. UUIDs := make([]string, 0)
  40. deleteDecisionMsg := deleteDecisions{
  41. Decisions: make([]string, 0),
  42. }
  43. if err := json.Unmarshal(data, &deleteDecisionMsg); err != nil {
  44. return fmt.Errorf("message for '%s' contains bad data format: %s", message.Header.OperationType, err)
  45. }
  46. UUIDs = append(UUIDs, deleteDecisionMsg.Decisions...)
  47. log.Infof("Decisions UUIDs to remove: %+v", UUIDs)
  48. filter := make(map[string][]string)
  49. filter["uuid"] = UUIDs
  50. _, deletedDecisions, err := p.DBClient.SoftDeleteDecisionsWithFilter(filter)
  51. if err != nil {
  52. return fmt.Errorf("unable to delete decisions %+v : %s", UUIDs, err)
  53. }
  54. decisions := make([]*models.Decision, 0)
  55. for _, deletedDecision := range deletedDecisions {
  56. log.Infof("Decision from '%s' for '%s' (%s) has been deleted", deletedDecision.Origin, deletedDecision.Value, deletedDecision.Type)
  57. dec := &models.Decision{
  58. UUID: deletedDecision.UUID,
  59. Origin: &deletedDecision.Origin,
  60. Scenario: &deletedDecision.Scenario,
  61. Scope: &deletedDecision.Scope,
  62. Value: &deletedDecision.Value,
  63. ID: int64(deletedDecision.ID),
  64. Until: deletedDecision.Until.String(),
  65. Type: &deletedDecision.Type,
  66. }
  67. decisions = append(decisions, dec)
  68. }
  69. p.Channels.DeleteDecisionChannel <- decisions
  70. default:
  71. return fmt.Errorf("unknown command '%s' for operation type '%s'", message.Header.OperationCmd, message.Header.OperationType)
  72. }
  73. return nil
  74. }
  75. func AlertCmd(message *Message, p *Papi, sync bool) error {
  76. switch message.Header.OperationCmd {
  77. case "add":
  78. data, err := json.Marshal(message.Data)
  79. if err != nil {
  80. return err
  81. }
  82. alert := &models.Alert{}
  83. if err := json.Unmarshal(data, alert); err != nil {
  84. return fmt.Errorf("message for '%s' contains bad alert format: %w", message.Header.OperationType, err)
  85. }
  86. log.Infof("Received order %s from PAPI (%d decisions)", alert.UUID, len(alert.Decisions))
  87. /*Fix the alert with missing mandatory items*/
  88. if alert.StartAt == nil || *alert.StartAt == "" {
  89. log.Warnf("Alert %d has no StartAt, setting it to now", alert.ID)
  90. alert.StartAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
  91. }
  92. if alert.StopAt == nil || *alert.StopAt == "" {
  93. log.Warnf("Alert %d has no StopAt, setting it to now", alert.ID)
  94. alert.StopAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
  95. }
  96. alert.EventsCount = ptr.Of(int32(0))
  97. alert.Capacity = ptr.Of(int32(0))
  98. alert.Leakspeed = ptr.Of("")
  99. alert.Simulated = ptr.Of(false)
  100. alert.ScenarioHash = ptr.Of("")
  101. alert.ScenarioVersion = ptr.Of("")
  102. alert.Message = ptr.Of("")
  103. alert.Scenario = ptr.Of("")
  104. alert.Source = &models.Source{}
  105. //if we're setting Source.Scope to types.ConsoleOrigin, it messes up the alert's value
  106. if len(alert.Decisions) >= 1 {
  107. alert.Source.Scope = alert.Decisions[0].Scope
  108. alert.Source.Value = alert.Decisions[0].Value
  109. } else {
  110. log.Warningf("No decision found in alert for Polling API (%s : %s)", message.Header.Source.User, message.Header.Message)
  111. alert.Source.Scope = ptr.Of(types.ConsoleOrigin)
  112. alert.Source.Value = &message.Header.Source.User
  113. }
  114. alert.Scenario = &message.Header.Message
  115. for _, decision := range alert.Decisions {
  116. if *decision.Scenario == "" {
  117. decision.Scenario = &message.Header.Message
  118. }
  119. log.Infof("Adding decision for '%s' with UUID: %s", *decision.Value, decision.UUID)
  120. }
  121. //use a different method : alert and/or decision might already be partially present in the database
  122. _, err = p.DBClient.CreateOrUpdateAlert("", alert)
  123. if err != nil {
  124. log.Errorf("Failed to create alerts in DB: %s", err)
  125. } else {
  126. p.Channels.AddAlertChannel <- []*models.Alert{alert}
  127. }
  128. default:
  129. return fmt.Errorf("unknown command '%s' for operation type '%s'", message.Header.OperationCmd, message.Header.OperationType)
  130. }
  131. return nil
  132. }
  133. func ManagementCmd(message *Message, p *Papi, sync bool) error {
  134. if sync {
  135. log.Infof("Ignoring management command from PAPI in sync mode")
  136. return nil
  137. }
  138. switch message.Header.OperationCmd {
  139. case "reauth":
  140. log.Infof("Received reauth command from PAPI, resetting token")
  141. p.apiClient.GetClient().Transport.(*apiclient.JWTTransport).ResetToken()
  142. case "force_pull":
  143. data, err := json.Marshal(message.Data)
  144. if err != nil {
  145. return err
  146. }
  147. forcePullMsg := forcePull{}
  148. if err := json.Unmarshal(data, &forcePullMsg); err != nil {
  149. return fmt.Errorf("message for '%s' contains bad data format: %s", message.Header.OperationType, err)
  150. }
  151. if forcePullMsg.Blocklist == nil {
  152. log.Infof("Received force_pull command from PAPI, pulling community and 3rd-party blocklists")
  153. err = p.apic.PullTop(true)
  154. if err != nil {
  155. return fmt.Errorf("failed to force pull operation: %s", err)
  156. }
  157. } else {
  158. log.Infof("Received force_pull command from PAPI, pulling blocklist %s", forcePullMsg.Blocklist.Name)
  159. err = p.apic.PullBlocklist(&modelscapi.BlocklistLink{
  160. Name: &forcePullMsg.Blocklist.Name,
  161. URL: &forcePullMsg.Blocklist.Url,
  162. Remediation: &forcePullMsg.Blocklist.Remediation,
  163. Scope: &forcePullMsg.Blocklist.Scope,
  164. Duration: &forcePullMsg.Blocklist.Duration,
  165. }, true)
  166. if err != nil {
  167. return fmt.Errorf("failed to force pull operation: %s", err)
  168. }
  169. }
  170. default:
  171. return fmt.Errorf("unknown command '%s' for operation type '%s'", message.Header.OperationCmd, message.Header.OperationType)
  172. }
  173. return nil
  174. }