console.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. package main
  2. import (
  3. "context"
  4. "encoding/csv"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io/fs"
  9. "net/url"
  10. "os"
  11. "github.com/crowdsecurity/crowdsec/pkg/apiclient"
  12. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  13. "github.com/crowdsecurity/crowdsec/pkg/cwhub"
  14. "github.com/crowdsecurity/crowdsec/pkg/cwversion"
  15. "github.com/crowdsecurity/crowdsec/pkg/types"
  16. "github.com/enescakir/emoji"
  17. "github.com/go-openapi/strfmt"
  18. "github.com/olekukonko/tablewriter"
  19. log "github.com/sirupsen/logrus"
  20. "github.com/spf13/cobra"
  21. )
  22. func NewConsoleCmd() *cobra.Command {
  23. var cmdConsole = &cobra.Command{
  24. Use: "console [action]",
  25. Short: "Manage interaction with Crowdsec console (https://app.crowdsec.net)",
  26. Args: cobra.MinimumNArgs(1),
  27. DisableAutoGenTag: true,
  28. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  29. if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
  30. var fdErr *fs.PathError
  31. if errors.As(err, &fdErr) {
  32. log.Fatalf("Unable to load Local API : %s", fdErr)
  33. }
  34. if err != nil {
  35. log.Fatalf("Unable to load required Local API Configuration : %s", err)
  36. }
  37. log.Fatal("Local API is disabled, please run this command on the local API machine")
  38. }
  39. if csConfig.DisableAPI {
  40. log.Fatal("Local API is disabled, please run this command on the local API machine")
  41. }
  42. if csConfig.API.Server.OnlineClient == nil {
  43. log.Fatalf("No configuration for Central API (CAPI) in '%s'", *csConfig.FilePath)
  44. }
  45. if csConfig.API.Server.OnlineClient.Credentials == nil {
  46. log.Fatal("You must configure Central API (CAPI) with `cscli capi register` before enrolling your instance")
  47. }
  48. return nil
  49. },
  50. }
  51. name := ""
  52. tags := []string{}
  53. cmdEnroll := &cobra.Command{
  54. Use: "enroll [enroll-key]",
  55. Short: "Enroll this instance to https://app.crowdsec.net [requires local API]",
  56. Long: `
  57. Enroll this instance to https://app.crowdsec.net
  58. You can get your enrollment key by creating an account on https://app.crowdsec.net.
  59. After running this command your will need to validate the enrollment in the webapp.`,
  60. Example: `cscli console enroll YOUR-ENROLL-KEY
  61. cscli console enroll --name [instance_name] YOUR-ENROLL-KEY
  62. cscli console enroll --name [instance_name] --tags [tag_1] --tags [tag_2] YOUR-ENROLL-KEY
  63. `,
  64. Args: cobra.ExactArgs(1),
  65. DisableAutoGenTag: true,
  66. Run: func(cmd *cobra.Command, args []string) {
  67. password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
  68. apiURL, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
  69. if err != nil {
  70. log.Fatalf("Could not parse CAPI URL : %s", err)
  71. }
  72. if err := csConfig.LoadHub(); err != nil {
  73. log.Fatalf(err.Error())
  74. }
  75. if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
  76. log.Fatalf("Failed to load hub index : %s", err)
  77. log.Infoln("Run 'sudo cscli hub update' to get the hub index")
  78. }
  79. scenarios, err := cwhub.GetInstalledScenariosAsString()
  80. if err != nil {
  81. log.Fatalf("failed to get scenarios : %s", err.Error())
  82. }
  83. if len(scenarios) == 0 {
  84. scenarios = make([]string, 0)
  85. }
  86. c, _ := apiclient.NewClient(&apiclient.Config{
  87. MachineID: csConfig.API.Server.OnlineClient.Credentials.Login,
  88. Password: password,
  89. Scenarios: scenarios,
  90. UserAgent: fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
  91. URL: apiURL,
  92. VersionPrefix: "v2",
  93. })
  94. _, err = c.Auth.EnrollWatcher(context.Background(), args[0], name, tags)
  95. if err != nil {
  96. log.Fatalf("Could not enroll instance: %s", err)
  97. }
  98. SetConsoleOpts(csconfig.CONSOLE_CONFIGS, true)
  99. if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
  100. log.Fatalf("failed writing console config : %s", err)
  101. }
  102. log.Infof("Enabled tainted&manual alerts sharing, see 'cscli console status'.")
  103. log.Infof("Watcher successfully enrolled. Visit https://app.crowdsec.net to accept it.")
  104. log.Infof("Please restart crowdsec after accepting the enrollment.")
  105. },
  106. }
  107. cmdEnroll.Flags().StringVarP(&name, "name", "n", "", "Name to display in the console")
  108. cmdEnroll.Flags().StringSliceVarP(&tags, "tags", "t", tags, "Tags to display in the console")
  109. cmdConsole.AddCommand(cmdEnroll)
  110. var enableAll, disableAll bool
  111. cmdEnable := &cobra.Command{
  112. Use: "enable [feature-flag]",
  113. Short: "Enable a feature flag",
  114. Example: "enable alerts-tainted",
  115. Long: `
  116. Enable given information push to the central API. Allows to empower the console`,
  117. ValidArgs: csconfig.CONSOLE_CONFIGS,
  118. DisableAutoGenTag: true,
  119. Run: func(cmd *cobra.Command, args []string) {
  120. if enableAll {
  121. SetConsoleOpts(csconfig.CONSOLE_CONFIGS, true)
  122. log.Infof("All features have been enabled successfully")
  123. } else {
  124. if len(args) == 0 {
  125. log.Fatalf("You must specify at least one feature to enable")
  126. }
  127. SetConsoleOpts(args, true)
  128. log.Infof("%v have been enabled", args)
  129. }
  130. if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
  131. log.Fatalf("failed writing console config : %s", err)
  132. }
  133. log.Infof(ReloadMessage())
  134. },
  135. }
  136. cmdEnable.Flags().BoolVarP(&enableAll, "all", "a", false, "Enable all feature flags")
  137. cmdConsole.AddCommand(cmdEnable)
  138. cmdDisable := &cobra.Command{
  139. Use: "disable [feature-flag]",
  140. Short: "Disable a feature flag",
  141. Example: "disable alerts-tainted",
  142. Long: `
  143. Disable given information push to the central API.`,
  144. ValidArgs: csconfig.CONSOLE_CONFIGS,
  145. Args: cobra.MinimumNArgs(1),
  146. DisableAutoGenTag: true,
  147. Run: func(cmd *cobra.Command, args []string) {
  148. if disableAll {
  149. SetConsoleOpts(csconfig.CONSOLE_CONFIGS, false)
  150. } else {
  151. SetConsoleOpts(args, false)
  152. }
  153. if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
  154. log.Fatalf("failed writing console config : %s", err)
  155. }
  156. if disableAll {
  157. log.Infof("All features have been disabled")
  158. } else {
  159. log.Infof("%v have been disabled", args)
  160. }
  161. log.Infof(ReloadMessage())
  162. },
  163. }
  164. cmdDisable.Flags().BoolVarP(&disableAll, "all", "a", false, "Enable all feature flags")
  165. cmdConsole.AddCommand(cmdDisable)
  166. cmdConsoleStatus := &cobra.Command{
  167. Use: "status [feature-flag]",
  168. Short: "Shows status of one or all feature flags",
  169. Example: "status alerts-tainted",
  170. DisableAutoGenTag: true,
  171. Run: func(cmd *cobra.Command, args []string) {
  172. switch csConfig.Cscli.Output {
  173. case "human":
  174. table := tablewriter.NewWriter(os.Stdout)
  175. table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
  176. table.SetAlignment(tablewriter.ALIGN_LEFT)
  177. table.SetHeader([]string{"Option Name", "Activated", "Description"})
  178. for _, option := range csconfig.CONSOLE_CONFIGS {
  179. switch option {
  180. case csconfig.SEND_CUSTOM_SCENARIOS:
  181. activated := string(emoji.CrossMark)
  182. if *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios {
  183. activated = string(emoji.CheckMarkButton)
  184. }
  185. table.Append([]string{option, activated, "Send alerts from custom scenarios to the console"})
  186. case csconfig.SEND_MANUAL_SCENARIOS:
  187. activated := string(emoji.CrossMark)
  188. if *csConfig.API.Server.ConsoleConfig.ShareManualDecisions {
  189. activated = string(emoji.CheckMarkButton)
  190. }
  191. table.Append([]string{option, activated, "Send manual decisions to the console"})
  192. case csconfig.SEND_TAINTED_SCENARIOS:
  193. activated := string(emoji.CrossMark)
  194. if *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios {
  195. activated = string(emoji.CheckMarkButton)
  196. }
  197. table.Append([]string{option, activated, "Send alerts from tainted scenarios to the console"})
  198. case csconfig.SEND_LABEL:
  199. activated := string(emoji.CrossMark)
  200. if *csConfig.API.Server.ConsoleConfig.ShareLabel {
  201. activated = string(emoji.CheckMarkButton)
  202. }
  203. table.Append([]string{option, activated, "Send label with alerts to the console"})
  204. }
  205. }
  206. table.Render()
  207. case "json":
  208. data, err := json.MarshalIndent(csConfig.API.Server.ConsoleConfig, "", " ")
  209. if err != nil {
  210. log.Fatalf("failed to marshal configuration: %s", err)
  211. }
  212. fmt.Printf("%s\n", string(data))
  213. case "raw":
  214. csvwriter := csv.NewWriter(os.Stdout)
  215. err := csvwriter.Write([]string{"option", "enabled"})
  216. if err != nil {
  217. log.Fatal(err)
  218. }
  219. rows := [][]string{
  220. {"share_manual_decisions", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareManualDecisions)},
  221. {"share_custom", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios)},
  222. {"share_tainted", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios)},
  223. {"share_labels", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareLabel)},
  224. }
  225. for _, row := range rows {
  226. err = csvwriter.Write(row)
  227. if err != nil {
  228. log.Fatal(err)
  229. }
  230. }
  231. csvwriter.Flush()
  232. }
  233. },
  234. }
  235. cmdConsole.AddCommand(cmdConsoleStatus)
  236. cmdLabel := &cobra.Command{
  237. Use: "label [feature-flag]",
  238. Short: "Manage label to send with alerts",
  239. DisableAutoGenTag: true,
  240. Run: func(cmd *cobra.Command, args []string) {
  241. printHelp(cmd)
  242. },
  243. }
  244. var key string
  245. var values []string
  246. cmdLabelAdd := &cobra.Command{
  247. Use: "add",
  248. Short: "Add label to send with alerts",
  249. DisableAutoGenTag: true,
  250. Run: func(cmd *cobra.Command, args []string) {
  251. if _, ok := csConfig.API.Server.ConsoleConfig.LabelsToSend[key]; !ok {
  252. csConfig.API.Server.ConsoleConfig.LabelsToSend[key] = make([]string, 0)
  253. }
  254. data := csConfig.API.Server.ConsoleConfig.LabelsToSend[key]
  255. for _, val := range values {
  256. if !inSlice(val, data) {
  257. data = append(data, val)
  258. }
  259. csConfig.API.Server.ConsoleConfig.LabelsToSend[key] = data
  260. }
  261. if err := csConfig.API.Server.DumpLabelConfigFile(); err != nil {
  262. log.Fatalf(err.Error())
  263. }
  264. },
  265. }
  266. cmdLabelAdd.Flags().StringVarP(&key, "key", "k", "", "The key of the different values to send")
  267. cmdLabelAdd.Flags().StringSliceVar(&values, "value", []string{}, "The expr fields to associate with the key")
  268. cmdLabelAdd.MarkFlagRequired("key")
  269. cmdLabelAdd.MarkFlagRequired("value")
  270. cmdLabel.AddCommand(cmdLabelAdd)
  271. cmdConsole.AddCommand(cmdLabel)
  272. return cmdConsole
  273. }
  274. func SetConsoleOpts(args []string, wanted bool) {
  275. for _, arg := range args {
  276. switch arg {
  277. case csconfig.SEND_CUSTOM_SCENARIOS:
  278. /*for each flag check if it's already set before setting it*/
  279. if csConfig.API.Server.ConsoleConfig.ShareCustomScenarios != nil {
  280. if *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios == wanted {
  281. log.Infof("%s already set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
  282. } else {
  283. log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
  284. *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = wanted
  285. }
  286. } else {
  287. log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
  288. csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = types.BoolPtr(wanted)
  289. }
  290. case csconfig.SEND_TAINTED_SCENARIOS:
  291. /*for each flag check if it's already set before setting it*/
  292. if csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios != nil {
  293. if *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios == wanted {
  294. log.Infof("%s already set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
  295. } else {
  296. log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
  297. *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = wanted
  298. }
  299. } else {
  300. log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
  301. csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = types.BoolPtr(wanted)
  302. }
  303. case csconfig.SEND_MANUAL_SCENARIOS:
  304. /*for each flag check if it's already set before setting it*/
  305. if csConfig.API.Server.ConsoleConfig.ShareManualDecisions != nil {
  306. if *csConfig.API.Server.ConsoleConfig.ShareManualDecisions == wanted {
  307. log.Infof("%s already set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
  308. } else {
  309. log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
  310. *csConfig.API.Server.ConsoleConfig.ShareManualDecisions = wanted
  311. }
  312. } else {
  313. log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
  314. csConfig.API.Server.ConsoleConfig.ShareManualDecisions = types.BoolPtr(wanted)
  315. }
  316. case csconfig.SEND_LABEL:
  317. /*for each flag check if it's already set before setting it*/
  318. if csConfig.API.Server.ConsoleConfig.ShareLabel != nil {
  319. if *csConfig.API.Server.ConsoleConfig.ShareLabel == wanted {
  320. log.Infof("%s already set to %t", csconfig.SEND_LABEL, wanted)
  321. } else {
  322. log.Infof("%s set to %t", csconfig.SEND_LABEL, wanted)
  323. *csConfig.API.Server.ConsoleConfig.ShareLabel = wanted
  324. }
  325. } else {
  326. log.Infof("%s set to %t", csconfig.SEND_LABEL, wanted)
  327. csConfig.API.Server.ConsoleConfig.ShareLabel = types.BoolPtr(wanted)
  328. }
  329. default:
  330. log.Fatalf("unknown flag %s", arg)
  331. }
  332. }
  333. }