console.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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/fatih/color"
  12. "github.com/go-openapi/strfmt"
  13. log "github.com/sirupsen/logrus"
  14. "github.com/spf13/cobra"
  15. "github.com/crowdsecurity/crowdsec/pkg/apiclient"
  16. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  17. "github.com/crowdsecurity/crowdsec/pkg/cwhub"
  18. "github.com/crowdsecurity/crowdsec/pkg/cwversion"
  19. "github.com/crowdsecurity/crowdsec/pkg/fflag"
  20. "github.com/crowdsecurity/crowdsec/pkg/types"
  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 accessing console features.")
  47. }
  48. return nil
  49. },
  50. }
  51. name := ""
  52. overwrite := false
  53. tags := []string{}
  54. cmdEnroll := &cobra.Command{
  55. Use: "enroll [enroll-key]",
  56. Short: "Enroll this instance to https://app.crowdsec.net [requires local API]",
  57. Long: `
  58. Enroll this instance to https://app.crowdsec.net
  59. You can get your enrollment key by creating an account on https://app.crowdsec.net.
  60. After running this command your will need to validate the enrollment in the webapp.`,
  61. Example: `cscli console enroll YOUR-ENROLL-KEY
  62. cscli console enroll --name [instance_name] YOUR-ENROLL-KEY
  63. cscli console enroll --name [instance_name] --tags [tag_1] --tags [tag_2] YOUR-ENROLL-KEY
  64. `,
  65. Args: cobra.ExactArgs(1),
  66. DisableAutoGenTag: true,
  67. Run: func(cmd *cobra.Command, args []string) {
  68. password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
  69. apiURL, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
  70. if err != nil {
  71. log.Fatalf("Could not parse CAPI URL : %s", err)
  72. }
  73. if err := csConfig.LoadHub(); err != nil {
  74. log.Fatal(err)
  75. }
  76. if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
  77. log.Fatalf("Failed to load hub index : %s", err)
  78. log.Info("Run 'sudo cscli hub update' to get the hub index")
  79. }
  80. scenarios, err := cwhub.GetInstalledScenariosAsString()
  81. if err != nil {
  82. log.Fatalf("failed to get scenarios : %s", err)
  83. }
  84. if len(scenarios) == 0 {
  85. scenarios = make([]string, 0)
  86. }
  87. c, _ := apiclient.NewClient(&apiclient.Config{
  88. MachineID: csConfig.API.Server.OnlineClient.Credentials.Login,
  89. Password: password,
  90. Scenarios: scenarios,
  91. UserAgent: fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
  92. URL: apiURL,
  93. VersionPrefix: "v3",
  94. })
  95. resp, err := c.Auth.EnrollWatcher(context.Background(), args[0], name, tags, overwrite)
  96. if err != nil {
  97. log.Fatalf("Could not enroll instance: %s", err)
  98. }
  99. if resp.Response.StatusCode == 200 && !overwrite {
  100. log.Warning("Instance already enrolled. You can use '--overwrite' to force enroll")
  101. return
  102. }
  103. SetConsoleOpts(csconfig.CONSOLE_CONFIGS, true)
  104. if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
  105. log.Fatalf("failed writing console config : %s", err)
  106. }
  107. log.Infof("Enabled tainted&manual alerts sharing, see 'cscli console status'.")
  108. log.Infof("Watcher successfully enrolled. Visit https://app.crowdsec.net to accept it.")
  109. log.Infof("Please restart crowdsec after accepting the enrollment.")
  110. },
  111. }
  112. cmdEnroll.Flags().StringVarP(&name, "name", "n", "", "Name to display in the console")
  113. cmdEnroll.Flags().BoolVarP(&overwrite, "overwrite", "", false, "Force enroll the instance")
  114. cmdEnroll.Flags().StringSliceVarP(&tags, "tags", "t", tags, "Tags to display in the console")
  115. cmdConsole.AddCommand(cmdEnroll)
  116. var enableAll, disableAll bool
  117. cmdEnable := &cobra.Command{
  118. Use: "enable [option]",
  119. Short: "Enable a console option",
  120. Example: "sudo cscli console enable tainted",
  121. Long: `
  122. Enable given information push to the central API. Allows to empower the console`,
  123. ValidArgs: csconfig.CONSOLE_CONFIGS,
  124. DisableAutoGenTag: true,
  125. Run: func(cmd *cobra.Command, args []string) {
  126. if enableAll {
  127. SetConsoleOpts(csconfig.CONSOLE_CONFIGS, true)
  128. log.Infof("All features have been enabled successfully")
  129. } else {
  130. if len(args) == 0 {
  131. log.Fatalf("You must specify at least one feature to enable")
  132. }
  133. SetConsoleOpts(args, true)
  134. log.Infof("%v have been enabled", args)
  135. }
  136. if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
  137. log.Fatalf("failed writing console config : %s", err)
  138. }
  139. log.Infof(ReloadMessage())
  140. },
  141. }
  142. cmdEnable.Flags().BoolVarP(&enableAll, "all", "a", false, "Enable all console options")
  143. cmdConsole.AddCommand(cmdEnable)
  144. cmdDisable := &cobra.Command{
  145. Use: "disable [option]",
  146. Short: "Disable a console option",
  147. Example: "sudo cscli console disable tainted",
  148. Long: `
  149. Disable given information push to the central API.`,
  150. ValidArgs: csconfig.CONSOLE_CONFIGS,
  151. Args: cobra.MinimumNArgs(1),
  152. DisableAutoGenTag: true,
  153. Run: func(cmd *cobra.Command, args []string) {
  154. if disableAll {
  155. SetConsoleOpts(csconfig.CONSOLE_CONFIGS, false)
  156. } else {
  157. SetConsoleOpts(args, false)
  158. }
  159. if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
  160. log.Fatalf("failed writing console config : %s", err)
  161. }
  162. if disableAll {
  163. log.Infof("All features have been disabled")
  164. } else {
  165. log.Infof("%v have been disabled", args)
  166. }
  167. log.Infof(ReloadMessage())
  168. },
  169. }
  170. cmdDisable.Flags().BoolVarP(&disableAll, "all", "a", false, "Disable all console options")
  171. cmdConsole.AddCommand(cmdDisable)
  172. cmdConsoleStatus := &cobra.Command{
  173. Use: "status [option]",
  174. Short: "Shows status of one or all console options",
  175. Example: `sudo cscli console status`,
  176. DisableAutoGenTag: true,
  177. Run: func(cmd *cobra.Command, args []string) {
  178. switch csConfig.Cscli.Output {
  179. case "human":
  180. cmdConsoleStatusTable(color.Output, *csConfig)
  181. case "json":
  182. data, err := json.MarshalIndent(csConfig.API.Server.ConsoleConfig, "", " ")
  183. if err != nil {
  184. log.Fatalf("failed to marshal configuration: %s", err)
  185. }
  186. fmt.Printf("%s\n", string(data))
  187. case "raw":
  188. csvwriter := csv.NewWriter(os.Stdout)
  189. err := csvwriter.Write([]string{"option", "enabled"})
  190. if err != nil {
  191. log.Fatal(err)
  192. }
  193. rows := [][]string{
  194. {csconfig.SEND_MANUAL_SCENARIOS, fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareManualDecisions)},
  195. {csconfig.SEND_CUSTOM_SCENARIOS, fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios)},
  196. {csconfig.SEND_TAINTED_SCENARIOS, fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios)},
  197. {csconfig.SEND_CONTEXT, fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareContext)},
  198. {csconfig.CONSOLE_MANAGEMENT, fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ReceiveDecisions)},
  199. }
  200. for _, row := range rows {
  201. err = csvwriter.Write(row)
  202. if err != nil {
  203. log.Fatal(err)
  204. }
  205. }
  206. csvwriter.Flush()
  207. }
  208. },
  209. }
  210. cmdConsole.AddCommand(cmdConsoleStatus)
  211. return cmdConsole
  212. }
  213. func SetConsoleOpts(args []string, wanted bool) {
  214. for _, arg := range args {
  215. switch arg {
  216. case csconfig.CONSOLE_MANAGEMENT:
  217. if !fflag.PapiClient.IsEnabled() {
  218. continue
  219. }
  220. /*for each flag check if it's already set before setting it*/
  221. if csConfig.API.Server.ConsoleConfig.ReceiveDecisions != nil {
  222. if *csConfig.API.Server.ConsoleConfig.ReceiveDecisions == wanted {
  223. log.Debugf("%s already set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
  224. } else {
  225. log.Infof("%s set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
  226. *csConfig.API.Server.ConsoleConfig.ReceiveDecisions = wanted
  227. }
  228. } else {
  229. log.Infof("%s set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
  230. csConfig.API.Server.ConsoleConfig.ReceiveDecisions = types.BoolPtr(wanted)
  231. }
  232. case csconfig.SEND_CUSTOM_SCENARIOS:
  233. /*for each flag check if it's already set before setting it*/
  234. if csConfig.API.Server.ConsoleConfig.ShareCustomScenarios != nil {
  235. if *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios == wanted {
  236. log.Debugf("%s already set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
  237. } else {
  238. log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
  239. *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = wanted
  240. }
  241. } else {
  242. log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
  243. csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = types.BoolPtr(wanted)
  244. }
  245. case csconfig.SEND_TAINTED_SCENARIOS:
  246. /*for each flag check if it's already set before setting it*/
  247. if csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios != nil {
  248. if *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios == wanted {
  249. log.Debugf("%s already set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
  250. } else {
  251. log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
  252. *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = wanted
  253. }
  254. } else {
  255. log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
  256. csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = types.BoolPtr(wanted)
  257. }
  258. case csconfig.SEND_MANUAL_SCENARIOS:
  259. /*for each flag check if it's already set before setting it*/
  260. if csConfig.API.Server.ConsoleConfig.ShareManualDecisions != nil {
  261. if *csConfig.API.Server.ConsoleConfig.ShareManualDecisions == wanted {
  262. log.Debugf("%s already set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
  263. } else {
  264. log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
  265. *csConfig.API.Server.ConsoleConfig.ShareManualDecisions = wanted
  266. }
  267. } else {
  268. log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
  269. csConfig.API.Server.ConsoleConfig.ShareManualDecisions = types.BoolPtr(wanted)
  270. }
  271. case csconfig.SEND_CONTEXT:
  272. /*for each flag check if it's already set before setting it*/
  273. if csConfig.API.Server.ConsoleConfig.ShareContext != nil {
  274. if *csConfig.API.Server.ConsoleConfig.ShareContext == wanted {
  275. log.Debugf("%s already set to %t", csconfig.SEND_CONTEXT, wanted)
  276. } else {
  277. log.Infof("%s set to %t", csconfig.SEND_CONTEXT, wanted)
  278. *csConfig.API.Server.ConsoleConfig.ShareContext = wanted
  279. }
  280. } else {
  281. log.Infof("%s set to %t", csconfig.SEND_CONTEXT, wanted)
  282. csConfig.API.Server.ConsoleConfig.ShareContext = types.BoolPtr(wanted)
  283. }
  284. default:
  285. log.Fatalf("unknown flag %s", arg)
  286. }
  287. }
  288. }