api.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "math/rand"
  7. "path"
  8. "strings"
  9. "time"
  10. "github.com/crowdsecurity/crowdsec/pkg/cwhub"
  11. "github.com/crowdsecurity/crowdsec/pkg/outputs"
  12. "github.com/crowdsecurity/crowdsec/pkg/types"
  13. "github.com/denisbrodbeck/machineid"
  14. log "github.com/sirupsen/logrus"
  15. "github.com/spf13/cobra"
  16. "gopkg.in/yaml.v2"
  17. )
  18. var (
  19. passwordLength = 64
  20. upper = "ABCDEFGHIJKLMNOPQRSTUVWXY"
  21. lower = "abcdefghijklmnopqrstuvwxyz"
  22. digits = "0123456789"
  23. )
  24. var (
  25. userID string // for flag parsing
  26. outputCTX *outputs.Output
  27. )
  28. const (
  29. uuid = "/proc/sys/kernel/random/uuid"
  30. apiConfigFile = "api.yaml"
  31. )
  32. func dumpCredentials() error {
  33. if config.output == "json" {
  34. credsYaml, err := json.Marshal(&outputCTX.API.Creds)
  35. if err != nil {
  36. log.Fatalf("Can't marshal credentials : %v", err)
  37. }
  38. fmt.Printf("%s\n", string(credsYaml))
  39. } else {
  40. credsYaml, err := yaml.Marshal(&outputCTX.API.Creds)
  41. if err != nil {
  42. log.Fatalf("Can't marshal credentials : %v", err)
  43. }
  44. fmt.Printf("%s\n", string(credsYaml))
  45. }
  46. return nil
  47. }
  48. func generatePassword() string {
  49. rand.Seed(time.Now().UnixNano())
  50. charset := upper + lower + digits
  51. buf := make([]byte, passwordLength)
  52. buf[0] = digits[rand.Intn(len(digits))]
  53. buf[1] = upper[rand.Intn(len(upper))]
  54. buf[2] = lower[rand.Intn(len(lower))]
  55. for i := 3; i < passwordLength; i++ {
  56. buf[i] = charset[rand.Intn(len(charset))]
  57. }
  58. rand.Shuffle(len(buf), func(i, j int) {
  59. buf[i], buf[j] = buf[j], buf[i]
  60. })
  61. return string(buf)
  62. }
  63. func pullTOP() error {
  64. /*profile from cwhub*/
  65. var profiles []string
  66. if _, ok := cwhub.HubIdx[cwhub.SCENARIOS]; !ok || len(cwhub.HubIdx[cwhub.SCENARIOS]) == 0 {
  67. log.Errorf("no loaded scenarios, can't fill profiles")
  68. return fmt.Errorf("no profiles")
  69. }
  70. for _, item := range cwhub.HubIdx[cwhub.SCENARIOS] {
  71. if item.Tainted || !item.Installed {
  72. continue
  73. }
  74. profiles = append(profiles, item.Name)
  75. }
  76. outputCTX.API.Creds.Profile = strings.Join(profiles[:], ",")
  77. if err := outputCTX.API.Signin(); err != nil {
  78. log.Fatalf(err.Error())
  79. }
  80. ret, err := outputCTX.API.PullTop()
  81. if err != nil {
  82. log.Fatalf(err.Error())
  83. }
  84. log.Warningf("api pull returned %d entries", len(ret))
  85. for _, item := range ret {
  86. if _, ok := item["range_ip"]; !ok {
  87. continue
  88. }
  89. if _, ok := item["scenario"]; !ok {
  90. continue
  91. }
  92. item["scenario"] = fmt.Sprintf("api: %s", item["scenario"])
  93. if _, ok := item["action"]; !ok {
  94. continue
  95. }
  96. if _, ok := item["expiration"]; !ok {
  97. continue
  98. }
  99. if _, ok := item["country"]; !ok {
  100. item["country"] = ""
  101. }
  102. if _, ok := item["as_org"]; !ok {
  103. item["as_org"] = ""
  104. }
  105. if _, ok := item["as_num"]; !ok {
  106. item["as_num"] = ""
  107. }
  108. var signalOcc types.SignalOccurence
  109. signalOcc, err = simpleBanToSignal(item["range_ip"], item["scenario"], item["expiration"], item["action"], item["as_name"], item["as_num"], item["country"], "api")
  110. if err != nil {
  111. return fmt.Errorf("failed to convert ban to signal : %s", err)
  112. }
  113. if err := outputCTX.Insert(signalOcc); err != nil {
  114. log.Fatalf("Unable to write pull to Database : %+s", err.Error())
  115. }
  116. }
  117. outputCTX.Flush()
  118. log.Infof("Wrote %d bans from api to database.", len(ret))
  119. return nil
  120. }
  121. func NewAPICmd() *cobra.Command {
  122. var cmdAPI = &cobra.Command{
  123. Use: "api [action]",
  124. Short: "Crowdsec API interaction",
  125. Long: `
  126. Allow to register your machine into crowdsec API to send and receive signal.
  127. `,
  128. Example: `
  129. cscli api register # Register to Crowdsec API
  130. cscli api pull # Pull malevolant IPs from Crowdsec API
  131. cscli api reset # Reset your machines credentials
  132. cscli api enroll # Enroll your machine to the user account you created on Crowdsec backend
  133. cscli api credentials # Display your API credentials
  134. `,
  135. Args: cobra.MinimumNArgs(1),
  136. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  137. var err error
  138. if !config.configured {
  139. return fmt.Errorf("you must configure cli before interacting with hub")
  140. }
  141. outputConfig := outputs.OutputFactory{
  142. BackendFolder: config.BackendPluginFolder,
  143. Flush: false,
  144. }
  145. outputCTX, err = outputs.NewOutput(&outputConfig)
  146. if err != nil {
  147. return err
  148. }
  149. err = outputCTX.LoadAPIConfig(path.Join(config.InstallFolder, apiConfigFile))
  150. if err != nil {
  151. return err
  152. }
  153. return nil
  154. },
  155. }
  156. var cmdAPIRegister = &cobra.Command{
  157. Use: "register",
  158. Short: "Register on Crowdsec API",
  159. Long: `This command will register your machine to crowdsec API to allow you to receive list of malveolent IPs.
  160. The printed machine_id and password should be added to your api.yaml file.`,
  161. Example: `cscli api register`,
  162. Args: cobra.MinimumNArgs(0),
  163. Run: func(cmd *cobra.Command, args []string) {
  164. id, err := machineid.ID()
  165. if err != nil {
  166. log.Debugf("failed to get machine-id with usual files : %s", err)
  167. }
  168. if id == "" || err != nil {
  169. bID, err := ioutil.ReadFile(uuid)
  170. if err != nil {
  171. log.Fatalf("can'get a valid machine_id")
  172. }
  173. id = string(bID)
  174. id = strings.ReplaceAll(id, "-", "")[:32]
  175. }
  176. password := generatePassword()
  177. if err := outputCTX.API.RegisterMachine(id, password); err != nil {
  178. log.Fatalf(err.Error())
  179. }
  180. fmt.Printf("machine_id: %s\n", outputCTX.API.Creds.User)
  181. fmt.Printf("password: %s\n", outputCTX.API.Creds.Password)
  182. },
  183. }
  184. var cmdAPIEnroll = &cobra.Command{
  185. Use: "enroll",
  186. Short: "Associate your machine to an existing crowdsec user",
  187. Long: `Enrolling your machine into your user account will allow for more accurate lists and threat detection. See website to create user account.`,
  188. Example: `cscli api enroll -u 1234567890ffff`,
  189. Args: cobra.MinimumNArgs(0),
  190. Run: func(cmd *cobra.Command, args []string) {
  191. if err := outputCTX.API.Signin(); err != nil {
  192. log.Fatalf("unable to signin : %s", err)
  193. }
  194. if err := outputCTX.API.Enroll(userID); err != nil {
  195. log.Fatalf(err.Error())
  196. }
  197. },
  198. }
  199. var cmdAPIResetPassword = &cobra.Command{
  200. Use: "reset",
  201. Short: "Reset password on CrowdSec API",
  202. Long: `Attempts to reset your credentials to the API.`,
  203. Example: `cscli api reset`,
  204. Args: cobra.MinimumNArgs(0),
  205. Run: func(cmd *cobra.Command, args []string) {
  206. id, err := machineid.ID()
  207. if err != nil {
  208. log.Debugf("failed to get machine-id with usual files : %s", err)
  209. }
  210. if id == "" || err != nil {
  211. bID, err := ioutil.ReadFile(uuid)
  212. if err != nil {
  213. log.Fatalf("can'get a valid machine_id")
  214. }
  215. id = string(bID)
  216. id = strings.ReplaceAll(id, "-", "")[:32]
  217. }
  218. password := generatePassword()
  219. if err := outputCTX.API.ResetPassword(id, password); err != nil {
  220. log.Fatalf(err.Error())
  221. }
  222. fmt.Printf("machine_id: %s\n", outputCTX.API.Creds.User)
  223. fmt.Printf("password: %s\n", outputCTX.API.Creds.Password)
  224. },
  225. }
  226. var cmdAPIPull = &cobra.Command{
  227. Use: "pull",
  228. Short: "Pull crowdsec API TopX",
  229. Long: `Pulls a list of malveolent IPs relevant to your situation and add them into the local ban database.`,
  230. Example: `cscli api pull`,
  231. Args: cobra.MinimumNArgs(0),
  232. Run: func(cmd *cobra.Command, args []string) {
  233. if err := cwhub.GetHubIdx(); err != nil {
  234. log.Fatalf(err.Error())
  235. }
  236. err := pullTOP()
  237. if err != nil {
  238. log.Fatalf(err.Error())
  239. }
  240. },
  241. }
  242. var cmdAPICreds = &cobra.Command{
  243. Use: "credentials",
  244. Short: "Display api credentials",
  245. Long: ``,
  246. Example: `cscli api credentials`,
  247. Args: cobra.MinimumNArgs(0),
  248. Run: func(cmd *cobra.Command, args []string) {
  249. if err := dumpCredentials(); err != nil {
  250. log.Fatalf(err.Error())
  251. }
  252. },
  253. }
  254. cmdAPI.AddCommand(cmdAPICreds)
  255. cmdAPIEnroll.Flags().StringVarP(&userID, "user", "u", "", "User ID (required)")
  256. if err := cmdAPIEnroll.MarkFlagRequired("user"); err != nil {
  257. log.Errorf("'user' flag : %s", err)
  258. }
  259. cmdAPI.AddCommand(cmdAPIEnroll)
  260. cmdAPI.AddCommand(cmdAPIResetPassword)
  261. cmdAPI.AddCommand(cmdAPIRegister)
  262. cmdAPI.AddCommand(cmdAPIPull)
  263. return cmdAPI
  264. }