refact "cscli decisions" (#2804)
* refact "cscli decisions" * CI: relax mysql test timing * lint
This commit is contained in:
parent
f5fbe4a200
commit
4160bb8102
|
@ -176,7 +176,7 @@ cscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
|
||||||
flags.StringVar(&metabaseImage, "metabase-image", metabaseImage, "Metabase image to use")
|
flags.StringVar(&metabaseImage, "metabase-image", metabaseImage, "Metabase image to use")
|
||||||
flags.StringVarP(&metabaseListenPort, "port", "p", metabaseListenPort, "Listen port of container")
|
flags.StringVarP(&metabaseListenPort, "port", "p", metabaseListenPort, "Listen port of container")
|
||||||
flags.BoolVarP(&forceYes, "yes", "y", false, "force yes")
|
flags.BoolVarP(&forceYes, "yes", "y", false, "force yes")
|
||||||
//flags.StringVarP(&metabaseUser, "user", "u", "crowdsec@crowdsec.net", "metabase user")
|
// flags.StringVarP(&metabaseUser, "user", "u", "crowdsec@crowdsec.net", "metabase user")
|
||||||
flags.StringVar(&metabasePassword, "password", "", "metabase password")
|
flags.StringVar(&metabasePassword, "password", "", "metabase password")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -443,6 +443,7 @@ func checkGroups(forceYes *bool) (*user.Group, error) {
|
||||||
func (cli *cliDashboard) chownDatabase(gid string) error {
|
func (cli *cliDashboard) chownDatabase(gid string) error {
|
||||||
cfg := cli.cfg()
|
cfg := cli.cfg()
|
||||||
intID, err := strconv.Atoi(gid)
|
intID, err := strconv.Atoi(gid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to convert group ID to int: %s", err)
|
return fmt.Errorf("unable to convert group ID to int: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
|
|
||||||
var Client *apiclient.ApiClient
|
var Client *apiclient.ApiClient
|
||||||
|
|
||||||
func DecisionsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
|
func (cli *cliDecisions) decisionsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
|
||||||
/*here we cheat a bit : to make it more readable for the user, we dedup some entries*/
|
/*here we cheat a bit : to make it more readable for the user, we dedup some entries*/
|
||||||
spamLimit := make(map[string]bool)
|
spamLimit := make(map[string]bool)
|
||||||
skipped := 0
|
skipped := 0
|
||||||
|
@ -49,7 +49,8 @@ func DecisionsToTable(alerts *models.GetAlertsResponse, printMachine bool) error
|
||||||
alertItem.Decisions = newDecisions
|
alertItem.Decisions = newDecisions
|
||||||
}
|
}
|
||||||
|
|
||||||
if csConfig.Cscli.Output == "raw" {
|
switch cli.cfg().Cscli.Output {
|
||||||
|
case "raw":
|
||||||
csvwriter := csv.NewWriter(os.Stdout)
|
csvwriter := csv.NewWriter(os.Stdout)
|
||||||
header := []string{"id", "source", "ip", "reason", "action", "country", "as", "events_count", "expiration", "simulated", "alert_id"}
|
header := []string{"id", "source", "ip", "reason", "action", "country", "as", "events_count", "expiration", "simulated", "alert_id"}
|
||||||
|
|
||||||
|
@ -89,21 +90,24 @@ func DecisionsToTable(alerts *models.GetAlertsResponse, printMachine bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
csvwriter.Flush()
|
csvwriter.Flush()
|
||||||
} else if csConfig.Cscli.Output == "json" {
|
case "json":
|
||||||
if *alerts == nil {
|
if *alerts == nil {
|
||||||
// avoid returning "null" in `json"
|
// avoid returning "null" in `json"
|
||||||
// could be cleaner if we used slice of alerts directly
|
// could be cleaner if we used slice of alerts directly
|
||||||
fmt.Println("[]")
|
fmt.Println("[]")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
x, _ := json.MarshalIndent(alerts, "", " ")
|
x, _ := json.MarshalIndent(alerts, "", " ")
|
||||||
fmt.Printf("%s", string(x))
|
fmt.Printf("%s", string(x))
|
||||||
} else if csConfig.Cscli.Output == "human" {
|
case "human":
|
||||||
if len(*alerts) == 0 {
|
if len(*alerts) == 0 {
|
||||||
fmt.Println("No active decisions")
|
fmt.Println("No active decisions")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
decisionsTable(color.Output, alerts, printMachine)
|
|
||||||
|
cli.decisionsTable(color.Output, alerts, printMachine)
|
||||||
|
|
||||||
if skipped > 0 {
|
if skipped > 0 {
|
||||||
fmt.Printf("%d duplicated entries skipped\n", skipped)
|
fmt.Printf("%d duplicated entries skipped\n", skipped)
|
||||||
}
|
}
|
||||||
|
@ -113,13 +117,17 @@ func DecisionsToTable(alerts *models.GetAlertsResponse, printMachine bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type cliDecisions struct {}
|
type cliDecisions struct {
|
||||||
|
cfg configGetter
|
||||||
func NewCLIDecisions() *cliDecisions {
|
|
||||||
return &cliDecisions{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli cliDecisions) NewCommand() *cobra.Command {
|
func NewCLIDecisions(getconfig configGetter) *cliDecisions {
|
||||||
|
return &cliDecisions{
|
||||||
|
cfg: getconfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *cliDecisions) NewCommand() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "decisions [action]",
|
Use: "decisions [action]",
|
||||||
Short: "Manage decisions",
|
Short: "Manage decisions",
|
||||||
|
@ -130,16 +138,17 @@ func (cli cliDecisions) NewCommand() *cobra.Command {
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||||
if err := csConfig.LoadAPIClient(); err != nil {
|
cfg := cli.cfg()
|
||||||
|
if err := cfg.LoadAPIClient(); err != nil {
|
||||||
return fmt.Errorf("loading api client: %w", err)
|
return fmt.Errorf("loading api client: %w", err)
|
||||||
}
|
}
|
||||||
password := strfmt.Password(csConfig.API.Client.Credentials.Password)
|
password := strfmt.Password(cfg.API.Client.Credentials.Password)
|
||||||
apiurl, err := url.Parse(csConfig.API.Client.Credentials.URL)
|
apiurl, err := url.Parse(cfg.API.Client.Credentials.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("parsing api url %s: %w", csConfig.API.Client.Credentials.URL, err)
|
return fmt.Errorf("parsing api url %s: %w", cfg.API.Client.Credentials.URL, err)
|
||||||
}
|
}
|
||||||
Client, err = apiclient.NewClient(&apiclient.Config{
|
Client, err = apiclient.NewClient(&apiclient.Config{
|
||||||
MachineID: csConfig.API.Client.Credentials.Login,
|
MachineID: cfg.API.Client.Credentials.Login,
|
||||||
Password: password,
|
Password: password,
|
||||||
UserAgent: fmt.Sprintf("crowdsec/%s", version.String()),
|
UserAgent: fmt.Sprintf("crowdsec/%s", version.String()),
|
||||||
URL: apiurl,
|
URL: apiurl,
|
||||||
|
@ -152,15 +161,15 @@ func (cli cliDecisions) NewCommand() *cobra.Command {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(cli.NewListCmd())
|
cmd.AddCommand(cli.newListCmd())
|
||||||
cmd.AddCommand(cli.NewAddCmd())
|
cmd.AddCommand(cli.newAddCmd())
|
||||||
cmd.AddCommand(cli.NewDeleteCmd())
|
cmd.AddCommand(cli.newDeleteCmd())
|
||||||
cmd.AddCommand(cli.NewImportCmd())
|
cmd.AddCommand(cli.newImportCmd())
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli cliDecisions) NewListCmd() *cobra.Command {
|
func (cli *cliDecisions) newListCmd() *cobra.Command {
|
||||||
var filter = apiclient.AlertsListOpts{
|
var filter = apiclient.AlertsListOpts{
|
||||||
ValueEquals: new(string),
|
ValueEquals: new(string),
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: new(string),
|
||||||
|
@ -262,7 +271,7 @@ cscli decisions list -t ban
|
||||||
return fmt.Errorf("unable to retrieve decisions: %w", err)
|
return fmt.Errorf("unable to retrieve decisions: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = DecisionsToTable(alerts, printMachine)
|
err = cli.decisionsToTable(alerts, printMachine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to print decisions: %w", err)
|
return fmt.Errorf("unable to print decisions: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -289,7 +298,7 @@ cscli decisions list -t ban
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli cliDecisions) NewAddCmd() *cobra.Command {
|
func (cli *cliDecisions) newAddCmd() *cobra.Command {
|
||||||
var (
|
var (
|
||||||
addIP string
|
addIP string
|
||||||
addRange string
|
addRange string
|
||||||
|
@ -325,7 +334,7 @@ cscli decisions add --scope username --value foobar
|
||||||
createdAt := time.Now().UTC().Format(time.RFC3339)
|
createdAt := time.Now().UTC().Format(time.RFC3339)
|
||||||
|
|
||||||
/*take care of shorthand options*/
|
/*take care of shorthand options*/
|
||||||
if err := manageCliDecisionAlerts(&addIP, &addRange, &addScope, &addValue); err != nil {
|
if err = manageCliDecisionAlerts(&addIP, &addRange, &addScope, &addValue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +350,7 @@ cscli decisions add --scope username --value foobar
|
||||||
}
|
}
|
||||||
|
|
||||||
if addReason == "" {
|
if addReason == "" {
|
||||||
addReason = fmt.Sprintf("manual '%s' from '%s'", addType, csConfig.API.Client.Credentials.Login)
|
addReason = fmt.Sprintf("manual '%s' from '%s'", addType, cli.cfg().API.Client.Credentials.Login)
|
||||||
}
|
}
|
||||||
decision := models.Decision{
|
decision := models.Decision{
|
||||||
Duration: &addDuration,
|
Duration: &addDuration,
|
||||||
|
@ -400,7 +409,7 @@ cscli decisions add --scope username --value foobar
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli cliDecisions) NewDeleteCmd() *cobra.Command {
|
func (cli *cliDecisions) newDeleteCmd() *cobra.Command {
|
||||||
var delFilter = apiclient.DecisionsDeleteOpts{
|
var delFilter = apiclient.DecisionsDeleteOpts{
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: new(string),
|
||||||
ValueEquals: new(string),
|
ValueEquals: new(string),
|
||||||
|
|
|
@ -67,7 +67,7 @@ func parseDecisionList(content []byte, format string) ([]decisionRaw, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (cli cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
func (cli *cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
|
||||||
input, err := flags.GetString("input")
|
input, err := flags.GetString("input")
|
||||||
|
@ -236,7 +236,7 @@ func (cli cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (cli cliDecisions) NewImportCmd() *cobra.Command {
|
func (cli *cliDecisions) newImportCmd() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "import [options]",
|
Use: "import [options]",
|
||||||
Short: "Import decisions from a file or pipe",
|
Short: "Import decisions from a file or pipe",
|
||||||
|
|
|
@ -8,13 +8,15 @@ import (
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func decisionsTable(out io.Writer, alerts *models.GetAlertsResponse, printMachine bool) {
|
func (cli *cliDecisions) decisionsTable(out io.Writer, alerts *models.GetAlertsResponse, printMachine bool) {
|
||||||
t := newTable(out)
|
t := newTable(out)
|
||||||
t.SetRowLines(false)
|
t.SetRowLines(false)
|
||||||
|
|
||||||
header := []string{"ID", "Source", "Scope:Value", "Reason", "Action", "Country", "AS", "Events", "expiration", "Alert ID"}
|
header := []string{"ID", "Source", "Scope:Value", "Reason", "Action", "Country", "AS", "Events", "expiration", "Alert ID"}
|
||||||
if printMachine {
|
if printMachine {
|
||||||
header = append(header, "Machine")
|
header = append(header, "Machine")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.SetHeaders(header...)
|
t.SetHeaders(header...)
|
||||||
|
|
||||||
for _, alertItem := range *alerts {
|
for _, alertItem := range *alerts {
|
||||||
|
@ -22,6 +24,7 @@ func decisionsTable(out io.Writer, alerts *models.GetAlertsResponse, printMachin
|
||||||
if *alertItem.Simulated {
|
if *alertItem.Simulated {
|
||||||
*decisionItem.Type = fmt.Sprintf("(simul)%s", *decisionItem.Type)
|
*decisionItem.Type = fmt.Sprintf("(simul)%s", *decisionItem.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
row := []string{
|
row := []string{
|
||||||
strconv.Itoa(int(decisionItem.ID)),
|
strconv.Itoa(int(decisionItem.ID)),
|
||||||
*decisionItem.Origin,
|
*decisionItem.Origin,
|
||||||
|
@ -42,5 +45,6 @@ func decisionsTable(out io.Writer, alerts *models.GetAlertsResponse, printMachin
|
||||||
t.AddRow(row...)
|
t.AddRow(row...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Render()
|
t.Render()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ func (p *MachinePassword) Set(v string) error {
|
||||||
if len(v) > 72 {
|
if len(v) > 72 {
|
||||||
return errors.New("password too long (max 72 characters)")
|
return errors.New("password too long (max 72 characters)")
|
||||||
}
|
}
|
||||||
|
|
||||||
*p = MachinePassword(v)
|
*p = MachinePassword(v)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -45,6 +45,7 @@ func generatePassword(length int) string {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed getting data from prng for password generation : %s", err)
|
log.Fatalf("failed getting data from prng for password generation : %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[i] = charset[rInt.Int64()]
|
buf[i] = charset[rInt.Int64()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,12 +60,14 @@ func generateIDPrefix() (string, error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return prefix, nil
|
return prefix, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("failed to get machine-id with usual files: %s", err)
|
log.Debugf("failed to get machine-id with usual files: %s", err)
|
||||||
|
|
||||||
bID, err := uuid.NewRandom()
|
bID, err := uuid.NewRandom()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return bID.String(), nil
|
return bID.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("generating machine id: %w", err)
|
return "", fmt.Errorf("generating machine id: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +78,14 @@ func generateID(prefix string) (string, error) {
|
||||||
if prefix == "" {
|
if prefix == "" {
|
||||||
prefix, err = generateIDPrefix()
|
prefix, err = generateIDPrefix()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix = strings.ReplaceAll(prefix, "-", "")[:32]
|
prefix = strings.ReplaceAll(prefix, "-", "")[:32]
|
||||||
suffix := generatePassword(16)
|
suffix := generatePassword(16)
|
||||||
|
|
||||||
return prefix + suffix, nil
|
return prefix + suffix, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,6 +295,7 @@ func (cli *cliMachines) add(args []string, machinePassword string, dumpFile stri
|
||||||
if !autoAdd {
|
if !autoAdd {
|
||||||
return fmt.Errorf("please specify a password with --password or use --auto")
|
return fmt.Errorf("please specify a password with --password or use --auto")
|
||||||
}
|
}
|
||||||
|
|
||||||
machinePassword = generatePassword(passwordLength)
|
machinePassword = generatePassword(passwordLength)
|
||||||
} else if machinePassword == "" && interactive {
|
} else if machinePassword == "" && interactive {
|
||||||
qs := &survey.Password{
|
qs := &survey.Password{
|
||||||
|
@ -328,10 +335,10 @@ func (cli *cliMachines) add(args []string, machinePassword string, dumpFile stri
|
||||||
}
|
}
|
||||||
|
|
||||||
if dumpFile != "" && dumpFile != "-" {
|
if dumpFile != "" && dumpFile != "-" {
|
||||||
err = os.WriteFile(dumpFile, apiConfigDump, 0o600)
|
if err = os.WriteFile(dumpFile, apiConfigDump, 0o600); err != nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err)
|
return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "API credentials written to '%s'.\n", dumpFile)
|
fmt.Fprintf(os.Stderr, "API credentials written to '%s'.\n", dumpFile)
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(string(apiConfigDump))
|
fmt.Print(string(apiConfigDump))
|
||||||
|
@ -359,11 +366,11 @@ func (cli *cliMachines) deleteValid(cmd *cobra.Command, args []string, toComplet
|
||||||
|
|
||||||
func (cli *cliMachines) delete(machines []string) error {
|
func (cli *cliMachines) delete(machines []string) error {
|
||||||
for _, machineID := range machines {
|
for _, machineID := range machines {
|
||||||
err := cli.db.DeleteWatcher(machineID)
|
if err := cli.db.DeleteWatcher(machineID); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to delete machine '%s': %s", machineID, err)
|
log.Errorf("unable to delete machine '%s': %s", machineID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("machine '%s' deleted successfully", machineID)
|
log.Infof("machine '%s' deleted successfully", machineID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,6 +480,7 @@ func (cli *cliMachines) validate(machineID string) error {
|
||||||
if err := cli.db.ValidateMachine(machineID); err != nil {
|
if err := cli.db.ValidateMachine(machineID); err != nil {
|
||||||
return fmt.Errorf("unable to validate machine '%s': %s", machineID, err)
|
return fmt.Errorf("unable to validate machine '%s': %s", machineID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("machine '%s' validated successfully", machineID)
|
log.Infof("machine '%s' validated successfully", machineID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -157,8 +157,8 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
||||||
cmd.PersistentFlags().BoolVar(&wrn_lvl, "warning", false, "Set logging to warning")
|
cmd.PersistentFlags().BoolVar(&wrn_lvl, "warning", false, "Set logging to warning")
|
||||||
cmd.PersistentFlags().BoolVar(&err_lvl, "error", false, "Set logging to error")
|
cmd.PersistentFlags().BoolVar(&err_lvl, "error", false, "Set logging to error")
|
||||||
cmd.PersistentFlags().BoolVar(&trace_lvl, "trace", false, "Set logging to trace")
|
cmd.PersistentFlags().BoolVar(&trace_lvl, "trace", false, "Set logging to trace")
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVar(&flagBranch, "branch", "", "Override hub branch on github")
|
cmd.PersistentFlags().StringVar(&flagBranch, "branch", "", "Override hub branch on github")
|
||||||
|
|
||||||
if err := cmd.PersistentFlags().MarkHidden("branch"); err != nil {
|
if err := cmd.PersistentFlags().MarkHidden("branch"); err != nil {
|
||||||
log.Fatalf("failed to hide flag: %s", err)
|
log.Fatalf("failed to hide flag: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
|
||||||
cmd.AddCommand(NewCLIHub(getconfig).NewCommand())
|
cmd.AddCommand(NewCLIHub(getconfig).NewCommand())
|
||||||
cmd.AddCommand(NewMetricsCmd())
|
cmd.AddCommand(NewMetricsCmd())
|
||||||
cmd.AddCommand(NewCLIDashboard(getconfig).NewCommand())
|
cmd.AddCommand(NewCLIDashboard(getconfig).NewCommand())
|
||||||
cmd.AddCommand(NewCLIDecisions().NewCommand())
|
cmd.AddCommand(NewCLIDecisions(getconfig).NewCommand())
|
||||||
cmd.AddCommand(NewCLIAlerts().NewCommand())
|
cmd.AddCommand(NewCLIAlerts().NewCommand())
|
||||||
cmd.AddCommand(NewCLISimulation(getconfig).NewCommand())
|
cmd.AddCommand(NewCLISimulation(getconfig).NewCommand())
|
||||||
cmd.AddCommand(NewCLIBouncers(getconfig).NewCommand())
|
cmd.AddCommand(NewCLIBouncers(getconfig).NewCommand())
|
||||||
|
|
|
@ -32,7 +32,7 @@ func (cli *cliPapi) NewCommand() *cobra.Command {
|
||||||
Short: "Manage interaction with Polling API (PAPI)",
|
Short: "Manage interaction with Polling API (PAPI)",
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||||
cfg := cli.cfg()
|
cfg := cli.cfg()
|
||||||
if err := require.LAPI(cfg); err != nil {
|
if err := require.LAPI(cfg); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -59,7 +59,7 @@ func (cli *cliPapi) NewStatusCmd() *cobra.Command {
|
||||||
Short: "Get status of the Polling API",
|
Short: "Get status of the Polling API",
|
||||||
Args: cobra.MinimumNArgs(0),
|
Args: cobra.MinimumNArgs(0),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
var err error
|
var err error
|
||||||
cfg := cli.cfg()
|
cfg := cli.cfg()
|
||||||
dbClient, err = database.NewClient(cfg.DbConfig)
|
dbClient, err = database.NewClient(cfg.DbConfig)
|
||||||
|
@ -111,7 +111,7 @@ func (cli *cliPapi) NewSyncCmd() *cobra.Command {
|
||||||
Short: "Sync with the Polling API, pulling all non-expired orders for the instance",
|
Short: "Sync with the Polling API, pulling all non-expired orders for the instance",
|
||||||
Args: cobra.MinimumNArgs(0),
|
Args: cobra.MinimumNArgs(0),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
var err error
|
var err error
|
||||||
cfg := cli.cfg()
|
cfg := cli.cfg()
|
||||||
t := tomb.Tomb{}
|
t := tomb.Tomb{}
|
||||||
|
|
|
@ -211,14 +211,17 @@ func (cli *cliSimulation) enableGlobalSimulation() error {
|
||||||
|
|
||||||
func (cli *cliSimulation) dumpSimulationFile() error {
|
func (cli *cliSimulation) dumpSimulationFile() error {
|
||||||
cfg := cli.cfg()
|
cfg := cli.cfg()
|
||||||
|
|
||||||
newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
|
newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to marshal simulation configuration: %s", err)
|
return fmt.Errorf("unable to marshal simulation configuration: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("write simulation config in '%s' failed: %s", cfg.ConfigPaths.SimulationFilePath, err)
|
return fmt.Errorf("write simulation config in '%s' failed: %s", cfg.ConfigPaths.SimulationFilePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("updated simulation file %s", cfg.ConfigPaths.SimulationFilePath)
|
log.Debugf("updated simulation file %s", cfg.ConfigPaths.SimulationFilePath)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -230,16 +233,19 @@ func (cli *cliSimulation) disableGlobalSimulation() error {
|
||||||
*cfg.Cscli.SimulationConfig.Simulation = false
|
*cfg.Cscli.SimulationConfig.Simulation = false
|
||||||
|
|
||||||
cfg.Cscli.SimulationConfig.Exclusions = []string{}
|
cfg.Cscli.SimulationConfig.Exclusions = []string{}
|
||||||
|
|
||||||
newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
|
newConfigSim, err := yaml.Marshal(cfg.Cscli.SimulationConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to marshal new simulation configuration: %s", err)
|
return fmt.Errorf("unable to marshal new simulation configuration: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
err = os.WriteFile(cfg.ConfigPaths.SimulationFilePath, newConfigSim, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to write new simulation config in '%s' : %s", cfg.ConfigPaths.SimulationFilePath, err)
|
return fmt.Errorf("unable to write new simulation config in '%s': %s", cfg.ConfigPaths.SimulationFilePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("global simulation: disabled")
|
log.Printf("global simulation: disabled")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,10 +255,13 @@ func (cli *cliSimulation) status() {
|
||||||
log.Printf("global simulation: disabled (configuration file is missing)")
|
log.Printf("global simulation: disabled (configuration file is missing)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if *cfg.Cscli.SimulationConfig.Simulation {
|
if *cfg.Cscli.SimulationConfig.Simulation {
|
||||||
log.Println("global simulation: enabled")
|
log.Println("global simulation: enabled")
|
||||||
|
|
||||||
if len(cfg.Cscli.SimulationConfig.Exclusions) > 0 {
|
if len(cfg.Cscli.SimulationConfig.Exclusions) > 0 {
|
||||||
log.Println("Scenarios not in simulation mode :")
|
log.Println("Scenarios not in simulation mode :")
|
||||||
|
|
||||||
for _, scenario := range cfg.Cscli.SimulationConfig.Exclusions {
|
for _, scenario := range cfg.Cscli.SimulationConfig.Exclusions {
|
||||||
log.Printf(" - %s", scenario)
|
log.Printf(" - %s", scenario)
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,9 +76,10 @@ func collectMetrics() ([]byte, []byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("could not create requests to prometheus endpoint: %s", err)
|
return nil, nil, fmt.Errorf("could not create requests to prometheus endpoint: %s", err)
|
||||||
}
|
}
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("could not get metrics from prometheus endpoint: %s", err)
|
return nil, nil, fmt.Errorf("could not get metrics from prometheus endpoint: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -100,17 +101,20 @@ func collectVersion() []byte {
|
||||||
|
|
||||||
func collectFeatures() []byte {
|
func collectFeatures() []byte {
|
||||||
log.Info("Collecting feature flags")
|
log.Info("Collecting feature flags")
|
||||||
|
|
||||||
enabledFeatures := fflag.Crowdsec.GetEnabledFeatures()
|
enabledFeatures := fflag.Crowdsec.GetEnabledFeatures()
|
||||||
|
|
||||||
w := bytes.NewBuffer(nil)
|
w := bytes.NewBuffer(nil)
|
||||||
for _, k := range enabledFeatures {
|
for _, k := range enabledFeatures {
|
||||||
fmt.Fprintf(w, "%s\n", k)
|
fmt.Fprintf(w, "%s\n", k)
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.Bytes()
|
return w.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectOSInfo() ([]byte, error) {
|
func collectOSInfo() ([]byte, error) {
|
||||||
log.Info("Collecting OS info")
|
log.Info("Collecting OS info")
|
||||||
|
|
||||||
info, err := osinfo.GetOSInfo()
|
info, err := osinfo.GetOSInfo()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -133,6 +137,7 @@ func collectHubItems(hub *cwhub.Hub, itemType string) []byte {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
log.Infof("Collecting %s list", itemType)
|
log.Infof("Collecting %s list", itemType)
|
||||||
|
|
||||||
items := make(map[string][]*cwhub.Item)
|
items := make(map[string][]*cwhub.Item)
|
||||||
|
@ -144,26 +149,33 @@ func collectHubItems(hub *cwhub.Hub, itemType string) []byte {
|
||||||
if err := listItems(out, []string{itemType}, items, false); err != nil {
|
if err := listItems(out, []string{itemType}, items, false); err != nil {
|
||||||
log.Warnf("could not collect %s list: %s", itemType, err)
|
log.Warnf("could not collect %s list: %s", itemType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.Bytes()
|
return out.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectBouncers(dbClient *database.Client) ([]byte, error) {
|
func collectBouncers(dbClient *database.Client) ([]byte, error) {
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
bouncers, err := dbClient.ListBouncers()
|
bouncers, err := dbClient.ListBouncers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to list bouncers: %s", err)
|
return nil, fmt.Errorf("unable to list bouncers: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
getBouncersTable(out, bouncers)
|
getBouncersTable(out, bouncers)
|
||||||
|
|
||||||
return out.Bytes(), nil
|
return out.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectAgents(dbClient *database.Client) ([]byte, error) {
|
func collectAgents(dbClient *database.Client) ([]byte, error) {
|
||||||
out := bytes.NewBuffer(nil)
|
out := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
machines, err := dbClient.ListMachines()
|
machines, err := dbClient.ListMachines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to list machines: %s", err)
|
return nil, fmt.Errorf("unable to list machines: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
getAgentsTable(out, machines)
|
getAgentsTable(out, machines)
|
||||||
|
|
||||||
return out.Bytes(), nil
|
return out.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,12 +183,14 @@ func collectAPIStatus(login string, password string, endpoint string, prefix str
|
||||||
if csConfig.API.Client == nil || csConfig.API.Client.Credentials == nil {
|
if csConfig.API.Client == nil || csConfig.API.Client.Credentials == nil {
|
||||||
return []byte("No agent credentials found, are we LAPI ?")
|
return []byte("No agent credentials found, are we LAPI ?")
|
||||||
}
|
}
|
||||||
pwd := strfmt.Password(password)
|
|
||||||
apiurl, err := url.Parse(endpoint)
|
|
||||||
|
|
||||||
|
pwd := strfmt.Password(password)
|
||||||
|
|
||||||
|
apiurl, err := url.Parse(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte(fmt.Sprintf("cannot parse API URL: %s", err))
|
return []byte(fmt.Sprintf("cannot parse API URL: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios, err := hub.GetInstalledItemNames(cwhub.SCENARIOS)
|
scenarios, err := hub.GetInstalledItemNames(cwhub.SCENARIOS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte(fmt.Sprintf("could not collect scenarios: %s", err))
|
return []byte(fmt.Sprintf("could not collect scenarios: %s", err))
|
||||||
|
@ -189,6 +203,7 @@ func collectAPIStatus(login string, password string, endpoint string, prefix str
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte(fmt.Sprintf("could not init client: %s", err))
|
return []byte(fmt.Sprintf("could not init client: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
t := models.WatcherAuthRequest{
|
t := models.WatcherAuthRequest{
|
||||||
MachineID: &login,
|
MachineID: &login,
|
||||||
Password: &pwd,
|
Password: &pwd,
|
||||||
|
@ -205,6 +220,7 @@ func collectAPIStatus(login string, password string, endpoint string, prefix str
|
||||||
|
|
||||||
func collectCrowdsecConfig() []byte {
|
func collectCrowdsecConfig() []byte {
|
||||||
log.Info("Collecting crowdsec config")
|
log.Info("Collecting crowdsec config")
|
||||||
|
|
||||||
config, err := os.ReadFile(*csConfig.FilePath)
|
config, err := os.ReadFile(*csConfig.FilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte(fmt.Sprintf("could not read config file: %s", err))
|
return []byte(fmt.Sprintf("could not read config file: %s", err))
|
||||||
|
@ -217,15 +233,18 @@ func collectCrowdsecConfig() []byte {
|
||||||
|
|
||||||
func collectCrowdsecProfile() []byte {
|
func collectCrowdsecProfile() []byte {
|
||||||
log.Info("Collecting crowdsec profile")
|
log.Info("Collecting crowdsec profile")
|
||||||
|
|
||||||
config, err := os.ReadFile(csConfig.API.Server.ProfilesPath)
|
config, err := os.ReadFile(csConfig.API.Server.ProfilesPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte(fmt.Sprintf("could not read profile file: %s", err))
|
return []byte(fmt.Sprintf("could not read profile file: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectAcquisitionConfig() map[string][]byte {
|
func collectAcquisitionConfig() map[string][]byte {
|
||||||
log.Info("Collecting acquisition config")
|
log.Info("Collecting acquisition config")
|
||||||
|
|
||||||
ret := make(map[string][]byte)
|
ret := make(map[string][]byte)
|
||||||
|
|
||||||
for _, filename := range csConfig.Crowdsec.AcquisitionFiles {
|
for _, filename := range csConfig.Crowdsec.AcquisitionFiles {
|
||||||
|
@ -287,7 +306,7 @@ cscli support dump -f /tmp/crowdsec-support.zip
|
||||||
`,
|
`,
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(_ *cobra.Command, _ []string) {
|
||||||
var err error
|
var err error
|
||||||
var skipHub, skipDB, skipCAPI, skipLAPI, skipAgent bool
|
var skipHub, skipDB, skipCAPI, skipLAPI, skipAgent bool
|
||||||
infos := map[string][]byte{
|
infos := map[string][]byte{
|
||||||
|
@ -307,13 +326,13 @@ cscli support dump -f /tmp/crowdsec-support.zip
|
||||||
infos[SUPPORT_AGENTS_PATH] = []byte(err.Error())
|
infos[SUPPORT_AGENTS_PATH] = []byte(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := csConfig.LoadAPIServer(true); err != nil {
|
if err = csConfig.LoadAPIServer(true); err != nil {
|
||||||
log.Warnf("could not load LAPI, skipping CAPI check")
|
log.Warnf("could not load LAPI, skipping CAPI check")
|
||||||
skipLAPI = true
|
skipLAPI = true
|
||||||
infos[SUPPORT_CAPI_STATUS_PATH] = []byte(err.Error())
|
infos[SUPPORT_CAPI_STATUS_PATH] = []byte(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := csConfig.LoadCrowdsec(); err != nil {
|
if err = csConfig.LoadCrowdsec(); err != nil {
|
||||||
log.Warnf("could not load agent config, skipping crowdsec config check")
|
log.Warnf("could not load agent config, skipping crowdsec config check")
|
||||||
skipAgent = true
|
skipAgent = true
|
||||||
}
|
}
|
||||||
|
@ -399,7 +418,6 @@ cscli support dump -f /tmp/crowdsec-support.zip
|
||||||
}
|
}
|
||||||
|
|
||||||
if !skipAgent {
|
if !skipAgent {
|
||||||
|
|
||||||
acquis := collectAcquisitionConfig()
|
acquis := collectAcquisitionConfig()
|
||||||
|
|
||||||
for filename, content := range acquis {
|
for filename, content := range acquis {
|
||||||
|
|
|
@ -25,6 +25,7 @@ func manageCliDecisionAlerts(ip *string, ipRange *string, scope *string, value *
|
||||||
return fmt.Errorf("%s isn't a valid range", *ipRange)
|
return fmt.Errorf("%s isn't a valid range", *ipRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ip != "" {
|
if *ip != "" {
|
||||||
ipRepr := net.ParseIP(*ip)
|
ipRepr := net.ParseIP(*ip)
|
||||||
if ipRepr == nil {
|
if ipRepr == nil {
|
||||||
|
@ -32,7 +33,7 @@ func manageCliDecisionAlerts(ip *string, ipRange *string, scope *string, value *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//avoid confusion on scope (ip vs Ip and range vs Range)
|
// avoid confusion on scope (ip vs Ip and range vs Range)
|
||||||
switch strings.ToLower(*scope) {
|
switch strings.ToLower(*scope) {
|
||||||
case "ip":
|
case "ip":
|
||||||
*scope = types.Ip
|
*scope = types.Ip
|
||||||
|
@ -43,6 +44,7 @@ func manageCliDecisionAlerts(ip *string, ipRange *string, scope *string, value *
|
||||||
case "as":
|
case "as":
|
||||||
*scope = types.AS
|
*scope = types.AS
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,7 @@ func (l *LocalApiClientCfg) Load() error {
|
||||||
|
|
||||||
func (lapiCfg *LocalApiServerCfg) GetTrustedIPs() ([]net.IPNet, error) {
|
func (lapiCfg *LocalApiServerCfg) GetTrustedIPs() ([]net.IPNet, error) {
|
||||||
trustedIPs := make([]net.IPNet, 0)
|
trustedIPs := make([]net.IPNet, 0)
|
||||||
|
|
||||||
for _, ip := range lapiCfg.TrustedIPs {
|
for _, ip := range lapiCfg.TrustedIPs {
|
||||||
cidr := toValidCIDR(ip)
|
cidr := toValidCIDR(ip)
|
||||||
|
|
||||||
|
@ -265,7 +266,7 @@ func (c *Config) LoadAPIServer(inCli bool) error {
|
||||||
return fmt.Errorf("no listen_uri specified")
|
return fmt.Errorf("no listen_uri specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
//inherit log level from common, then api->server
|
// inherit log level from common, then api->server
|
||||||
var logLevel log.Level
|
var logLevel log.Level
|
||||||
if c.API.Server.LogLevel != nil {
|
if c.API.Server.LogLevel != nil {
|
||||||
logLevel = *c.API.Server.LogLevel
|
logLevel = *c.API.Server.LogLevel
|
||||||
|
|
|
@ -25,7 +25,7 @@ var globalConfig = Config{}
|
||||||
|
|
||||||
// Config contains top-level defaults -> overridden by configuration file -> overridden by CLI flags
|
// Config contains top-level defaults -> overridden by configuration file -> overridden by CLI flags
|
||||||
type Config struct {
|
type Config struct {
|
||||||
//just a path to ourselves :p
|
// just a path to ourselves :p
|
||||||
FilePath *string `yaml:"-"`
|
FilePath *string `yaml:"-"`
|
||||||
Self []byte `yaml:"-"`
|
Self []byte `yaml:"-"`
|
||||||
Common *CommonCfg `yaml:"common,omitempty"`
|
Common *CommonCfg `yaml:"common,omitempty"`
|
||||||
|
@ -44,10 +44,12 @@ type Config struct {
|
||||||
func NewConfig(configFile string, disableAgent bool, disableAPI bool, inCli bool) (*Config, string, error) {
|
func NewConfig(configFile string, disableAgent bool, disableAPI bool, inCli bool) (*Config, string, error) {
|
||||||
patcher := yamlpatch.NewPatcher(configFile, ".local")
|
patcher := yamlpatch.NewPatcher(configFile, ".local")
|
||||||
patcher.SetQuiet(inCli)
|
patcher.SetQuiet(inCli)
|
||||||
|
|
||||||
fcontent, err := patcher.MergedPatchContent()
|
fcontent, err := patcher.MergedPatchContent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
configData := csstring.StrictExpand(string(fcontent), os.LookupEnv)
|
configData := csstring.StrictExpand(string(fcontent), os.LookupEnv)
|
||||||
cfg := Config{
|
cfg := Config{
|
||||||
FilePath: &configFile,
|
FilePath: &configFile,
|
||||||
|
|
|
@ -19,7 +19,7 @@ teardown_file() {
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
load "../lib/setup.sh"
|
load "../lib/setup.sh"
|
||||||
if is_db_mysql; then sleep 0.3; fi
|
if is_db_mysql; then sleep 0.5; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
api() {
|
api() {
|
||||||
|
|
Loading…
Reference in a new issue