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 }