github.com/crowdsecurity/crowdsec@v1.6.1/cmd/crowdsec-cli/setup.go (about)

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