setup.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. log "github.com/sirupsen/logrus"
  8. "github.com/spf13/cobra"
  9. "gopkg.in/yaml.v3"
  10. goccyyaml "github.com/goccy/go-yaml"
  11. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  12. "github.com/crowdsecurity/crowdsec/pkg/setup"
  13. )
  14. // NewSetupCmd defines the "cscli setup" command.
  15. func NewSetupCmd() *cobra.Command {
  16. cmdSetup := &cobra.Command{
  17. Use: "setup",
  18. Short: "Tools to configure crowdsec",
  19. Long: "Manage hub configuration and service detection",
  20. Args: cobra.MinimumNArgs(0),
  21. DisableAutoGenTag: true,
  22. }
  23. //
  24. // cscli setup detect
  25. //
  26. {
  27. cmdSetupDetect := &cobra.Command{
  28. Use: "detect",
  29. Short: "detect running services, generate a setup file",
  30. DisableAutoGenTag: true,
  31. RunE: runSetupDetect,
  32. }
  33. defaultServiceDetect := csconfig.DefaultConfigPath("hub", "detect.yaml")
  34. flags := cmdSetupDetect.Flags()
  35. flags.String("detect-config", defaultServiceDetect, "path to service detection configuration")
  36. flags.Bool("list-supported-services", false, "do not detect; only print supported services")
  37. flags.StringSlice("force-unit", nil, "force detection of a systemd unit (can be repeated)")
  38. flags.StringSlice("force-process", nil, "force detection of a running process (can be repeated)")
  39. flags.StringSlice("skip-service", nil, "ignore a service, don't recommend hub/datasources (can be repeated)")
  40. flags.String("force-os-family", "", "override OS.Family: one of linux, freebsd, windows or darwin")
  41. flags.String("force-os-id", "", "override OS.ID=[debian | ubuntu | , redhat...]")
  42. flags.String("force-os-version", "", "override OS.RawVersion (of OS or Linux distribution)")
  43. flags.Bool("snub-systemd", false, "don't use systemd, even if available")
  44. flags.Bool("yaml", false, "output yaml, not json")
  45. cmdSetup.AddCommand(cmdSetupDetect)
  46. }
  47. //
  48. // cscli setup install-hub
  49. //
  50. {
  51. cmdSetupInstallHub := &cobra.Command{
  52. Use: "install-hub [setup_file] [flags]",
  53. Short: "install items from a setup file",
  54. Args: cobra.ExactArgs(1),
  55. DisableAutoGenTag: true,
  56. RunE: runSetupInstallHub,
  57. }
  58. flags := cmdSetupInstallHub.Flags()
  59. flags.Bool("dry-run", false, "don't install anything; print out what would have been")
  60. cmdSetup.AddCommand(cmdSetupInstallHub)
  61. }
  62. //
  63. // cscli setup datasources
  64. //
  65. {
  66. cmdSetupDataSources := &cobra.Command{
  67. Use: "datasources [setup_file] [flags]",
  68. Short: "generate datasource (acquisition) configuration from a setup file",
  69. Args: cobra.ExactArgs(1),
  70. DisableAutoGenTag: true,
  71. RunE: runSetupDataSources,
  72. }
  73. flags := cmdSetupDataSources.Flags()
  74. flags.String("to-dir", "", "write the configuration to a directory, in multiple files")
  75. cmdSetup.AddCommand(cmdSetupDataSources)
  76. }
  77. //
  78. // cscli setup validate
  79. //
  80. {
  81. cmdSetupValidate := &cobra.Command{
  82. Use: "validate [setup_file]",
  83. Short: "validate a setup file",
  84. Args: cobra.ExactArgs(1),
  85. DisableAutoGenTag: true,
  86. RunE: runSetupValidate,
  87. }
  88. cmdSetup.AddCommand(cmdSetupValidate)
  89. }
  90. return cmdSetup
  91. }
  92. func runSetupDetect(cmd *cobra.Command, args []string) error {
  93. flags := cmd.Flags()
  94. detectConfigFile, err := flags.GetString("detect-config")
  95. if err != nil {
  96. return err
  97. }
  98. var detectReader *os.File
  99. switch detectConfigFile {
  100. case "-":
  101. log.Tracef("Reading detection rules from stdin")
  102. detectReader = os.Stdin
  103. default:
  104. log.Tracef("Reading detection rules: %s", detectConfigFile)
  105. detectReader, err = os.Open(detectConfigFile)
  106. if err != nil {
  107. return err
  108. }
  109. }
  110. listSupportedServices, err := flags.GetBool("list-supported-services")
  111. if err != nil {
  112. return err
  113. }
  114. forcedUnits, err := flags.GetStringSlice("force-unit")
  115. if err != nil {
  116. return err
  117. }
  118. forcedProcesses, err := flags.GetStringSlice("force-process")
  119. if err != nil {
  120. return err
  121. }
  122. forcedOSFamily, err := flags.GetString("force-os-family")
  123. if err != nil {
  124. return err
  125. }
  126. forcedOSID, err := flags.GetString("force-os-id")
  127. if err != nil {
  128. return err
  129. }
  130. forcedOSVersion, err := flags.GetString("force-os-version")
  131. if err != nil {
  132. return err
  133. }
  134. skipServices, err := flags.GetStringSlice("skip-service")
  135. if err != nil {
  136. return err
  137. }
  138. snubSystemd, err := flags.GetBool("snub-systemd")
  139. if err != nil {
  140. return err
  141. }
  142. if !snubSystemd {
  143. _, err := exec.LookPath("systemctl")
  144. if err != nil {
  145. log.Debug("systemctl not available: snubbing systemd")
  146. snubSystemd = true
  147. }
  148. }
  149. outYaml, err := flags.GetBool("yaml")
  150. if err != nil {
  151. return err
  152. }
  153. if forcedOSFamily == "" && forcedOSID != "" {
  154. log.Debug("force-os-id is set: force-os-family defaults to 'linux'")
  155. forcedOSFamily = "linux"
  156. }
  157. if listSupportedServices {
  158. supported, err := setup.ListSupported(detectReader)
  159. if err != nil {
  160. return err
  161. }
  162. for _, svc := range supported {
  163. fmt.Println(svc)
  164. }
  165. return nil
  166. }
  167. opts := setup.DetectOptions{
  168. ForcedUnits: forcedUnits,
  169. ForcedProcesses: forcedProcesses,
  170. ForcedOS: setup.ExprOS{
  171. Family: forcedOSFamily,
  172. ID: forcedOSID,
  173. RawVersion: forcedOSVersion,
  174. },
  175. SkipServices: skipServices,
  176. SnubSystemd: snubSystemd,
  177. }
  178. hubSetup, err := setup.Detect(detectReader, opts)
  179. if err != nil {
  180. return fmt.Errorf("detecting services: %w", err)
  181. }
  182. setup, err := setupAsString(hubSetup, outYaml)
  183. if err != nil {
  184. return err
  185. }
  186. fmt.Println(setup)
  187. return nil
  188. }
  189. func setupAsString(cs setup.Setup, outYaml bool) (string, error) {
  190. var (
  191. ret []byte
  192. err error
  193. )
  194. wrap := func(err error) error {
  195. return fmt.Errorf("while marshaling setup: %w", err)
  196. }
  197. indentLevel := 2
  198. buf := &bytes.Buffer{}
  199. enc := yaml.NewEncoder(buf)
  200. enc.SetIndent(indentLevel)
  201. if err = enc.Encode(cs); err != nil {
  202. return "", wrap(err)
  203. }
  204. if err = enc.Close(); err != nil {
  205. return "", wrap(err)
  206. }
  207. ret = buf.Bytes()
  208. if !outYaml {
  209. // take a general approach to output json, so we avoid the
  210. // double tags in the structures and can use go-yaml features
  211. // missing from the json package
  212. ret, err = goccyyaml.YAMLToJSON(ret)
  213. if err != nil {
  214. return "", wrap(err)
  215. }
  216. }
  217. return string(ret), nil
  218. }
  219. func runSetupDataSources(cmd *cobra.Command, args []string) error {
  220. flags := cmd.Flags()
  221. fromFile := args[0]
  222. toDir, err := flags.GetString("to-dir")
  223. if err != nil {
  224. return err
  225. }
  226. input, err := os.ReadFile(fromFile)
  227. if err != nil {
  228. return fmt.Errorf("while reading setup file: %w", err)
  229. }
  230. output, err := setup.DataSources(input, toDir)
  231. if err != nil {
  232. return err
  233. }
  234. if toDir == "" {
  235. fmt.Println(output)
  236. }
  237. return nil
  238. }
  239. func runSetupInstallHub(cmd *cobra.Command, args []string) error {
  240. flags := cmd.Flags()
  241. fromFile := args[0]
  242. dryRun, err := flags.GetBool("dry-run")
  243. if err != nil {
  244. return err
  245. }
  246. input, err := os.ReadFile(fromFile)
  247. if err != nil {
  248. return fmt.Errorf("while reading file %s: %w", fromFile, err)
  249. }
  250. if err = setup.InstallHubItems(csConfig, input, dryRun); err != nil {
  251. return err
  252. }
  253. return nil
  254. }
  255. func runSetupValidate(cmd *cobra.Command, args []string) error {
  256. fromFile := args[0]
  257. input, err := os.ReadFile(fromFile)
  258. if err != nil {
  259. return fmt.Errorf("while reading stdin: %w", err)
  260. }
  261. if err = setup.Validate(input); err != nil {
  262. fmt.Printf("%v\n", err)
  263. return fmt.Errorf("invalid setup file")
  264. }
  265. return nil
  266. }