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

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"slices"
     7  
     8  	log "github.com/sirupsen/logrus"
     9  	"github.com/spf13/cobra"
    10  	"gopkg.in/yaml.v2"
    11  
    12  	"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
    13  	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
    14  )
    15  
    16  type cliSimulation struct {
    17  	cfg configGetter
    18  }
    19  
    20  func NewCLISimulation(cfg configGetter) *cliSimulation {
    21  	return &cliSimulation{
    22  		cfg: cfg,
    23  	}
    24  }
    25  
    26  func (cli *cliSimulation) NewCommand() *cobra.Command {
    27  	cmd := &cobra.Command{
    28  		Use:   "simulation [command]",
    29  		Short: "Manage simulation status of scenarios",
    30  		Example: `cscli simulation status
    31  cscli simulation enable crowdsecurity/ssh-bf
    32  cscli simulation disable crowdsecurity/ssh-bf`,
    33  		DisableAutoGenTag: true,
    34  		PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
    35  			if err := cli.cfg().LoadSimulation(); err != nil {
    36  				return err
    37  			}
    38  			if cli.cfg().Cscli.SimulationConfig == nil {
    39  				return fmt.Errorf("no simulation configured")
    40  			}
    41  
    42  			return nil
    43  		},
    44  		PersistentPostRun: func(cmd *cobra.Command, _ []string) {
    45  			if cmd.Name() != "status" {
    46  				log.Infof(ReloadMessage())
    47  			}
    48  		},
    49  	}
    50  	cmd.Flags().SortFlags = false
    51  	cmd.PersistentFlags().SortFlags = false
    52  
    53  	cmd.AddCommand(cli.NewEnableCmd())
    54  	cmd.AddCommand(cli.NewDisableCmd())
    55  	cmd.AddCommand(cli.NewStatusCmd())
    56  
    57  	return cmd
    58  }
    59  
    60  func (cli *cliSimulation) NewEnableCmd() *cobra.Command {
    61  	var forceGlobalSimulation bool
    62  
    63  	cmd := &cobra.Command{
    64  		Use:               "enable [scenario] [-global]",
    65  		Short:             "Enable the simulation, globally or on specified scenarios",
    66  		Example:           `cscli simulation enable`,
    67  		DisableAutoGenTag: true,
    68  		RunE: func(cmd *cobra.Command, args []string) error {
    69  			hub, err := require.Hub(cli.cfg(), nil, nil)
    70  			if err != nil {
    71  				return err
    72  			}
    73  
    74  			if len(args) > 0 {
    75  				for _, scenario := range args {
    76  					var item = hub.GetItem(cwhub.SCENARIOS, scenario)
    77  					if item == nil {
    78  						log.Errorf("'%s' doesn't exist or is not a scenario", scenario)
    79  						continue
    80  					}
    81  					if !item.State.Installed {
    82  						log.Warningf("'%s' isn't enabled", scenario)
    83  					}
    84  					isExcluded := slices.Contains(cli.cfg().Cscli.SimulationConfig.Exclusions, scenario)
    85  					if *cli.cfg().Cscli.SimulationConfig.Simulation && !isExcluded {
    86  						log.Warning("global simulation is already enabled")
    87  						continue
    88  					}
    89  					if !*cli.cfg().Cscli.SimulationConfig.Simulation && isExcluded {
    90  						log.Warningf("simulation for '%s' already enabled", scenario)
    91  						continue
    92  					}
    93  					if *cli.cfg().Cscli.SimulationConfig.Simulation && isExcluded {
    94  						cli.removeFromExclusion(scenario)
    95  						log.Printf("simulation enabled for '%s'", scenario)
    96  						continue
    97  					}
    98  					cli.addToExclusion(scenario)
    99  					log.Printf("simulation mode for '%s' enabled", scenario)
   100  				}
   101  				if err := cli.dumpSimulationFile(); err != nil {
   102  					return fmt.Errorf("simulation enable: %s", err)
   103  				}
   104  			} else if forceGlobalSimulation {
   105  				if err := cli.enableGlobalSimulation(); err != nil {
   106  					return fmt.Errorf("unable to enable global simulation mode: %s", err)
   107  				}
   108  			} else {
   109  				printHelp(cmd)
   110  			}
   111  
   112  			return nil
   113  		},
   114  	}
   115  	cmd.Flags().BoolVarP(&forceGlobalSimulation, "global", "g", false, "Enable global simulation (reverse mode)")
   116  
   117  	return cmd
   118  }
   119  
   120  func (cli *cliSimulation) NewDisableCmd() *cobra.Command {
   121  	var forceGlobalSimulation bool
   122  
   123  	cmd := &cobra.Command{
   124  		Use:               "disable [scenario]",
   125  		Short:             "Disable the simulation mode. Disable only specified scenarios",
   126  		Example:           `cscli simulation disable`,
   127  		DisableAutoGenTag: true,
   128  		RunE: func(cmd *cobra.Command, args []string) error {
   129  			if len(args) > 0 {
   130  				for _, scenario := range args {
   131  					isExcluded := slices.Contains(cli.cfg().Cscli.SimulationConfig.Exclusions, scenario)
   132  					if !*cli.cfg().Cscli.SimulationConfig.Simulation && !isExcluded {
   133  						log.Warningf("%s isn't in simulation mode", scenario)
   134  						continue
   135  					}
   136  					if !*cli.cfg().Cscli.SimulationConfig.Simulation && isExcluded {
   137  						cli.removeFromExclusion(scenario)
   138  						log.Printf("simulation mode for '%s' disabled", scenario)
   139  						continue
   140  					}
   141  					if isExcluded {
   142  						log.Warningf("simulation mode is enabled but is already disable for '%s'", scenario)
   143  						continue
   144  					}
   145  					cli.addToExclusion(scenario)
   146  					log.Printf("simulation mode for '%s' disabled", scenario)
   147  				}
   148  				if err := cli.dumpSimulationFile(); err != nil {
   149  					return fmt.Errorf("simulation disable: %s", err)
   150  				}
   151  			} else if forceGlobalSimulation {
   152  				if err := cli.disableGlobalSimulation(); err != nil {
   153  					return fmt.Errorf("unable to disable global simulation mode: %s", err)
   154  				}
   155  			} else {
   156  				printHelp(cmd)
   157  			}
   158  
   159  			return nil
   160  		},
   161  	}
   162  	cmd.Flags().BoolVarP(&forceGlobalSimulation, "global", "g", false, "Disable global simulation (reverse mode)")
   163  
   164  	return cmd
   165  }
   166  
   167  func (cli *cliSimulation) NewStatusCmd() *cobra.Command {
   168  	cmd := &cobra.Command{
   169  		Use:               "status",
   170  		Short:             "Show simulation mode status",
   171  		Example:           `cscli simulation status`,
   172  		DisableAutoGenTag: true,
   173  		Run: func(_ *cobra.Command, _ []string) {
   174  			cli.status()
   175  		},
   176  		PersistentPostRun: func(cmd *cobra.Command, args []string) {
   177  		},
   178  	}
   179  
   180  	return cmd
   181  }
   182  
   183  func (cli *cliSimulation) addToExclusion(name string) {
   184  	cfg := cli.cfg()
   185  	cfg.Cscli.SimulationConfig.Exclusions = append(cfg.Cscli.SimulationConfig.Exclusions, name)
   186  }
   187  
   188  func (cli *cliSimulation) removeFromExclusion(name string) {
   189  	cfg := cli.cfg()
   190  	index := slices.Index(cfg.Cscli.SimulationConfig.Exclusions, name)
   191  
   192  	// Remove element from the slice
   193  	cfg.Cscli.SimulationConfig.Exclusions[index] = cfg.Cscli.SimulationConfig.Exclusions[len(cfg.Cscli.SimulationConfig.Exclusions)-1]
   194  	cfg.Cscli.SimulationConfig.Exclusions[len(cfg.Cscli.SimulationConfig.Exclusions)-1] = ""
   195  	cfg.Cscli.SimulationConfig.Exclusions = cfg.Cscli.SimulationConfig.Exclusions[:len(cfg.Cscli.SimulationConfig.Exclusions)-1]
   196  }
   197  
   198  func (cli *cliSimulation) enableGlobalSimulation() error {
   199  	cfg := cli.cfg()
   200  	cfg.Cscli.SimulationConfig.Simulation = new(bool)
   201  	*cfg.Cscli.SimulationConfig.Simulation = true
   202  	cfg.Cscli.SimulationConfig.Exclusions = []string{}
   203  
   204  	if err := cli.dumpSimulationFile(); err != nil {
   205  		return fmt.Errorf("unable to dump simulation file: %s", err)
   206  	}
   207  
   208  	log.Printf("global simulation: enabled")
   209  
   210  	return nil
   211  }
   212  
   213  func (cli *cliSimulation) dumpSimulationFile() error {
   214  	cfg := cli.cfg()
   215  
   216  	newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
   217  	if err != nil {
   218  		return fmt.Errorf("unable to marshal simulation configuration: %s", err)
   219  	}
   220  
   221  	err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
   222  	if err != nil {
   223  		return fmt.Errorf("write simulation config in '%s' failed: %s", cfg.ConfigPaths.SimulationFilePath, err)
   224  	}
   225  
   226  	log.Debugf("updated simulation file %s", cfg.ConfigPaths.SimulationFilePath)
   227  
   228  	return nil
   229  }
   230  
   231  func (cli *cliSimulation) disableGlobalSimulation() error {
   232  	cfg := cli.cfg()
   233  	cfg.Cscli.SimulationConfig.Simulation = new(bool)
   234  	*cfg.Cscli.SimulationConfig.Simulation = false
   235  
   236  	cfg.Cscli.SimulationConfig.Exclusions = []string{}
   237  
   238  	newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
   239  	if err != nil {
   240  		return fmt.Errorf("unable to marshal new simulation configuration: %s", err)
   241  	}
   242  
   243  	err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
   244  	if err != nil {
   245  		return fmt.Errorf("unable to write new simulation config in '%s': %s", cfg.ConfigPaths.SimulationFilePath, err)
   246  	}
   247  
   248  	log.Printf("global simulation: disabled")
   249  
   250  	return nil
   251  }
   252  
   253  func (cli *cliSimulation) status() {
   254  	cfg := cli.cfg()
   255  	if cfg.Cscli.SimulationConfig == nil {
   256  		log.Printf("global simulation: disabled (configuration file is missing)")
   257  		return
   258  	}
   259  
   260  	if *cfg.Cscli.SimulationConfig.Simulation {
   261  		log.Println("global simulation: enabled")
   262  
   263  		if len(cfg.Cscli.SimulationConfig.Exclusions) > 0 {
   264  			log.Println("Scenarios not in simulation mode :")
   265  
   266  			for _, scenario := range cfg.Cscli.SimulationConfig.Exclusions {
   267  				log.Printf("  - %s", scenario)
   268  			}
   269  		}
   270  	} else {
   271  		log.Println("global simulation: disabled")
   272  		if len(cfg.Cscli.SimulationConfig.Exclusions) > 0 {
   273  			log.Println("Scenarios in simulation mode :")
   274  			for _, scenario := range cfg.Cscli.SimulationConfig.Exclusions {
   275  				log.Printf("  - %s", scenario)
   276  			}
   277  		}
   278  	}
   279  }