Merge branch 'master' into docker_auto_discovery
This commit is contained in:
commit
2874eb51d3
1
.github/workflows/bats-sqlite-coverage.yml
vendored
1
.github/workflows/bats-sqlite-coverage.yml
vendored
|
@ -81,3 +81,4 @@ jobs:
|
||||||
with:
|
with:
|
||||||
files: ./coverage-bats.out
|
files: ./coverage-bats.out
|
||||||
flags: bats
|
flags: bats
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
1
.github/workflows/go-tests-windows.yml
vendored
1
.github/workflows/go-tests-windows.yml
vendored
|
@ -52,6 +52,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
files: coverage.out
|
files: coverage.out
|
||||||
flags: unit-windows
|
flags: unit-windows
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v4
|
||||||
|
|
1
.github/workflows/go-tests.yml
vendored
1
.github/workflows/go-tests.yml
vendored
|
@ -153,6 +153,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
files: coverage.out
|
files: coverage.out
|
||||||
flags: unit-linux
|
flags: unit-linux
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v4
|
||||||
|
|
|
@ -58,7 +58,7 @@ linters-settings:
|
||||||
min-complexity: 28
|
min-complexity: 28
|
||||||
|
|
||||||
nlreturn:
|
nlreturn:
|
||||||
block-size: 4
|
block-size: 5
|
||||||
|
|
||||||
nolintlint:
|
nolintlint:
|
||||||
allow-unused: false # report any unused nolint directives
|
allow-unused: false # report any unused nolint directives
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -204,6 +205,7 @@ func (cli *cliAlerts) NewCommand() *cobra.Command {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("parsing api url %s: %w", apiURL, err)
|
return fmt.Errorf("parsing api url %s: %w", apiURL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cli.client, err = apiclient.NewClient(&apiclient.Config{
|
cli.client, err = apiclient.NewClient(&apiclient.Config{
|
||||||
MachineID: cfg.API.Client.Credentials.Login,
|
MachineID: cfg.API.Client.Credentials.Login,
|
||||||
Password: strfmt.Password(cfg.API.Client.Credentials.Password),
|
Password: strfmt.Password(cfg.API.Client.Credentials.Password),
|
||||||
|
@ -211,7 +213,6 @@ func (cli *cliAlerts) NewCommand() *cobra.Command {
|
||||||
URL: apiURL,
|
URL: apiURL,
|
||||||
VersionPrefix: "v1",
|
VersionPrefix: "v1",
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("new api client: %w", err)
|
return fmt.Errorf("new api client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -229,7 +230,7 @@ func (cli *cliAlerts) NewCommand() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *cliAlerts) NewListCmd() *cobra.Command {
|
func (cli *cliAlerts) NewListCmd() *cobra.Command {
|
||||||
var alertListFilter = apiclient.AlertsListOpts{
|
alertListFilter := apiclient.AlertsListOpts{
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: new(string),
|
||||||
ValueEquals: new(string),
|
ValueEquals: new(string),
|
||||||
ScenarioEquals: new(string),
|
ScenarioEquals: new(string),
|
||||||
|
@ -363,7 +364,7 @@ func (cli *cliAlerts) NewDeleteCmd() *cobra.Command {
|
||||||
delAlertByID string
|
delAlertByID string
|
||||||
)
|
)
|
||||||
|
|
||||||
var alertDeleteFilter = apiclient.AlertsDeleteOpts{
|
alertDeleteFilter := apiclient.AlertsDeleteOpts{
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: new(string),
|
||||||
ValueEquals: new(string),
|
ValueEquals: new(string),
|
||||||
ScenarioEquals: new(string),
|
ScenarioEquals: new(string),
|
||||||
|
@ -391,7 +392,7 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
|
||||||
*alertDeleteFilter.ScenarioEquals == "" && *alertDeleteFilter.IPEquals == "" &&
|
*alertDeleteFilter.ScenarioEquals == "" && *alertDeleteFilter.IPEquals == "" &&
|
||||||
*alertDeleteFilter.RangeEquals == "" && delAlertByID == "" {
|
*alertDeleteFilter.RangeEquals == "" && delAlertByID == "" {
|
||||||
_ = cmd.Usage()
|
_ = cmd.Usage()
|
||||||
return fmt.Errorf("at least one filter or --all must be specified")
|
return errors.New("at least one filter or --all must be specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -478,7 +479,7 @@ func (cli *cliAlerts) NewInspectCmd() *cobra.Command {
|
||||||
cfg := cli.cfg()
|
cfg := cli.cfg()
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
printHelp(cmd)
|
printHelp(cmd)
|
||||||
return fmt.Errorf("missing alert_id")
|
return errors.New("missing alert_id")
|
||||||
}
|
}
|
||||||
for _, alertID := range args {
|
for _, alertID := range args {
|
||||||
id, err := strconv.Atoi(alertID)
|
id, err := strconv.Atoi(alertID)
|
||||||
|
|
|
@ -10,13 +10,15 @@ import (
|
||||||
"github.com/sanity-io/litter"
|
"github.com/sanity-io/litter"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func showConfigKey(key string) error {
|
func (cli *cliConfig) showKey(key string) error {
|
||||||
|
cfg := cli.cfg()
|
||||||
|
|
||||||
type Env struct {
|
type Env struct {
|
||||||
Config *csconfig.Config
|
Config *csconfig.Config
|
||||||
}
|
}
|
||||||
|
@ -30,15 +32,15 @@ func showConfigKey(key string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := expr.Run(program, Env{Config: csConfig})
|
output, err := expr.Run(program, Env{Config: cfg})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch csConfig.Cscli.Output {
|
switch cfg.Cscli.Output {
|
||||||
case "human", "raw":
|
case "human", "raw":
|
||||||
// Don't use litter for strings, it adds quotes
|
// Don't use litter for strings, it adds quotes
|
||||||
// that we didn't have before
|
// that would break compatibility with previous versions
|
||||||
switch output.(type) {
|
switch output.(type) {
|
||||||
case string:
|
case string:
|
||||||
fmt.Println(output)
|
fmt.Println(output)
|
||||||
|
@ -51,13 +53,14 @@ func showConfigKey(key string) error {
|
||||||
return fmt.Errorf("failed to marshal configuration: %w", err)
|
return fmt.Errorf("failed to marshal configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Println(string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var configShowTemplate = `Global:
|
func (cli *cliConfig) template() string {
|
||||||
|
return `Global:
|
||||||
|
|
||||||
{{- if .ConfigPaths }}
|
{{- if .ConfigPaths }}
|
||||||
- Configuration Folder : {{.ConfigPaths.ConfigDir}}
|
- Configuration Folder : {{.ConfigPaths.ConfigDir}}
|
||||||
|
@ -182,19 +185,11 @@ Central API:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
`
|
`
|
||||||
|
}
|
||||||
|
|
||||||
func (cli *cliConfig) show(key string) error {
|
func (cli *cliConfig) show() error {
|
||||||
cfg := cli.cfg()
|
cfg := cli.cfg()
|
||||||
|
|
||||||
if err := cfg.LoadAPIClient(); err != nil {
|
|
||||||
log.Errorf("failed to load API client configuration: %s", err)
|
|
||||||
// don't return, we can still show the configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
if key != "" {
|
|
||||||
return showConfigKey(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch cfg.Cscli.Output {
|
switch cfg.Cscli.Output {
|
||||||
case "human":
|
case "human":
|
||||||
// The tests on .Enable look funny because the option has a true default which has
|
// The tests on .Enable look funny because the option has a true default which has
|
||||||
|
@ -205,7 +200,7 @@ func (cli *cliConfig) show(key string) error {
|
||||||
"ValueBool": func(b *bool) bool { return b != nil && *b },
|
"ValueBool": func(b *bool) bool { return b != nil && *b },
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp, err := template.New("config").Funcs(funcs).Parse(configShowTemplate)
|
tmp, err := template.New("config").Funcs(funcs).Parse(cli.template())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -220,14 +215,14 @@ func (cli *cliConfig) show(key string) error {
|
||||||
return fmt.Errorf("failed to marshal configuration: %w", err)
|
return fmt.Errorf("failed to marshal configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Println(string(data))
|
||||||
case "raw":
|
case "raw":
|
||||||
data, err := yaml.Marshal(cfg)
|
data, err := yaml.Marshal(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to marshal configuration: %w", err)
|
return fmt.Errorf("failed to marshal configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Println(string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -243,7 +238,16 @@ func (cli *cliConfig) newShowCmd() *cobra.Command {
|
||||||
Args: cobra.ExactArgs(0),
|
Args: cobra.ExactArgs(0),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(_ *cobra.Command, _ []string) error {
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
return cli.show(key)
|
if err := cli.cfg().LoadAPIClient(); err != nil {
|
||||||
|
log.Errorf("failed to load API client configuration: %s", err)
|
||||||
|
// don't return, we can still show the configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
if key != "" {
|
||||||
|
return cli.showKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cli.show()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
@ -36,7 +38,7 @@ func NewCLIConsole(cfg configGetter) *cliConsole {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *cliConsole) NewCommand() *cobra.Command {
|
func (cli *cliConsole) NewCommand() *cobra.Command {
|
||||||
var cmd = &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "console [action]",
|
Use: "console [action]",
|
||||||
Short: "Manage interaction with Crowdsec console (https://app.crowdsec.net)",
|
Short: "Manage interaction with Crowdsec console (https://app.crowdsec.net)",
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
@ -203,7 +205,7 @@ Enable given information push to the central API. Allows to empower the console`
|
||||||
log.Infof("All features have been enabled successfully")
|
log.Infof("All features have been enabled successfully")
|
||||||
} else {
|
} else {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf("you must specify at least one feature to enable")
|
return errors.New("you must specify at least one feature to enable")
|
||||||
}
|
}
|
||||||
if err := cli.setConsoleOpts(args, true); err != nil {
|
if err := cli.setConsoleOpts(args, true); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -288,11 +290,11 @@ func (cli *cliConsole) newStatusCmd() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
rows := [][]string{
|
rows := [][]string{
|
||||||
{csconfig.SEND_MANUAL_SCENARIOS, fmt.Sprintf("%t", *consoleCfg.ShareManualDecisions)},
|
{csconfig.SEND_MANUAL_SCENARIOS, strconv.FormatBool(*consoleCfg.ShareManualDecisions)},
|
||||||
{csconfig.SEND_CUSTOM_SCENARIOS, fmt.Sprintf("%t", *consoleCfg.ShareCustomScenarios)},
|
{csconfig.SEND_CUSTOM_SCENARIOS, strconv.FormatBool(*consoleCfg.ShareCustomScenarios)},
|
||||||
{csconfig.SEND_TAINTED_SCENARIOS, fmt.Sprintf("%t", *consoleCfg.ShareTaintedScenarios)},
|
{csconfig.SEND_TAINTED_SCENARIOS, strconv.FormatBool(*consoleCfg.ShareTaintedScenarios)},
|
||||||
{csconfig.SEND_CONTEXT, fmt.Sprintf("%t", *consoleCfg.ShareContext)},
|
{csconfig.SEND_CONTEXT, strconv.FormatBool(*consoleCfg.ShareContext)},
|
||||||
{csconfig.CONSOLE_MANAGEMENT, fmt.Sprintf("%t", *consoleCfg.ConsoleManagement)},
|
{csconfig.CONSOLE_MANAGEMENT, strconv.FormatBool(*consoleCfg.ConsoleManagement)},
|
||||||
}
|
}
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
err = csvwriter.Write(row)
|
err = csvwriter.Write(row)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
/*help to copy the file, ioutil doesn't offer the feature*/
|
/*help to copy the file, ioutil doesn't offer the feature*/
|
||||||
|
|
||||||
func copyFileContents(src, dst string) (err error) {
|
func copyFileContents(src, dst string) (err error) {
|
||||||
|
@ -69,6 +68,7 @@ func CopyFile(sourceSymLink, destinationFile string) error {
|
||||||
if !(destinationFileStat.Mode().IsRegular()) {
|
if !(destinationFileStat.Mode().IsRegular()) {
|
||||||
return fmt.Errorf("copyFile: non-regular destination file %s (%q)", destinationFileStat.Name(), destinationFileStat.Mode().String())
|
return fmt.Errorf("copyFile: non-regular destination file %s (%q)", destinationFileStat.Name(), destinationFileStat.Mode().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.SameFile(sourceFileStat, destinationFileStat) {
|
if os.SameFile(sourceFileStat, destinationFileStat) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -80,4 +80,3 @@ func CopyFile(sourceSymLink, destinationFile string) error {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -346,7 +347,7 @@ cscli decisions add --scope username --value foobar
|
||||||
addScope = types.Range
|
addScope = types.Range
|
||||||
} else if addValue == "" {
|
} else if addValue == "" {
|
||||||
printHelp(cmd)
|
printHelp(cmd)
|
||||||
return fmt.Errorf("missing arguments, a value is required (--ip, --range or --scope and --value)")
|
return errors.New("missing arguments, a value is required (--ip, --range or --scope and --value)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if addReason == "" {
|
if addReason == "" {
|
||||||
|
@ -411,7 +412,7 @@ cscli decisions add --scope username --value foobar
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *cliDecisions) newDeleteCmd() *cobra.Command {
|
func (cli *cliDecisions) newDeleteCmd() *cobra.Command {
|
||||||
var delFilter = apiclient.DecisionsDeleteOpts{
|
delFilter := apiclient.DecisionsDeleteOpts{
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: new(string),
|
||||||
ValueEquals: new(string),
|
ValueEquals: new(string),
|
||||||
TypeEquals: new(string),
|
TypeEquals: new(string),
|
||||||
|
@ -448,7 +449,7 @@ cscli decisions delete --origin lists --scenario list_name
|
||||||
*delFilter.RangeEquals == "" && *delFilter.ScenarioEquals == "" &&
|
*delFilter.RangeEquals == "" && *delFilter.ScenarioEquals == "" &&
|
||||||
*delFilter.OriginEquals == "" && delDecisionID == "" {
|
*delFilter.OriginEquals == "" && delDecisionID == "" {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return fmt.Errorf("at least one filter or --all must be specified")
|
return errors.New("at least one filter or --all must be specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -81,7 +82,7 @@ func (cli *cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaultDuration == "" {
|
if defaultDuration == "" {
|
||||||
return fmt.Errorf("--duration cannot be empty")
|
return errors.New("--duration cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultScope, err := flags.GetString("scope")
|
defaultScope, err := flags.GetString("scope")
|
||||||
|
@ -90,7 +91,7 @@ func (cli *cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaultScope == "" {
|
if defaultScope == "" {
|
||||||
return fmt.Errorf("--scope cannot be empty")
|
return errors.New("--scope cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultReason, err := flags.GetString("reason")
|
defaultReason, err := flags.GetString("reason")
|
||||||
|
@ -99,7 +100,7 @@ func (cli *cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaultReason == "" {
|
if defaultReason == "" {
|
||||||
return fmt.Errorf("--reason cannot be empty")
|
return errors.New("--reason cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultType, err := flags.GetString("type")
|
defaultType, err := flags.GetString("type")
|
||||||
|
@ -108,7 +109,7 @@ func (cli *cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaultType == "" {
|
if defaultType == "" {
|
||||||
return fmt.Errorf("--type cannot be empty")
|
return errors.New("--type cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
batchSize, err := flags.GetInt("batch")
|
batchSize, err := flags.GetInt("batch")
|
||||||
|
@ -136,7 +137,7 @@ func (cli *cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == "" {
|
if format == "" {
|
||||||
return fmt.Errorf("unable to guess format from file extension, please provide a format with --format flag")
|
return errors.New("unable to guess format from file extension, please provide a format with --format flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
if input == "-" {
|
if input == "-" {
|
||||||
|
@ -235,7 +236,6 @@ func (cli *cliDecisions) runImport(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (cli *cliDecisions) newImportCmd() *cobra.Command {
|
func (cli *cliDecisions) newImportCmd() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "import [options]",
|
Use: "import [options]",
|
||||||
|
|
|
@ -39,8 +39,10 @@ id: %s
|
||||||
title: %s
|
title: %s
|
||||||
---
|
---
|
||||||
`
|
`
|
||||||
|
|
||||||
name := filepath.Base(filename)
|
name := filepath.Base(filename)
|
||||||
base := strings.TrimSuffix(name, filepath.Ext(name))
|
base := strings.TrimSuffix(name, filepath.Ext(name))
|
||||||
|
|
||||||
return fmt.Sprintf(header, base, strings.ReplaceAll(base, "_", " "))
|
return fmt.Sprintf(header, base, strings.ReplaceAll(base, "_", " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ tail -n 5 myfile.log | cscli explain --type nginx -f -
|
||||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||||
fileInfo, _ := os.Stdin.Stat()
|
fileInfo, _ := os.Stdin.Stat()
|
||||||
if cli.flags.logFile == "-" && ((fileInfo.Mode() & os.ModeCharDevice) == os.ModeCharDevice) {
|
if cli.flags.logFile == "-" && ((fileInfo.Mode() & os.ModeCharDevice) == os.ModeCharDevice) {
|
||||||
return fmt.Errorf("the option -f - is intended to work with pipes")
|
return errors.New("the option -f - is intended to work with pipes")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -160,18 +160,22 @@ func (cli *cliExplain) run() error {
|
||||||
} else if logFile == "-" {
|
} else if logFile == "-" {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
errCount := 0
|
errCount := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
input, err := reader.ReadBytes('\n')
|
input, err := reader.ReadBytes('\n')
|
||||||
if err != nil && errors.Is(err, io.EOF) {
|
if err != nil && errors.Is(err, io.EOF) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(input) > 1 {
|
if len(input) > 1 {
|
||||||
_, err = f.Write(input)
|
_, err = f.Write(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil || len(input) <= 1 {
|
if err != nil || len(input) <= 1 {
|
||||||
errCount++
|
errCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if errCount > 0 {
|
if errCount > 0 {
|
||||||
log.Warnf("Failed to write %d lines to %s", errCount, tmpFile)
|
log.Warnf("Failed to write %d lines to %s", errCount, tmpFile)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +211,7 @@ func (cli *cliExplain) run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if dsn == "" {
|
if dsn == "" {
|
||||||
return fmt.Errorf("no acquisition (--file or --dsn) provided, can't run cscli test")
|
return errors.New("no acquisition (--file or --dsn) provided, can't run cscli test")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdArgs := []string{"-c", ConfigFilePath, "-type", logType, "-dsn", dsn, "-dump-data", dir, "-no-api"}
|
cmdArgs := []string{"-c", ConfigFilePath, "-type", logType, "-dsn", dsn, "-dump-data", dir, "-no-api"}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
@ -22,7 +21,8 @@ import (
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/parser"
|
"github.com/crowdsecurity/crowdsec/pkg/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*prometheus*/
|
// Prometheus
|
||||||
|
|
||||||
var globalParserHits = prometheus.NewCounterVec(
|
var globalParserHits = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "cs_parser_hits_total",
|
Name: "cs_parser_hits_total",
|
||||||
|
@ -30,6 +30,7 @@ var globalParserHits = prometheus.NewCounterVec(
|
||||||
},
|
},
|
||||||
[]string{"source", "type"},
|
[]string{"source", "type"},
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalParserHitsOk = prometheus.NewCounterVec(
|
var globalParserHitsOk = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "cs_parser_hits_ok_total",
|
Name: "cs_parser_hits_ok_total",
|
||||||
|
@ -37,6 +38,7 @@ var globalParserHitsOk = prometheus.NewCounterVec(
|
||||||
},
|
},
|
||||||
[]string{"source", "type"},
|
[]string{"source", "type"},
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalParserHitsKo = prometheus.NewCounterVec(
|
var globalParserHitsKo = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "cs_parser_hits_ko_total",
|
Name: "cs_parser_hits_ko_total",
|
||||||
|
@ -116,9 +118,7 @@ func computeDynamicMetrics(next http.Handler, dbClient *database.Client) http.Ha
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
decisionsFilters := make(map[string][]string, 0)
|
decisions, err := dbClient.QueryDecisionCountByScenario()
|
||||||
|
|
||||||
decisions, err := dbClient.QueryDecisionCountByScenario(decisionsFilters)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error querying decisions for metrics: %v", err)
|
log.Errorf("Error querying decisions for metrics: %v", err)
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
@ -139,7 +139,6 @@ func computeDynamicMetrics(next http.Handler, dbClient *database.Client) http.Ha
|
||||||
}
|
}
|
||||||
|
|
||||||
alerts, err := dbClient.AlertsCountPerScenario(alertsFilter)
|
alerts, err := dbClient.AlertsCountPerScenario(alertsFilter)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error querying alerts for metrics: %v", err)
|
log.Errorf("Error querying alerts for metrics: %v", err)
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
@ -194,7 +193,6 @@ func servePrometheus(config *csconfig.PrometheusCfg, dbClient *database.Client,
|
||||||
defer trace.CatchPanic("crowdsec/servePrometheus")
|
defer trace.CatchPanic("crowdsec/servePrometheus")
|
||||||
|
|
||||||
http.Handle("/metrics", computeDynamicMetrics(promhttp.Handler(), dbClient))
|
http.Handle("/metrics", computeDynamicMetrics(promhttp.Handler(), dbClient))
|
||||||
log.Debugf("serving metrics after %s ms", time.Since(crowdsecT0))
|
|
||||||
|
|
||||||
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", config.ListenAddr, config.ListenPort), nil); err != nil {
|
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", config.ListenAddr, config.ListenPort), nil); err != nil {
|
||||||
// in time machine, we most likely have the LAPI using the port
|
// in time machine, we most likely have the LAPI using the port
|
||||||
|
|
|
@ -37,6 +37,7 @@ func BuildDecisionRequestWithFilter(query *ent.DecisionQuery, filter map[string]
|
||||||
if v[0] == "false" {
|
if v[0] == "false" {
|
||||||
query = query.Where(decision.SimulatedEQ(false))
|
query = query.Where(decision.SimulatedEQ(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(filter, "simulated")
|
delete(filter, "simulated")
|
||||||
} else {
|
} else {
|
||||||
query = query.Where(decision.SimulatedEQ(false))
|
query = query.Where(decision.SimulatedEQ(false))
|
||||||
|
@ -63,6 +64,7 @@ func BuildDecisionRequestWithFilter(query *ent.DecisionQuery, filter map[string]
|
||||||
scopes[i] = types.AS
|
scopes[i] = types.AS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query = query.Where(decision.ScopeIn(scopes...))
|
query = query.Where(decision.ScopeIn(scopes...))
|
||||||
case "value":
|
case "value":
|
||||||
query = query.Where(decision.ValueEQ(value[0]))
|
query = query.Where(decision.ValueEQ(value[0]))
|
||||||
|
@ -164,11 +166,11 @@ func (c *Client) QueryExpiredDecisionsWithFilters(filters map[string][]string) (
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) QueryDecisionCountByScenario(filters map[string][]string) ([]*DecisionsByScenario, error) {
|
func (c *Client) QueryDecisionCountByScenario() ([]*DecisionsByScenario, error) {
|
||||||
query := c.Ent.Decision.Query().Where(
|
query := c.Ent.Decision.Query().Where(
|
||||||
decision.UntilGT(time.Now().UTC()),
|
decision.UntilGT(time.Now().UTC()),
|
||||||
)
|
)
|
||||||
query, err := BuildDecisionRequestWithFilter(query, filters)
|
query, err := BuildDecisionRequestWithFilter(query, make(map[string][]string))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Warningf("QueryDecisionCountByScenario : %s", err)
|
c.Log.Warningf("QueryDecisionCountByScenario : %s", err)
|
||||||
|
@ -277,10 +279,12 @@ func (c *Client) QueryNewDecisionsSinceWithFilters(since time.Time, filters map[
|
||||||
decision.CreatedAtGT(since),
|
decision.CreatedAtGT(since),
|
||||||
decision.UntilGT(time.Now().UTC()),
|
decision.UntilGT(time.Now().UTC()),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Allow a bouncer to ask for non-deduplicated results
|
// Allow a bouncer to ask for non-deduplicated results
|
||||||
if v, ok := filters["dedup"]; !ok || v[0] != "false" {
|
if v, ok := filters["dedup"]; !ok || v[0] != "false" {
|
||||||
query = query.Where(longestDecisionForScopeTypeValue)
|
query = query.Where(longestDecisionForScopeTypeValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
query, err := BuildDecisionRequestWithFilter(query, filters)
|
query, err := BuildDecisionRequestWithFilter(query, filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Warningf("QueryNewDecisionsSinceWithFilters : %s", err)
|
c.Log.Warningf("QueryNewDecisionsSinceWithFilters : %s", err)
|
||||||
|
@ -294,17 +298,20 @@ func (c *Client) QueryNewDecisionsSinceWithFilters(since time.Time, filters map[
|
||||||
c.Log.Warningf("QueryNewDecisionsSinceWithFilters : %s", err)
|
c.Log.Warningf("QueryNewDecisionsSinceWithFilters : %s", err)
|
||||||
return []*ent.Decision{}, errors.Wrapf(QueryFail, "new decisions since '%s'", since.String())
|
return []*ent.Decision{}, errors.Wrapf(QueryFail, "new decisions since '%s'", since.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteDecisionById(decisionId int) ([]*ent.Decision, error) {
|
func (c *Client) DeleteDecisionById(decisionID int) ([]*ent.Decision, error) {
|
||||||
toDelete, err := c.Ent.Decision.Query().Where(decision.IDEQ(decisionId)).All(c.CTX)
|
toDelete, err := c.Ent.Decision.Query().Where(decision.IDEQ(decisionID)).All(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Warningf("DeleteDecisionById : %s", err)
|
c.Log.Warningf("DeleteDecisionById : %s", err)
|
||||||
return nil, errors.Wrapf(DeleteFail, "decision with id '%d' doesn't exist", decisionId)
|
return nil, errors.Wrapf(DeleteFail, "decision with id '%d' doesn't exist", decisionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := c.BulkDeleteDecisions(toDelete, false)
|
count, err := c.BulkDeleteDecisions(toDelete, false)
|
||||||
c.Log.Debugf("deleted %d decisions", count)
|
c.Log.Debugf("deleted %d decisions", count)
|
||||||
|
|
||||||
return toDelete, err
|
return toDelete, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,6 +324,7 @@ func (c *Client) DeleteDecisionsWithFilter(filter map[string][]string) (string,
|
||||||
else, return bans that are *contained* by the given value (value is the outer) */
|
else, return bans that are *contained* by the given value (value is the outer) */
|
||||||
|
|
||||||
decisions := c.Ent.Decision.Query()
|
decisions := c.Ent.Decision.Query()
|
||||||
|
|
||||||
for param, value := range filter {
|
for param, value := range filter {
|
||||||
switch param {
|
switch param {
|
||||||
case "contains":
|
case "contains":
|
||||||
|
@ -415,11 +423,13 @@ func (c *Client) DeleteDecisionsWithFilter(filter map[string][]string) (string,
|
||||||
c.Log.Warningf("DeleteDecisionsWithFilter : %s", err)
|
c.Log.Warningf("DeleteDecisionsWithFilter : %s", err)
|
||||||
return "0", nil, errors.Wrap(DeleteFail, "decisions with provided filter")
|
return "0", nil, errors.Wrap(DeleteFail, "decisions with provided filter")
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := c.BulkDeleteDecisions(toDelete, false)
|
count, err := c.BulkDeleteDecisions(toDelete, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Warningf("While deleting decisions : %s", err)
|
c.Log.Warningf("While deleting decisions : %s", err)
|
||||||
return "0", nil, errors.Wrap(DeleteFail, "decisions with provided filter")
|
return "0", nil, errors.Wrap(DeleteFail, "decisions with provided filter")
|
||||||
}
|
}
|
||||||
|
|
||||||
return strconv.Itoa(count), toDelete, nil
|
return strconv.Itoa(count), toDelete, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,6 +442,7 @@ func (c *Client) SoftDeleteDecisionsWithFilter(filter map[string][]string) (stri
|
||||||
/*if contains is true, return bans that *contains* the given value (value is the inner)
|
/*if contains is true, return bans that *contains* the given value (value is the inner)
|
||||||
else, return bans that are *contained* by the given value (value is the outer)*/
|
else, return bans that are *contained* by the given value (value is the outer)*/
|
||||||
decisions := c.Ent.Decision.Query().Where(decision.UntilGT(time.Now().UTC()))
|
decisions := c.Ent.Decision.Query().Where(decision.UntilGT(time.Now().UTC()))
|
||||||
|
|
||||||
for param, value := range filter {
|
for param, value := range filter {
|
||||||
switch param {
|
switch param {
|
||||||
case "contains":
|
case "contains":
|
||||||
|
@ -531,6 +542,7 @@ func (c *Client) SoftDeleteDecisionsWithFilter(filter map[string][]string) (stri
|
||||||
} else if ip_sz != 0 {
|
} else if ip_sz != 0 {
|
||||||
return "0", nil, errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz)
|
return "0", nil, errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz)
|
||||||
}
|
}
|
||||||
|
|
||||||
DecisionsToDelete, err := decisions.All(c.CTX)
|
DecisionsToDelete, err := decisions.All(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Warningf("SoftDeleteDecisionsWithFilter : %s", err)
|
c.Log.Warningf("SoftDeleteDecisionsWithFilter : %s", err)
|
||||||
|
@ -541,10 +553,11 @@ func (c *Client) SoftDeleteDecisionsWithFilter(filter map[string][]string) (stri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "0", nil, errors.Wrapf(DeleteFail, "soft delete decisions with provided filter : %s", err)
|
return "0", nil, errors.Wrapf(DeleteFail, "soft delete decisions with provided filter : %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return strconv.Itoa(count), DecisionsToDelete, err
|
return strconv.Itoa(count), DecisionsToDelete, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BulkDeleteDecisions set the expiration of a bulk of decisions to now() or hard deletes them.
|
// BulkDeleteDecisions sets the expiration of a bulk of decisions to now() or hard deletes them.
|
||||||
// We are doing it this way so we can return impacted decisions for sync with CAPI/PAPI
|
// We are doing it this way so we can return impacted decisions for sync with CAPI/PAPI
|
||||||
func (c *Client) BulkDeleteDecisions(decisionsToDelete []*ent.Decision, softDelete bool) (int, error) {
|
func (c *Client) BulkDeleteDecisions(decisionsToDelete []*ent.Decision, softDelete bool) (int, error) {
|
||||||
const bulkSize = 256 // scientifically proven to be the best value for bulk delete
|
const bulkSize = 256 // scientifically proven to be the best value for bulk delete
|
||||||
|
@ -576,6 +589,7 @@ func (c *Client) BulkDeleteDecisions(decisionsToDelete []*ent.Decision, softDele
|
||||||
return totalUpdates, fmt.Errorf("hard delete decisions with provided filter: %w", err)
|
return totalUpdates, fmt.Errorf("hard delete decisions with provided filter: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
totalUpdates += nbUpdates
|
totalUpdates += nbUpdates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,6 +626,7 @@ func (c *Client) CountDecisionsByValue(decisionValue string) (int, error) {
|
||||||
|
|
||||||
contains := true
|
contains := true
|
||||||
decisions := c.Ent.Decision.Query()
|
decisions := c.Ent.Decision.Query()
|
||||||
|
|
||||||
decisions, err = applyStartIpEndIpFilter(decisions, contains, ip_sz, start_ip, start_sfx, end_ip, end_sfx)
|
decisions, err = applyStartIpEndIpFilter(decisions, contains, ip_sz, start_ip, start_sfx, end_ip, end_sfx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.Wrapf(err, "fail to apply StartIpEndIpFilter")
|
return 0, errors.Wrapf(err, "fail to apply StartIpEndIpFilter")
|
||||||
|
@ -667,6 +682,7 @@ func applyStartIpEndIpFilter(decisions *ent.DecisionQuery, contains bool, ip_sz
|
||||||
decision.IPSizeEQ(int64(ip_sz)),
|
decision.IPSizeEQ(int64(ip_sz)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
return decisions, nil
|
return decisions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,6 +738,7 @@ func applyStartIpEndIpFilter(decisions *ent.DecisionQuery, contains bool, ip_sz
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
return decisions, nil
|
return decisions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,8 +752,10 @@ func applyStartIpEndIpFilter(decisions *ent.DecisionQuery, contains bool, ip_sz
|
||||||
func decisionPredicatesFromStr(s string, predicateFunc func(string) predicate.Decision) []predicate.Decision {
|
func decisionPredicatesFromStr(s string, predicateFunc func(string) predicate.Decision) []predicate.Decision {
|
||||||
words := strings.Split(s, ",")
|
words := strings.Split(s, ",")
|
||||||
predicates := make([]predicate.Decision, len(words))
|
predicates := make([]predicate.Decision, len(words))
|
||||||
|
|
||||||
for i, word := range words {
|
for i, word := range words {
|
||||||
predicates[i] = predicateFunc(word)
|
predicates[i] = predicateFunc(word)
|
||||||
}
|
}
|
||||||
|
|
||||||
return predicates
|
return predicates
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue