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 }