merge master
This commit is contained in:
commit
17384368ae
|
@ -110,7 +110,7 @@ func NewCapiRegisterCmd() *cobra.Command {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("write api credentials in '%s' failed: %w", dumpFile, err)
|
return fmt.Errorf("write api credentials in '%s' failed: %w", dumpFile, err)
|
||||||
}
|
}
|
||||||
log.Printf("Central API credentials dumped to '%s'", dumpFile)
|
log.Printf("Central API credentials written to '%s'", dumpFile)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s\n", string(apiConfigDump))
|
fmt.Printf("%s\n", string(apiConfigDump))
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ func backupHub(dirPath string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
//for the local/tainted ones, we back up the full file
|
//for the local/tainted ones, we back up the full file
|
||||||
if v.State.Tainted || v.IsLocal() || !v.State.UpToDate {
|
if v.State.Tainted || v.State.IsLocal() || !v.State.UpToDate {
|
||||||
//we need to backup stages for parsers
|
//we need to backup stages for parsers
|
||||||
if itemType == cwhub.PARSERS || itemType == cwhub.POSTOVERFLOWS {
|
if itemType == cwhub.PARSERS || itemType == cwhub.POSTOVERFLOWS {
|
||||||
fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
|
fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
|
||||||
|
@ -54,7 +54,7 @@ func backupHub(dirPath string) error {
|
||||||
return fmt.Errorf("error while creating stage dir %s : %s", fstagedir, err)
|
return fmt.Errorf("error while creating stage dir %s : %s", fstagedir, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clog.Debugf("[%s]: backing up file (tainted:%t local:%t up-to-date:%t)", k, v.State.Tainted, v.IsLocal(), v.State.UpToDate)
|
clog.Debugf("[%s]: backing up file (tainted:%t local:%t up-to-date:%t)", k, v.State.Tainted, v.State.IsLocal(), v.State.UpToDate)
|
||||||
tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
|
tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
|
||||||
if err = CopyFile(v.State.LocalPath, tfile); err != nil {
|
if err = CopyFile(v.State.LocalPath, tfile); err != nil {
|
||||||
return fmt.Errorf("failed copy %s %s to %s : %s", itemType, v.State.LocalPath, tfile, err)
|
return fmt.Errorf("failed copy %s %s to %s : %s", itemType, v.State.LocalPath, tfile, err)
|
||||||
|
|
|
@ -183,7 +183,7 @@ func restoreConfigFromDirectory(dirPath string, oldBackup bool) error {
|
||||||
if csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.CredentialsFilePath != "" {
|
if csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.CredentialsFilePath != "" {
|
||||||
apiConfigDumpFile = csConfig.API.Server.OnlineClient.CredentialsFilePath
|
apiConfigDumpFile = csConfig.API.Server.OnlineClient.CredentialsFilePath
|
||||||
}
|
}
|
||||||
err = os.WriteFile(apiConfigDumpFile, apiConfigDump, 0o644)
|
err = os.WriteFile(apiConfigDumpFile, apiConfigDump, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("write api credentials in '%s' failed: %s", apiConfigDumpFile, err)
|
return fmt.Errorf("write api credentials in '%s' failed: %s", apiConfigDumpFile, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/antonmedv/expr"
|
"github.com/antonmedv/expr"
|
||||||
|
"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.v2"
|
||||||
|
@ -35,13 +36,13 @@ func showConfigKey(key string) error {
|
||||||
|
|
||||||
switch csConfig.Cscli.Output {
|
switch csConfig.Cscli.Output {
|
||||||
case "human", "raw":
|
case "human", "raw":
|
||||||
|
// Don't use litter for strings, it adds quotes
|
||||||
|
// that we didn't have before
|
||||||
switch output.(type) {
|
switch output.(type) {
|
||||||
case string:
|
case string:
|
||||||
fmt.Printf("%s\n", output)
|
fmt.Println(output)
|
||||||
case int:
|
|
||||||
fmt.Printf("%d\n", output)
|
|
||||||
default:
|
default:
|
||||||
fmt.Printf("%v\n", output)
|
litter.Dump(output)
|
||||||
}
|
}
|
||||||
case "json":
|
case "json":
|
||||||
data, err := json.MarshalIndent(output, "", " ")
|
data, err := json.MarshalIndent(output, "", " ")
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -9,6 +11,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
@ -136,6 +139,9 @@ cscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err = chownDatabase(dockerGroup.Gid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
mb, err := metabase.SetupMetabase(csConfig.API.Server.DbConfig, metabaseListenAddress, metabaseListenPort, metabaseUser, metabasePassword, metabaseDbPath, dockerGroup.Gid, metabaseContainerID, metabaseImage)
|
mb, err := metabase.SetupMetabase(csConfig.API.Server.DbConfig, metabaseListenAddress, metabaseListenPort, metabaseUser, metabasePassword, metabaseDbPath, dockerGroup.Gid, metabaseContainerID, metabaseImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -366,12 +372,10 @@ func disclaimer(forceYes *bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkGroups(forceYes *bool) (*user.Group, error) {
|
func checkGroups(forceYes *bool) (*user.Group, error) {
|
||||||
groupExist := false
|
|
||||||
dockerGroup, err := user.LookupGroup(crowdsecGroup)
|
dockerGroup, err := user.LookupGroup(crowdsecGroup)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
groupExist = true
|
return dockerGroup, nil
|
||||||
}
|
}
|
||||||
if !groupExist {
|
|
||||||
if !*forceYes {
|
if !*forceYes {
|
||||||
var answer bool
|
var answer bool
|
||||||
prompt := &survey.Confirm{
|
prompt := &survey.Confirm{
|
||||||
|
@ -394,17 +398,30 @@ func checkGroups(forceYes *bool) (*user.Group, error) {
|
||||||
if err := groupAdd.Run(); err != nil {
|
if err := groupAdd.Run(); err != nil {
|
||||||
return dockerGroup, fmt.Errorf("unable to add group '%s': %s", dockerGroup, err)
|
return dockerGroup, fmt.Errorf("unable to add group '%s': %s", dockerGroup, err)
|
||||||
}
|
}
|
||||||
dockerGroup, err = user.LookupGroup(crowdsecGroup)
|
return user.LookupGroup(crowdsecGroup)
|
||||||
if err != nil {
|
}
|
||||||
return dockerGroup, fmt.Errorf("unable to lookup '%s' group: %+v", dockerGroup, err)
|
|
||||||
}
|
func chownDatabase(gid string) error {
|
||||||
}
|
intID, err := strconv.Atoi(gid)
|
||||||
intID, err := strconv.Atoi(dockerGroup.Gid)
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("unable to convert group ID to int: %s", err)
|
||||||
return dockerGroup, fmt.Errorf("unable to convert group ID to int: %s", err)
|
}
|
||||||
}
|
if stat, err := os.Stat(csConfig.DbConfig.DbPath); !os.IsNotExist(err) {
|
||||||
if err := os.Chown(csConfig.DbConfig.DbPath, 0, intID); err != nil {
|
info := stat.Sys()
|
||||||
return dockerGroup, fmt.Errorf("unable to chown sqlite db file '%s': %s", csConfig.DbConfig.DbPath, err)
|
if err := os.Chown(csConfig.DbConfig.DbPath, int(info.(*syscall.Stat_t).Uid), intID); err != nil {
|
||||||
}
|
return fmt.Errorf("unable to chown sqlite db file '%s': %s", csConfig.DbConfig.DbPath, err)
|
||||||
return dockerGroup, nil
|
}
|
||||||
|
}
|
||||||
|
if csConfig.DbConfig.Type == "sqlite" && csConfig.DbConfig.UseWal != nil && *csConfig.DbConfig.UseWal {
|
||||||
|
for _, ext := range []string{"-wal", "-shm"} {
|
||||||
|
file := csConfig.DbConfig.DbPath + ext
|
||||||
|
if stat, err := os.Stat(file); !os.IsNotExist(err) {
|
||||||
|
info := stat.Sys()
|
||||||
|
if err := os.Chown(file, int(info.(*syscall.Stat_t).Uid), intID); err != nil {
|
||||||
|
return fmt.Errorf("unable to chown sqlite db file '%s': %s", file, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
22
cmd/crowdsec-cli/dashboard_unsupported.go
Normal file
22
cmd/crowdsec-cli/dashboard_unsupported.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDashboardCmd() *cobra.Command {
|
||||||
|
var cmdDashboard = &cobra.Command{
|
||||||
|
Use: "dashboard",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
log.Infof("Dashboard command is disabled on %s", runtime.GOOS)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdDashboard
|
||||||
|
}
|
|
@ -232,7 +232,7 @@ func NewDecisionsImportCmd() *cobra.Command {
|
||||||
Short: "Import decisions from a file or pipe",
|
Short: "Import decisions from a file or pipe",
|
||||||
Long: "expected format:\n" +
|
Long: "expected format:\n" +
|
||||||
"csv : any of duration,reason,scope,type,value, with a header line\n" +
|
"csv : any of duration,reason,scope,type,value, with a header line\n" +
|
||||||
`json : {"duration" : "24h", "reason" : "my_scenario", "scope" : "ip", "type" : "ban", "value" : "x.y.z.z"}`,
|
"json :" + "`{" + `"duration" : "24h", "reason" : "my_scenario", "scope" : "ip", "type" : "ban", "value" : "x.y.z.z"` + "}`",
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
Example: `decisions.csv:
|
Example: `decisions.csv:
|
||||||
duration,scope,value
|
duration,scope,value
|
||||||
|
|
|
@ -21,10 +21,16 @@ func GetLineCountForFile(filepath string) (int, error) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
lc := 0
|
lc := 0
|
||||||
fs := bufio.NewScanner(f)
|
fs := bufio.NewReader(f)
|
||||||
for fs.Scan() {
|
for {
|
||||||
|
input, err := fs.ReadBytes('\n')
|
||||||
|
if len(input) > 1 {
|
||||||
lc++
|
lc++
|
||||||
}
|
}
|
||||||
|
if err != nil && err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
return lc, nil
|
return lc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,19 +85,6 @@ func runExplain(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInfo, _ := os.Stdin.Stat()
|
|
||||||
|
|
||||||
if logType == "" || (logLine == "" && logFile == "" && dsn == "") {
|
|
||||||
printHelp(cmd)
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Printf("Please provide --type flag\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if logFile == "-" && ((fileInfo.Mode() & os.ModeCharDevice) == os.ModeCharDevice) {
|
|
||||||
return fmt.Errorf("the option -f - is intended to work with pipes")
|
|
||||||
}
|
|
||||||
|
|
||||||
var f *os.File
|
var f *os.File
|
||||||
|
|
||||||
// using empty string fallback to /tmp
|
// using empty string fallback to /tmp
|
||||||
|
@ -99,6 +92,13 @@ func runExplain(cmd *cobra.Command, args []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't create a temporary directory to store cscli explain result: %s", err)
|
return fmt.Errorf("couldn't create a temporary directory to store cscli explain result: %s", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if _, err := os.Stat(dir); !os.IsNotExist(err) {
|
||||||
|
if err := os.RemoveAll(dir); err != nil {
|
||||||
|
log.Errorf("unable to delete temporary directory '%s': %s", dir, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
tmpFile := ""
|
tmpFile := ""
|
||||||
// we create a temporary log file if a log line/stdin has been provided
|
// we create a temporary log file if a log line/stdin has been provided
|
||||||
if logLine != "" || logFile == "-" {
|
if logLine != "" || logFile == "-" {
|
||||||
|
@ -121,13 +121,15 @@ func runExplain(cmd *cobra.Command, args []string) error {
|
||||||
if err != nil && err == io.EOF {
|
if err != nil && err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if len(input) > 1 {
|
||||||
_, err = f.Write(input)
|
_, err = f.Write(input)
|
||||||
if err != nil {
|
}
|
||||||
|
if err != nil || len(input) <= 1 {
|
||||||
errCount++
|
errCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if errCount > 0 {
|
if errCount > 0 {
|
||||||
log.Warnf("Failed to write %d lines to tmp file", errCount)
|
log.Warnf("Failed to write %d lines to %s", errCount, tmpFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
|
@ -145,8 +147,12 @@ func runExplain(cmd *cobra.Command, args []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.Debugf("file %s has %d lines", absolutePath, lineCount)
|
||||||
|
if lineCount == 0 {
|
||||||
|
return fmt.Errorf("the log file is empty: %s", absolutePath)
|
||||||
|
}
|
||||||
if lineCount > 100 {
|
if lineCount > 100 {
|
||||||
log.Warnf("The log file contains %d lines. This may take a lot of resources.", lineCount)
|
log.Warnf("%s contains %d lines. This may take a lot of resources.", absolutePath, lineCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,12 +172,6 @@ func runExplain(cmd *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("fail to run crowdsec for test: %v", err)
|
return fmt.Errorf("fail to run crowdsec for test: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rm the temporary log file if only a log line/stdin was provided
|
|
||||||
if tmpFile != "" {
|
|
||||||
if err := os.Remove(tmpFile); err != nil {
|
|
||||||
return fmt.Errorf("unable to remove tmp log file '%s': %+v", tmpFile, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parserDumpFile := filepath.Join(dir, hubtest.ParserResultFileName)
|
parserDumpFile := filepath.Join(dir, hubtest.ParserResultFileName)
|
||||||
bucketStateDumpFile := filepath.Join(dir, hubtest.BucketPourResultFileName)
|
bucketStateDumpFile := filepath.Join(dir, hubtest.BucketPourResultFileName)
|
||||||
|
|
||||||
|
@ -187,10 +187,6 @@ func runExplain(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
hubtest.DumpTree(*parserDump, *bucketStateDump, opts)
|
hubtest.DumpTree(*parserDump, *bucketStateDump, opts)
|
||||||
|
|
||||||
if err := os.RemoveAll(dir); err != nil {
|
|
||||||
return fmt.Errorf("unable to delete temporary directory '%s': %s", dir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +206,45 @@ tail -n 5 myfile.log | cscli explain --type nginx -f -
|
||||||
Args: cobra.ExactArgs(0),
|
Args: cobra.ExactArgs(0),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: runExplain,
|
RunE: runExplain,
|
||||||
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
logFile, err := flags.GetString("file")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn, err := flags.GetString("dsn")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logLine, err := flags.GetString("log")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logType, err := flags.GetString("type")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if logLine == "" && logFile == "" && dsn == "" {
|
||||||
|
printHelp(cmd)
|
||||||
|
fmt.Println()
|
||||||
|
return fmt.Errorf("please provide --log, --file or --dsn flag")
|
||||||
|
}
|
||||||
|
if logType == "" {
|
||||||
|
printHelp(cmd)
|
||||||
|
fmt.Println()
|
||||||
|
return fmt.Errorf("please provide --type flag")
|
||||||
|
}
|
||||||
|
fileInfo, _ := os.Stdin.Stat()
|
||||||
|
if logFile == "-" && ((fileInfo.Mode() & os.ModeCharDevice) == os.ModeCharDevice) {
|
||||||
|
return fmt.Errorf("the option -f - is intended to work with pipes")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmdExplain.Flags()
|
flags := cmdExplain.Flags()
|
||||||
|
|
|
@ -66,7 +66,7 @@ func runHubList(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = listItems(color.Output, cwhub.ItemTypes, items)
|
err = listItems(color.Output, cwhub.ItemTypes, items, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -640,7 +640,7 @@ func itemsListRunner(it hubItemType) func(cmd *cobra.Command, args []string) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = listItems(color.Output, []string{it.name}, items); err != nil {
|
if err = listItems(color.Output, []string{it.name}, items, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,11 +53,19 @@ func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly b
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item) error {
|
func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item, omitIfEmpty bool) error {
|
||||||
switch csConfig.Cscli.Output {
|
switch csConfig.Cscli.Output {
|
||||||
case "human":
|
case "human":
|
||||||
|
nothingToDisplay := true
|
||||||
for _, itemType := range itemTypes {
|
for _, itemType := range itemTypes {
|
||||||
|
if omitIfEmpty && len(items[itemType]) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
listHubItemTable(out, "\n"+strings.ToUpper(itemType), items[itemType])
|
listHubItemTable(out, "\n"+strings.ToUpper(itemType), items[itemType])
|
||||||
|
nothingToDisplay = false
|
||||||
|
}
|
||||||
|
if nothingToDisplay {
|
||||||
|
fmt.Println("No items to display")
|
||||||
}
|
}
|
||||||
case "json":
|
case "json":
|
||||||
type itemHubStatus struct {
|
type itemHubStatus struct {
|
||||||
|
@ -75,14 +83,15 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
|
||||||
hubStatus[itemType] = make([]itemHubStatus, len(items[itemType]))
|
hubStatus[itemType] = make([]itemHubStatus, len(items[itemType]))
|
||||||
|
|
||||||
for i, item := range items[itemType] {
|
for i, item := range items[itemType] {
|
||||||
status, emo := item.InstallStatus()
|
status := item.State.Text()
|
||||||
|
status_emo := item.State.Emoji()
|
||||||
hubStatus[itemType][i] = itemHubStatus{
|
hubStatus[itemType][i] = itemHubStatus{
|
||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
LocalVersion: item.State.LocalVersion,
|
LocalVersion: item.State.LocalVersion,
|
||||||
LocalPath: item.State.LocalPath,
|
LocalPath: item.State.LocalPath,
|
||||||
Description: item.Description,
|
Description: item.Description,
|
||||||
Status: status,
|
Status: status,
|
||||||
UTF8Status: fmt.Sprintf("%v %s", emo, status),
|
UTF8Status: fmt.Sprintf("%v %s", status_emo, status),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,10 +116,9 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
|
||||||
|
|
||||||
for _, itemType := range itemTypes {
|
for _, itemType := range itemTypes {
|
||||||
for _, item := range items[itemType] {
|
for _, item := range items[itemType] {
|
||||||
status, _ := item.InstallStatus()
|
|
||||||
row := []string{
|
row := []string{
|
||||||
item.Name,
|
item.Name,
|
||||||
status,
|
item.State.Text(),
|
||||||
item.State.LocalVersion,
|
item.State.LocalVersion,
|
||||||
item.Description,
|
item.Description,
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,11 +150,11 @@ func runLapiRegister(cmd *cobra.Command, args []string) error {
|
||||||
log.Fatalf("unable to marshal api credentials: %s", err)
|
log.Fatalf("unable to marshal api credentials: %s", err)
|
||||||
}
|
}
|
||||||
if dumpFile != "" {
|
if dumpFile != "" {
|
||||||
err = os.WriteFile(dumpFile, apiConfigDump, 0644)
|
err = os.WriteFile(dumpFile, apiConfigDump, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("write api credentials in '%s' failed: %s", dumpFile, err)
|
log.Fatalf("write api credentials in '%s' failed: %s", dumpFile, err)
|
||||||
}
|
}
|
||||||
log.Printf("Local API credentials dumped to '%s'", dumpFile)
|
log.Printf("Local API credentials written to '%s'", dumpFile)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s\n", string(apiConfigDump))
|
fmt.Printf("%s\n", string(apiConfigDump))
|
||||||
}
|
}
|
||||||
|
@ -332,7 +332,7 @@ cscli lapi context detect crowdsecurity/sshd-logs
|
||||||
}
|
}
|
||||||
|
|
||||||
// to avoid all the log.Info from the loaders functions
|
// to avoid all the log.Info from the loaders functions
|
||||||
log.SetLevel(log.ErrorLevel)
|
log.SetLevel(log.WarnLevel)
|
||||||
|
|
||||||
err = exprhelpers.Init(nil)
|
err = exprhelpers.Init(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -499,9 +499,8 @@ func detectNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
|
||||||
|
|
||||||
if node.Grok.RegexpName != "" {
|
if node.Grok.RegexpName != "" {
|
||||||
grokCompiled, err := parserCTX.Grok.Get(node.Grok.RegexpName)
|
grokCompiled, err := parserCTX.Grok.Get(node.Grok.RegexpName)
|
||||||
if err != nil {
|
// ignore error (parser does not exist?)
|
||||||
log.Warningf("Can't get subgrok: %s", err)
|
if err == nil {
|
||||||
}
|
|
||||||
for _, capturedField := range grokCompiled.Names() {
|
for _, capturedField := range grokCompiled.Names() {
|
||||||
fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
|
fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
|
||||||
if !slices.Contains(ret, fieldName) {
|
if !slices.Contains(ret, fieldName) {
|
||||||
|
@ -509,6 +508,7 @@ func detectNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(node.Grok.Statics) > 0 {
|
if len(node.Grok.Statics) > 0 {
|
||||||
staticsField := detectStaticField(node.Grok.Statics)
|
staticsField := detectStaticField(node.Grok.Statics)
|
||||||
|
@ -545,9 +545,8 @@ func detectSubNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
|
||||||
}
|
}
|
||||||
if subnode.Grok.RegexpName != "" {
|
if subnode.Grok.RegexpName != "" {
|
||||||
grokCompiled, err := parserCTX.Grok.Get(subnode.Grok.RegexpName)
|
grokCompiled, err := parserCTX.Grok.Get(subnode.Grok.RegexpName)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
log.Warningf("Can't get subgrok: %s", err)
|
// ignore error (parser does not exist?)
|
||||||
}
|
|
||||||
for _, capturedField := range grokCompiled.Names() {
|
for _, capturedField := range grokCompiled.Names() {
|
||||||
fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
|
fieldName := fmt.Sprintf("evt.Parsed.%s", capturedField)
|
||||||
if !slices.Contains(ret, fieldName) {
|
if !slices.Contains(ret, fieldName) {
|
||||||
|
@ -555,6 +554,7 @@ func detectSubNode(node parser.Node, parserCTX parser.UnixParserCtx) []string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(subnode.Grok.Statics) > 0 {
|
if len(subnode.Grok.Statics) > 0 {
|
||||||
staticsField := detectStaticField(subnode.Grok.Statics)
|
staticsField := detectStaticField(subnode.Grok.Statics)
|
||||||
|
|
|
@ -30,9 +30,7 @@ import (
|
||||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const passwordLength = 64
|
||||||
passwordLength = 64
|
|
||||||
)
|
|
||||||
|
|
||||||
func generatePassword(length int) string {
|
func generatePassword(length int) string {
|
||||||
upper := "ABCDEFGHIJKLMNOPQRSTUVWXY"
|
upper := "ABCDEFGHIJKLMNOPQRSTUVWXY"
|
||||||
|
@ -43,6 +41,7 @@ func generatePassword(length int) string {
|
||||||
charsetLength := len(charset)
|
charsetLength := len(charset)
|
||||||
|
|
||||||
buf := make([]byte, length)
|
buf := make([]byte, length)
|
||||||
|
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
rInt, err := saferand.Int(saferand.Reader, big.NewInt(int64(charsetLength)))
|
rInt, err := saferand.Int(saferand.Reader, big.NewInt(int64(charsetLength)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -190,7 +189,6 @@ cscli machines add MyTestMachine --password MyPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
||||||
var dumpFile string
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
@ -200,7 +198,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
outputFile, err := flags.GetString("file")
|
dumpFile, err := flags.GetString("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -220,7 +218,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
forceAdd, err := flags.GetBool("force")
|
force, err := flags.GetBool("force")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -242,17 +240,28 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*check if file already exists*/
|
/*check if file already exists*/
|
||||||
if outputFile != "" {
|
if dumpFile == "" && csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" {
|
||||||
dumpFile = outputFile
|
credFile := csConfig.API.Client.CredentialsFilePath
|
||||||
} else if csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" {
|
// use the default only if the file does not exist
|
||||||
|
_, err := os.Stat(credFile)
|
||||||
|
switch {
|
||||||
|
case os.IsNotExist(err) || force:
|
||||||
dumpFile = csConfig.API.Client.CredentialsFilePath
|
dumpFile = csConfig.API.Client.CredentialsFilePath
|
||||||
|
case err != nil:
|
||||||
|
return fmt.Errorf("unable to stat '%s': %s", credFile, err)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(`credentials file '%s' already exists: please remove it, use "--force" or specify a different file with "-f" ("-f -" for standard output)`, credFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dumpFile == "" {
|
||||||
|
return fmt.Errorf(`please specify a file to dump credentials to, with -f ("-f -" for standard output)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a password if it's not specified by user
|
// create a password if it's not specified by user
|
||||||
if machinePassword == "" && !interactive {
|
if machinePassword == "" && !interactive {
|
||||||
if !autoAdd {
|
if !autoAdd {
|
||||||
printHelp(cmd)
|
return fmt.Errorf("please specify a password with --password or use --auto")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
machinePassword = generatePassword(passwordLength)
|
machinePassword = generatePassword(passwordLength)
|
||||||
} else if machinePassword == "" && interactive {
|
} else if machinePassword == "" && interactive {
|
||||||
|
@ -262,7 +271,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
||||||
survey.AskOne(qs, &machinePassword)
|
survey.AskOne(qs, &machinePassword)
|
||||||
}
|
}
|
||||||
password := strfmt.Password(machinePassword)
|
password := strfmt.Password(machinePassword)
|
||||||
_, err = dbClient.CreateMachine(&machineID, &password, "", true, forceAdd, types.PasswordAuthType)
|
_, err = dbClient.CreateMachine(&machineID, &password, "", true, force, types.PasswordAuthType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create machine: %s", err)
|
return fmt.Errorf("unable to create machine: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -291,7 +300,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
||||||
if 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)
|
||||||
}
|
}
|
||||||
log.Printf("API credentials dumped to '%s'", dumpFile)
|
log.Printf("API credentials written to '%s'", dumpFile)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s\n", string(apiConfigDump))
|
fmt.Printf("%s\n", string(apiConfigDump))
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ func collectHubItems(hub *cwhub.Hub, itemType string) []byte {
|
||||||
log.Warnf("could not collect %s list: %s", itemType, err)
|
log.Warnf("could not collect %s list: %s", itemType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := listItems(out, []string{itemType}, items); 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()
|
||||||
|
|
|
@ -18,8 +18,8 @@ func listHubItemTable(out io.Writer, title string, items []*cwhub.Item) {
|
||||||
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
status, emo := item.InstallStatus()
|
status := fmt.Sprintf("%v %s", item.State.Emoji(), item.State.Text())
|
||||||
t.AddRow(item.Name, fmt.Sprintf("%v %s", emo, status), item.State.LocalVersion, item.State.LocalPath)
|
t.AddRow(item.Name, status, item.State.LocalVersion, item.State.LocalPath)
|
||||||
}
|
}
|
||||||
renderTableTitle(out, title)
|
renderTableTitle(out, title)
|
||||||
t.Render()
|
t.Render()
|
||||||
|
|
|
@ -262,10 +262,6 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo
|
||||||
return nil, errors.New("You must run at least the API Server or crowdsec")
|
return nil, errors.New("You must run at least the API Server or crowdsec")
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.TestMode && !cConfig.DisableAgent {
|
|
||||||
cConfig.Crowdsec.LintOnly = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if flags.OneShotDSN != "" && flags.SingleFileType == "" {
|
if flags.OneShotDSN != "" && flags.SingleFileType == "" {
|
||||||
return nil, errors.New("-dsn requires a -type argument")
|
return nil, errors.New("-dsn requires a -type argument")
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,13 +58,12 @@ func (s *HTTPPlugin) Notify(ctx context.Context, notification *protobufs.Notific
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for headerName, headerValue := range cfg.Headers {
|
for headerName, headerValue := range cfg.Headers {
|
||||||
logger.Debug(fmt.Sprintf("adding header %s: %s", headerName, headerValue))
|
logger.Debug(fmt.Sprintf("adding header %s: %s", headerName, headerValue))
|
||||||
request.Header.Add(headerName, headerValue)
|
request.Header.Add(headerName, headerValue)
|
||||||
}
|
}
|
||||||
logger.Debug(fmt.Sprintf("making HTTP %s call to %s with body %s", cfg.Method, cfg.URL, notification.Text))
|
logger.Debug(fmt.Sprintf("making HTTP %s call to %s with body %s", cfg.Method, cfg.URL, notification.Text))
|
||||||
resp, err := client.Do(request)
|
resp, err := client.Do(request.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(fmt.Sprintf("Failed to make HTTP request : %s", err))
|
logger.Error(fmt.Sprintf("Failed to make HTTP request : %s", err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (s *SentinelPlugin) Notify(ctx context.Context, notification *protobufs.Not
|
||||||
req.Header.Set("x-ms-date", now)
|
req.Header.Set("x-ms-date", now)
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to send request", "error", err)
|
logger.Error("failed to send request", "error", err)
|
||||||
return &protobufs.Empty{}, err
|
return &protobufs.Empty{}, err
|
||||||
|
|
|
@ -38,10 +38,9 @@ func (n *Notify) Notify(ctx context.Context, notification *protobufs.Notificatio
|
||||||
if cfg.LogLevel != nil && *cfg.LogLevel != "" {
|
if cfg.LogLevel != nil && *cfg.LogLevel != "" {
|
||||||
logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel))
|
logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info(fmt.Sprintf("found notify signal for %s config", notification.Name))
|
logger.Info(fmt.Sprintf("found notify signal for %s config", notification.Name))
|
||||||
logger.Debug(fmt.Sprintf("posting to %s webhook, message %s", cfg.Webhook, notification.Text))
|
logger.Debug(fmt.Sprintf("posting to %s webhook, message %s", cfg.Webhook, notification.Text))
|
||||||
err := slack.PostWebhook(n.ConfigByName[notification.Name].Webhook, &slack.WebhookMessage{
|
err := slack.PostWebhookContext(ctx, n.ConfigByName[notification.Name].Webhook, &slack.WebhookMessage{
|
||||||
Text: notification.Text,
|
Text: notification.Text,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -65,7 +65,7 @@ func (s *Splunk) Notify(ctx context.Context, notification *protobufs.Notificatio
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf("Splunk %s", cfg.Token))
|
req.Header.Add("Authorization", fmt.Sprintf("Splunk %s", cfg.Token))
|
||||||
logger.Debug(fmt.Sprintf("posting event %s to %s", string(data), req.URL))
|
logger.Debug(fmt.Sprintf("posting event %s to %s", string(data), req.URL))
|
||||||
resp, err := s.Client.Do(req)
|
resp, err := s.Client.Do(req.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &protobufs.Empty{}, err
|
return &protobufs.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,7 +202,7 @@ if isfalse "$DISABLE_LOCAL_API"; then
|
||||||
# if the db is persistent but the credentials are not, we need to
|
# if the db is persistent but the credentials are not, we need to
|
||||||
# delete the old machine to generate new credentials
|
# delete the old machine to generate new credentials
|
||||||
cscli machines delete "$CUSTOM_HOSTNAME" >/dev/null 2>&1 || true
|
cscli machines delete "$CUSTOM_HOSTNAME" >/dev/null 2>&1 || true
|
||||||
cscli machines add "$CUSTOM_HOSTNAME" --auto
|
cscli machines add "$CUSTOM_HOSTNAME" --auto --force
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -70,6 +70,7 @@ require (
|
||||||
github.com/prometheus/client_model v0.4.0
|
github.com/prometheus/client_model v0.4.0
|
||||||
github.com/prometheus/prom2json v1.3.0
|
github.com/prometheus/prom2json v1.3.0
|
||||||
github.com/r3labs/diff/v2 v2.14.1
|
github.com/r3labs/diff/v2 v2.14.1
|
||||||
|
github.com/sanity-io/litter v1.5.5
|
||||||
github.com/segmentio/kafka-go v0.4.45
|
github.com/segmentio/kafka-go v0.4.45
|
||||||
github.com/shirou/gopsutil/v3 v3.23.5
|
github.com/shirou/gopsutil/v3 v3.23.5
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -114,6 +114,7 @@ github.com/crowdsecurity/grokky v0.2.1 h1:t4VYnDlAd0RjDM2SlILalbwfCrQxtJSMGdQOR0
|
||||||
github.com/crowdsecurity/grokky v0.2.1/go.mod h1:33usDIYzGDsgX1kHAThCbseso6JuWNJXOzRQDGXHtWM=
|
github.com/crowdsecurity/grokky v0.2.1/go.mod h1:33usDIYzGDsgX1kHAThCbseso6JuWNJXOzRQDGXHtWM=
|
||||||
github.com/crowdsecurity/machineid v1.0.2 h1:wpkpsUghJF8Khtmn/tg6GxgdhLA1Xflerh5lirI+bdc=
|
github.com/crowdsecurity/machineid v1.0.2 h1:wpkpsUghJF8Khtmn/tg6GxgdhLA1Xflerh5lirI+bdc=
|
||||||
github.com/crowdsecurity/machineid v1.0.2/go.mod h1:XWUSlnS0R0+u/JK5ulidwlbceNT3ZOCKteoVQEn6Luo=
|
github.com/crowdsecurity/machineid v1.0.2/go.mod h1:XWUSlnS0R0+u/JK5ulidwlbceNT3ZOCKteoVQEn6Luo=
|
||||||
|
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -570,6 +571,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
|
@ -609,6 +611,8 @@ github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OK
|
||||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||||
|
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/segmentio/kafka-go v0.4.45 h1:prqrZp1mMId4kI6pyPolkLsH6sWOUmDxmmucbL4WS6E=
|
github.com/segmentio/kafka-go v0.4.45 h1:prqrZp1mMId4kI6pyPolkLsH6sWOUmDxmmucbL4WS6E=
|
||||||
github.com/segmentio/kafka-go v0.4.45/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
|
github.com/segmentio/kafka-go v0.4.45/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
|
||||||
|
@ -646,6 +650,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
|
|
@ -106,13 +106,13 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
var flushScheduler *gocron.Scheduler
|
var flushScheduler *gocron.Scheduler
|
||||||
dbClient, err := database.NewClient(config.DbConfig)
|
dbClient, err := database.NewClient(config.DbConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &APIServer{}, fmt.Errorf("unable to init database client: %w", err)
|
return nil, fmt.Errorf("unable to init database client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DbConfig.Flush != nil {
|
if config.DbConfig.Flush != nil {
|
||||||
flushScheduler, err = dbClient.StartFlushScheduler(config.DbConfig.Flush)
|
flushScheduler, err = dbClient.StartFlushScheduler(config.DbConfig.Flush)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &APIServer{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
|
|
||||||
if config.TrustedProxies != nil && config.UseForwardedForHeaders {
|
if config.TrustedProxies != nil && config.UseForwardedForHeaders {
|
||||||
if err := router.SetTrustedProxies(*config.TrustedProxies); err != nil {
|
if err := router.SetTrustedProxies(*config.TrustedProxies); err != nil {
|
||||||
return &APIServer{}, fmt.Errorf("while setting trusted_proxies: %w", err)
|
return nil, fmt.Errorf("while setting trusted_proxies: %w", err)
|
||||||
}
|
}
|
||||||
router.ForwardedByClientIP = true
|
router.ForwardedByClientIP = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -215,7 +215,7 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
log.Printf("Loading CAPI manager")
|
log.Printf("Loading CAPI manager")
|
||||||
apiClient, err = NewAPIC(config.OnlineClient, dbClient, config.ConsoleConfig, config.CapiWhitelists)
|
apiClient, err = NewAPIC(config.OnlineClient, dbClient, config.ConsoleConfig, config.CapiWhitelists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &APIServer{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Infof("CAPI manager configured successfully")
|
log.Infof("CAPI manager configured successfully")
|
||||||
isMachineEnrolled = isEnrolled(apiClient.apiClient)
|
isMachineEnrolled = isEnrolled(apiClient.apiClient)
|
||||||
|
@ -225,7 +225,7 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
log.Infof("Machine is enrolled in the console, Loading PAPI Client")
|
log.Infof("Machine is enrolled in the console, Loading PAPI Client")
|
||||||
papiClient, err = NewPAPI(apiClient, dbClient, config.ConsoleConfig, *config.PapiLogLevel)
|
papiClient, err = NewPAPI(apiClient, dbClient, config.ConsoleConfig, *config.PapiLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &APIServer{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
controller.DecisionDeleteChan = papiClient.Channels.DeleteDecisionChannel
|
controller.DecisionDeleteChan = papiClient.Channels.DeleteDecisionChannel
|
||||||
} else {
|
} else {
|
||||||
|
@ -241,7 +241,7 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
if trustedIPs, err := config.GetTrustedIPs(); err == nil {
|
if trustedIPs, err := config.GetTrustedIPs(); err == nil {
|
||||||
controller.TrustedIPs = trustedIPs
|
controller.TrustedIPs = trustedIPs
|
||||||
} else {
|
} else {
|
||||||
return &APIServer{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &APIServer{
|
return &APIServer{
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/go-cs-lib/cstest"
|
||||||
"github.com/crowdsecurity/go-cs-lib/version"
|
"github.com/crowdsecurity/go-cs-lib/version"
|
||||||
|
|
||||||
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
|
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
|
||||||
|
@ -295,8 +296,8 @@ func TestWithWrongDBConfig(t *testing.T) {
|
||||||
config.API.Server.DbConfig.Type = "test"
|
config.API.Server.DbConfig.Type = "test"
|
||||||
apiServer, err := NewServer(config.API.Server)
|
apiServer, err := NewServer(config.API.Server)
|
||||||
|
|
||||||
assert.Equal(t, apiServer, &APIServer{})
|
cstest.RequireErrorContains(t, err, "unable to init database client: unknown database type 'test'")
|
||||||
assert.Equal(t, "unable to init database client: unknown database type 'test'", err.Error())
|
assert.Nil(t, apiServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithWrongFlushConfig(t *testing.T) {
|
func TestWithWrongFlushConfig(t *testing.T) {
|
||||||
|
@ -305,8 +306,8 @@ func TestWithWrongFlushConfig(t *testing.T) {
|
||||||
config.API.Server.DbConfig.Flush.MaxItems = &maxItems
|
config.API.Server.DbConfig.Flush.MaxItems = &maxItems
|
||||||
apiServer, err := NewServer(config.API.Server)
|
apiServer, err := NewServer(config.API.Server)
|
||||||
|
|
||||||
assert.Equal(t, apiServer, &APIServer{})
|
cstest.RequireErrorContains(t, err, "max_items can't be zero or negative number")
|
||||||
assert.Equal(t, "max_items can't be zero or negative number", err.Error())
|
assert.Nil(t, apiServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnknownPath(t *testing.T) {
|
func TestUnknownPath(t *testing.T) {
|
||||||
|
|
|
@ -55,132 +55,110 @@ func HashSHA512(str string) string {
|
||||||
return hashStr
|
return hashStr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIKey) MiddlewareFunc() gin.HandlerFunc {
|
func (a *APIKey) authTLS(c *gin.Context, logger *log.Entry) *ent.Bouncer {
|
||||||
return func(c *gin.Context) {
|
|
||||||
var bouncer *ent.Bouncer
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if c.Request.TLS != nil && len(c.Request.TLS.PeerCertificates) > 0 {
|
|
||||||
if a.TlsAuth == nil {
|
if a.TlsAuth == nil {
|
||||||
log.WithField("ip", c.ClientIP()).Error("TLS Auth is not configured but client presented a certificate")
|
logger.Error("TLS Auth is not configured but client presented a certificate")
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
return nil
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
validCert, extractedCN, err := a.TlsAuth.ValidateCert(c)
|
validCert, extractedCN, err := a.TlsAuth.ValidateCert(c)
|
||||||
if !validCert {
|
if !validCert {
|
||||||
log.WithField("ip", c.ClientIP()).Errorf("invalid client certificate: %s", err)
|
logger.Errorf("invalid client certificate: %s", err)
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
return nil
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithField("ip", c.ClientIP()).Error(err)
|
logger.Error(err)
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
return nil
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger = logger.WithFields(log.Fields{
|
||||||
|
"cn": extractedCN,
|
||||||
|
})
|
||||||
|
|
||||||
bouncerName := fmt.Sprintf("%s@%s", extractedCN, c.ClientIP())
|
bouncerName := fmt.Sprintf("%s@%s", extractedCN, c.ClientIP())
|
||||||
bouncer, err = a.DbClient.SelectBouncerByName(bouncerName)
|
bouncer, err := a.DbClient.SelectBouncerByName(bouncerName)
|
||||||
|
|
||||||
//This is likely not the proper way, but isNotFound does not seem to work
|
//This is likely not the proper way, but isNotFound does not seem to work
|
||||||
if err != nil && strings.Contains(err.Error(), "bouncer not found") {
|
if err != nil && strings.Contains(err.Error(), "bouncer not found") {
|
||||||
//Because we have a valid cert, automatically create the bouncer in the database if it does not exist
|
//Because we have a valid cert, automatically create the bouncer in the database if it does not exist
|
||||||
//Set a random API key, but it will never be used
|
//Set a random API key, but it will never be used
|
||||||
apiKey, err := GenerateAPIKey(dummyAPIKeySize)
|
apiKey, err := GenerateAPIKey(dummyAPIKeySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("error generating mock api key: %s", err)
|
||||||
"ip": c.ClientIP(),
|
return nil
|
||||||
"cn": extractedCN,
|
|
||||||
}).Errorf("error generating mock api key: %s", err)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
log.WithFields(log.Fields{
|
logger.Infof("Creating bouncer %s", bouncerName)
|
||||||
"ip": c.ClientIP(),
|
|
||||||
"cn": extractedCN,
|
|
||||||
}).Infof("Creating bouncer %s", bouncerName)
|
|
||||||
bouncer, err = a.DbClient.CreateBouncer(bouncerName, c.ClientIP(), HashSHA512(apiKey), types.TlsAuthType)
|
bouncer, err = a.DbClient.CreateBouncer(bouncerName, c.ClientIP(), HashSHA512(apiKey), types.TlsAuthType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("while creating bouncer db entry: %s", err)
|
||||||
"ip": c.ClientIP(),
|
return nil
|
||||||
"cn": extractedCN,
|
|
||||||
}).Errorf("creating bouncer db entry : %s", err)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
//error while selecting bouncer
|
//error while selecting bouncer
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("while selecting bouncers: %s", err)
|
||||||
"ip": c.ClientIP(),
|
return nil
|
||||||
"cn": extractedCN,
|
|
||||||
}).Errorf("while selecting bouncers: %s", err)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
} else if bouncer.AuthType != types.TlsAuthType {
|
} else if bouncer.AuthType != types.TlsAuthType {
|
||||||
//bouncer was found in DB
|
//bouncer was found in DB
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("bouncer isn't allowed to auth by TLS")
|
||||||
"ip": c.ClientIP(),
|
return nil
|
||||||
"cn": extractedCN,
|
|
||||||
}).Errorf("bouncer isn't allowed to auth by TLS")
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} else {
|
return bouncer
|
||||||
//API Key Authentication
|
}
|
||||||
|
|
||||||
|
func (a *APIKey) authPlain(c *gin.Context, logger *log.Entry) *ent.Bouncer {
|
||||||
val, ok := c.Request.Header[APIKeyHeader]
|
val, ok := c.Request.Header[APIKeyHeader]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("API key not found")
|
||||||
"ip": c.ClientIP(),
|
return nil
|
||||||
}).Errorf("API key not found")
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
hashStr := HashSHA512(val[0])
|
hashStr := HashSHA512(val[0])
|
||||||
bouncer, err = a.DbClient.SelectBouncer(hashStr)
|
|
||||||
|
bouncer, err := a.DbClient.SelectBouncer(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("while fetching bouncer info: %s", err)
|
||||||
"ip": c.ClientIP(),
|
return nil
|
||||||
}).Errorf("while fetching bouncer info: %s", err)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if bouncer.AuthType != types.ApiKeyAuthType {
|
if bouncer.AuthType != types.ApiKeyAuthType {
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("bouncer %s attempted to login using an API key but it is configured to auth with %s", bouncer.Name, bouncer.AuthType)
|
||||||
"ip": c.ClientIP(),
|
return nil
|
||||||
}).Errorf("bouncer %s attempted to login using an API key but it is configured to auth with %s", bouncer.Name, bouncer.AuthType)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bouncer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIKey) MiddlewareFunc() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var bouncer *ent.Bouncer
|
||||||
|
|
||||||
|
logger := log.WithFields(log.Fields{
|
||||||
|
"ip": c.ClientIP(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if c.Request.TLS != nil && len(c.Request.TLS.PeerCertificates) > 0 {
|
||||||
|
bouncer = a.authTLS(c, logger)
|
||||||
|
} else {
|
||||||
|
bouncer = a.authPlain(c, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bouncer == nil {
|
if bouncer == nil {
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"ip": c.ClientIP(),
|
|
||||||
}).Errorf("bouncer not found")
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//maybe we want to store the whole bouncer object in the context instead, this would avoid another db query
|
logger = logger.WithFields(log.Fields{
|
||||||
//in StreamDecision
|
"name": bouncer.Name,
|
||||||
|
})
|
||||||
|
|
||||||
|
// maybe we want to store the whole bouncer object in the context instead, this would avoid another db query
|
||||||
|
// in StreamDecision
|
||||||
c.Set("BOUNCER_NAME", bouncer.Name)
|
c.Set("BOUNCER_NAME", bouncer.Name)
|
||||||
c.Set("BOUNCER_HASHED_KEY", bouncer.APIKey)
|
c.Set("BOUNCER_HASHED_KEY", bouncer.APIKey)
|
||||||
|
|
||||||
if bouncer.IPAddress == "" {
|
if bouncer.IPAddress == "" {
|
||||||
err = a.DbClient.UpdateBouncerIP(c.ClientIP(), bouncer.ID)
|
if err := a.DbClient.UpdateBouncerIP(c.ClientIP(), bouncer.ID); err != nil {
|
||||||
if err != nil {
|
logger.Errorf("Failed to update ip address for '%s': %s\n", bouncer.Name, err)
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"ip": c.ClientIP(),
|
|
||||||
"name": bouncer.Name,
|
|
||||||
}).Errorf("Failed to update ip address for '%s': %s\n", bouncer.Name, err)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
|
@ -189,12 +167,8 @@ func (a *APIKey) MiddlewareFunc() gin.HandlerFunc {
|
||||||
|
|
||||||
if bouncer.IPAddress != c.ClientIP() && bouncer.IPAddress != "" {
|
if bouncer.IPAddress != c.ClientIP() && bouncer.IPAddress != "" {
|
||||||
log.Warningf("new IP address detected for bouncer '%s': %s (old: %s)", bouncer.Name, c.ClientIP(), bouncer.IPAddress)
|
log.Warningf("new IP address detected for bouncer '%s': %s (old: %s)", bouncer.Name, c.ClientIP(), bouncer.IPAddress)
|
||||||
err = a.DbClient.UpdateBouncerIP(c.ClientIP(), bouncer.ID)
|
if err := a.DbClient.UpdateBouncerIP(c.ClientIP(), bouncer.ID); err != nil {
|
||||||
if err != nil {
|
logger.Errorf("Failed to update ip address for '%s': %s\n", bouncer.Name, err)
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"ip": c.ClientIP(),
|
|
||||||
"name": bouncer.Name,
|
|
||||||
}).Errorf("Failed to update ip address for '%s': %s\n", bouncer.Name, err)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
c.JSON(http.StatusForbidden, gin.H{"message": "access forbidden"})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
|
@ -202,21 +176,14 @@ func (a *APIKey) MiddlewareFunc() gin.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
useragent := strings.Split(c.Request.UserAgent(), "/")
|
useragent := strings.Split(c.Request.UserAgent(), "/")
|
||||||
|
|
||||||
if len(useragent) != 2 {
|
if len(useragent) != 2 {
|
||||||
log.WithFields(log.Fields{
|
logger.Warningf("bad user agent '%s'", c.Request.UserAgent())
|
||||||
"ip": c.ClientIP(),
|
|
||||||
"name": bouncer.Name,
|
|
||||||
}).Warningf("bad user agent '%s'", c.Request.UserAgent())
|
|
||||||
useragent = []string{c.Request.UserAgent(), "N/A"}
|
useragent = []string{c.Request.UserAgent(), "N/A"}
|
||||||
}
|
}
|
||||||
|
|
||||||
if bouncer.Version != useragent[1] || bouncer.Type != useragent[0] {
|
if bouncer.Version != useragent[1] || bouncer.Type != useragent[0] {
|
||||||
if err := a.DbClient.UpdateBouncerTypeAndVersion(useragent[0], useragent[1], bouncer.ID); err != nil {
|
if err := a.DbClient.UpdateBouncerTypeAndVersion(useragent[0], useragent[1], bouncer.ID); err != nil {
|
||||||
log.WithFields(log.Fields{
|
logger.Errorf("failed to update bouncer version and type: %s", err)
|
||||||
"ip": c.ClientIP(),
|
|
||||||
"name": bouncer.Name,
|
|
||||||
}).Errorf("failed to update bouncer version and type: %s", err)
|
|
||||||
c.JSON(http.StatusForbidden, gin.H{"message": "bad user agent"})
|
c.JSON(http.StatusForbidden, gin.H{"message": "bad user agent"})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
|
|
|
@ -14,7 +14,7 @@ func NewMiddlewares(dbClient *database.Client) (*Middlewares, error) {
|
||||||
|
|
||||||
ret.JWT, err = NewJWT(dbClient)
|
ret.JWT, err = NewJWT(dbClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &Middlewares{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.APIKey = NewAPIKey(dbClient)
|
ret.APIKey = NewAPIKey(dbClient)
|
||||||
|
|
|
@ -23,7 +23,6 @@ type CrowdsecServiceCfg struct {
|
||||||
BucketsRoutinesCount int `yaml:"buckets_routines"`
|
BucketsRoutinesCount int `yaml:"buckets_routines"`
|
||||||
OutputRoutinesCount int `yaml:"output_routines"`
|
OutputRoutinesCount int `yaml:"output_routines"`
|
||||||
SimulationConfig *SimulationConfig `yaml:"-"`
|
SimulationConfig *SimulationConfig `yaml:"-"`
|
||||||
LintOnly bool `yaml:"-"` // if set to true, exit after loading configs
|
|
||||||
BucketStateFile string `yaml:"state_input_file,omitempty"` // if we need to unserialize buckets at start
|
BucketStateFile string `yaml:"state_input_file,omitempty"` // if we need to unserialize buckets at start
|
||||||
BucketStateDumpDir string `yaml:"state_output_dir,omitempty"` // if we need to unserialize buckets on shutdown
|
BucketStateDumpDir string `yaml:"state_output_dir,omitempty"` // if we need to unserialize buckets on shutdown
|
||||||
BucketsGCEnabled bool `yaml:"-"` // we need to garbage collect buckets when in forensic mode
|
BucketsGCEnabled bool `yaml:"-"` // we need to garbage collect buckets when in forensic mode
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (m *GRPCClient) Notify(ctx context.Context, notification *protobufs.Notific
|
||||||
done := make(chan error)
|
done := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
_, err := m.client.Notify(
|
_, err := m.client.Notify(
|
||||||
context.Background(), &protobufs.Notification{Text: notification.Text, Name: notification.Name},
|
ctx, &protobufs.Notification{Text: notification.Text, Name: notification.Name},
|
||||||
)
|
)
|
||||||
done <- err
|
done <- err
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -109,7 +109,7 @@ func (h *Hub) ItemStats() []string {
|
||||||
loaded += fmt.Sprintf("%d %s, ", len(h.GetItemMap(itemType)), itemType)
|
loaded += fmt.Sprintf("%d %s, ", len(h.GetItemMap(itemType)), itemType)
|
||||||
|
|
||||||
for _, item := range h.GetItemMap(itemType) {
|
for _, item := range h.GetItemMap(itemType) {
|
||||||
if item.IsLocal() {
|
if item.State.IsLocal() {
|
||||||
local++
|
local++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,48 @@ type ItemState struct {
|
||||||
BelongsToCollections []string `json:"belongs_to_collections,omitempty" yaml:"belongs_to_collections,omitempty"`
|
BelongsToCollections []string `json:"belongs_to_collections,omitempty" yaml:"belongs_to_collections,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLocal returns true if the item has been create by a user (not downloaded from the hub).
|
||||||
|
func (s *ItemState) IsLocal() bool {
|
||||||
|
return s.Installed && !s.Downloaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text returns the status of the item as a string (eg. "enabled,update-available").
|
||||||
|
func (s *ItemState) Text() string {
|
||||||
|
ret := "disabled"
|
||||||
|
|
||||||
|
if s.Installed {
|
||||||
|
ret = "enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.IsLocal() {
|
||||||
|
ret += ",local"
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Tainted {
|
||||||
|
ret += ",tainted"
|
||||||
|
} else if !s.UpToDate && !s.IsLocal() {
|
||||||
|
ret += ",update-available"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emoji returns the status of the item as an emoji (eg. emoji.Warning).
|
||||||
|
func (s *ItemState) Emoji() emoji.Emoji {
|
||||||
|
switch {
|
||||||
|
case s.IsLocal():
|
||||||
|
return emoji.House
|
||||||
|
case !s.Installed:
|
||||||
|
return emoji.Prohibited
|
||||||
|
case s.Tainted || (!s.UpToDate && !s.IsLocal()):
|
||||||
|
return emoji.Warning
|
||||||
|
case s.Installed:
|
||||||
|
return emoji.CheckMark
|
||||||
|
default:
|
||||||
|
return emoji.QuestionMark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Item is created from an index file and enriched with local info.
|
// Item is created from an index file and enriched with local info.
|
||||||
type Item struct {
|
type Item struct {
|
||||||
hub *Hub // back pointer to the hub, to retrieve other items and call install/remove methods
|
hub *Hub // back pointer to the hub, to retrieve other items and call install/remove methods
|
||||||
|
@ -111,11 +153,6 @@ func (i *Item) HasSubItems() bool {
|
||||||
return i.Type == COLLECTIONS
|
return i.Type == COLLECTIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLocal returns true if the item has been create by a user (not downloaded from the hub).
|
|
||||||
func (i *Item) IsLocal() bool {
|
|
||||||
return i.State.Installed && !i.State.Downloaded
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON is used to prepare the output for "cscli ... inspect -o json".
|
// MarshalJSON is used to prepare the output for "cscli ... inspect -o json".
|
||||||
// It must not use a pointer receiver.
|
// It must not use a pointer receiver.
|
||||||
func (i Item) MarshalJSON() ([]byte, error) {
|
func (i Item) MarshalJSON() ([]byte, error) {
|
||||||
|
@ -143,7 +180,7 @@ func (i Item) MarshalJSON() ([]byte, error) {
|
||||||
UpToDate: i.State.UpToDate,
|
UpToDate: i.State.UpToDate,
|
||||||
Tainted: i.State.Tainted,
|
Tainted: i.State.Tainted,
|
||||||
BelongsToCollections: i.State.BelongsToCollections,
|
BelongsToCollections: i.State.BelongsToCollections,
|
||||||
Local: i.IsLocal(),
|
Local: i.State.IsLocal(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +196,7 @@ func (i Item) MarshalYAML() (interface{}, error) {
|
||||||
}{
|
}{
|
||||||
Alias: Alias(i),
|
Alias: Alias(i),
|
||||||
State: i.State,
|
State: i.State,
|
||||||
Local: i.IsLocal(),
|
Local: i.State.IsLocal(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,48 +361,6 @@ func (i *Item) descendants() ([]*Item, error) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstallStatus returns the status of the item as a string and an emoji
|
|
||||||
// (eg. "enabled,update-available" and emoji.Warning).
|
|
||||||
func (i *Item) InstallStatus() (string, emoji.Emoji) {
|
|
||||||
status := "disabled"
|
|
||||||
ok := false
|
|
||||||
|
|
||||||
if i.State.Installed {
|
|
||||||
ok = true
|
|
||||||
status = "enabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
managed := true
|
|
||||||
if i.IsLocal() {
|
|
||||||
managed = false
|
|
||||||
status += ",local"
|
|
||||||
}
|
|
||||||
|
|
||||||
warning := false
|
|
||||||
if i.State.Tainted {
|
|
||||||
warning = true
|
|
||||||
status += ",tainted"
|
|
||||||
} else if !i.State.UpToDate && !i.IsLocal() {
|
|
||||||
warning = true
|
|
||||||
status += ",update-available"
|
|
||||||
}
|
|
||||||
|
|
||||||
emo := emoji.QuestionMark
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case !managed:
|
|
||||||
emo = emoji.House
|
|
||||||
case !i.State.Installed:
|
|
||||||
emo = emoji.Prohibited
|
|
||||||
case warning:
|
|
||||||
emo = emoji.Warning
|
|
||||||
case ok:
|
|
||||||
emo = emoji.CheckMark
|
|
||||||
}
|
|
||||||
|
|
||||||
return status, emo
|
|
||||||
}
|
|
||||||
|
|
||||||
// versionStatus returns the status of the item version compared to the hub version.
|
// versionStatus returns the status of the item version compared to the hub version.
|
||||||
// semver requires the 'v' prefix.
|
// semver requires the 'v' prefix.
|
||||||
func (i *Item) versionStatus() int {
|
func (i *Item) versionStatus() int {
|
||||||
|
|
|
@ -23,7 +23,7 @@ func TestItemStatus(t *testing.T) {
|
||||||
item.State.Tainted = false
|
item.State.Tainted = false
|
||||||
item.State.Downloaded = true
|
item.State.Downloaded = true
|
||||||
|
|
||||||
txt, _ := item.InstallStatus()
|
txt := item.State.Text()
|
||||||
require.Equal(t, "enabled,update-available", txt)
|
require.Equal(t, "enabled,update-available", txt)
|
||||||
|
|
||||||
item.State.Installed = true
|
item.State.Installed = true
|
||||||
|
@ -31,7 +31,7 @@ func TestItemStatus(t *testing.T) {
|
||||||
item.State.Tainted = false
|
item.State.Tainted = false
|
||||||
item.State.Downloaded = false
|
item.State.Downloaded = false
|
||||||
|
|
||||||
txt, _ = item.InstallStatus()
|
txt = item.State.Text()
|
||||||
require.Equal(t, "enabled,local", txt)
|
require.Equal(t, "enabled,local", txt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ func (i *Item) enable() error {
|
||||||
return fmt.Errorf("%s is tainted, won't enable unless --force", i.Name)
|
return fmt.Errorf("%s is tainted, won't enable unless --force", i.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.IsLocal() {
|
if i.State.IsLocal() {
|
||||||
return fmt.Errorf("%s is local, won't enable", i.Name)
|
return fmt.Errorf("%s is local, won't enable", i.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ func (i *Item) disable(purge bool, force bool) (bool, error) {
|
||||||
|
|
||||||
// Remove disables the item, optionally removing the downloaded content.
|
// Remove disables the item, optionally removing the downloaded content.
|
||||||
func (i *Item) Remove(purge bool, force bool) (bool, error) {
|
func (i *Item) Remove(purge bool, force bool) (bool, error) {
|
||||||
if i.IsLocal() {
|
if i.State.IsLocal() {
|
||||||
log.Warningf("%s is a local item, please delete manually", i.Name)
|
log.Warningf("%s is a local item, please delete manually", i.Name)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
func (i *Item) Upgrade(force bool) (bool, error) {
|
func (i *Item) Upgrade(force bool) (bool, error) {
|
||||||
updated := false
|
updated := false
|
||||||
|
|
||||||
if i.IsLocal() {
|
if i.State.IsLocal() {
|
||||||
log.Infof("not upgrading %s: local item", i.Name)
|
log.Infof("not upgrading %s: local item", i.Name)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ func (i *Item) fetch() ([]byte, error) {
|
||||||
|
|
||||||
// download downloads the item from the hub and writes it to the hub directory.
|
// download downloads the item from the hub and writes it to the hub directory.
|
||||||
func (i *Item) download(overwrite bool) (string, error) {
|
func (i *Item) download(overwrite bool) (string, error) {
|
||||||
if i.IsLocal() {
|
if i.State.IsLocal() {
|
||||||
return "", fmt.Errorf("%s is local, can't download", i.Name)
|
return "", fmt.Errorf("%s is local, can't download", i.Name)
|
||||||
}
|
}
|
||||||
// if user didn't --force, don't overwrite local, tainted, up-to-date files
|
// if user didn't --force, don't overwrite local, tainted, up-to-date files
|
||||||
|
|
|
@ -426,7 +426,7 @@ func (h *Hub) localSync() error {
|
||||||
case versionFuture:
|
case versionFuture:
|
||||||
warnings = append(warnings, fmt.Sprintf("collection %s is in the future (currently:%s, latest:%s)", item.Name, item.State.LocalVersion, item.Version))
|
warnings = append(warnings, fmt.Sprintf("collection %s is in the future (currently:%s, latest:%s)", item.Name, item.State.LocalVersion, item.Version))
|
||||||
case versionUnknown:
|
case versionUnknown:
|
||||||
if !item.IsLocal() {
|
if !item.State.IsLocal() {
|
||||||
warnings = append(warnings, fmt.Sprintf("collection %s is tainted (latest:%s)", item.Name, item.Version))
|
warnings = append(warnings, fmt.Sprintf("collection %s is tainted (latest:%s)", item.Name, item.Version))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func (c *Client) SelectBouncer(apiKeyHash string) (*ent.Bouncer, error) {
|
func (c *Client) SelectBouncer(apiKeyHash string) (*ent.Bouncer, error) {
|
||||||
result, err := c.Ent.Bouncer.Query().Where(bouncer.APIKeyEQ(apiKeyHash)).First(c.CTX)
|
result, err := c.Ent.Bouncer.Query().Where(bouncer.APIKeyEQ(apiKeyHash)).First(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &ent.Bouncer{}, errors.Wrapf(QueryFail, "select bouncer: %s", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
@ -22,7 +22,7 @@ func (c *Client) SelectBouncer(apiKeyHash string) (*ent.Bouncer, error) {
|
||||||
func (c *Client) SelectBouncerByName(bouncerName string) (*ent.Bouncer, error) {
|
func (c *Client) SelectBouncerByName(bouncerName string) (*ent.Bouncer, error) {
|
||||||
result, err := c.Ent.Bouncer.Query().Where(bouncer.NameEQ(bouncerName)).First(c.CTX)
|
result, err := c.Ent.Bouncer.Query().Where(bouncer.NameEQ(bouncerName)).First(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &ent.Bouncer{}, errors.Wrapf(QueryFail, "select bouncer: %s", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
@ -31,7 +31,7 @@ func (c *Client) SelectBouncerByName(bouncerName string) (*ent.Bouncer, error) {
|
||||||
func (c *Client) ListBouncers() ([]*ent.Bouncer, error) {
|
func (c *Client) ListBouncers() ([]*ent.Bouncer, error) {
|
||||||
result, err := c.Ent.Bouncer.Query().All(c.CTX)
|
result, err := c.Ent.Bouncer.Query().All(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*ent.Bouncer{}, errors.Wrapf(QueryFail, "listing bouncer: %s", err)
|
return nil, errors.Wrapf(QueryFail, "listing bouncers: %s", err)
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ const CapiListsMachineID = types.ListOrigin
|
||||||
func (c *Client) CreateMachine(machineID *string, password *strfmt.Password, ipAddress string, isValidated bool, force bool, authType string) (*ent.Machine, error) {
|
func (c *Client) CreateMachine(machineID *string, password *strfmt.Password, ipAddress string, isValidated bool, force bool, authType string) (*ent.Machine, error) {
|
||||||
hashPassword, err := bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
|
hashPassword, err := bcrypt.GenerateFromPassword([]byte(*password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Warningf("CreateMachine : %s", err)
|
c.Log.Warningf("CreateMachine: %s", err)
|
||||||
return nil, errors.Wrap(HashError, "")
|
return nil, HashError
|
||||||
}
|
}
|
||||||
|
|
||||||
machineExist, err := c.Ent.Machine.
|
machineExist, err := c.Ent.Machine.
|
||||||
|
@ -78,7 +78,7 @@ func (c *Client) QueryMachineByID(machineID string) (*ent.Machine, error) {
|
||||||
func (c *Client) ListMachines() ([]*ent.Machine, error) {
|
func (c *Client) ListMachines() ([]*ent.Machine, error) {
|
||||||
machines, err := c.Ent.Machine.Query().All(c.CTX)
|
machines, err := c.Ent.Machine.Query().All(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*ent.Machine{}, errors.Wrapf(QueryFail, "listing machines: %s", err)
|
return nil, errors.Wrapf(QueryFail, "listing machines: %s", err)
|
||||||
}
|
}
|
||||||
return machines, nil
|
return machines, nil
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ func (c *Client) QueryPendingMachine() ([]*ent.Machine, error) {
|
||||||
machines, err = c.Ent.Machine.Query().Where(machine.IsValidatedEQ(false)).All(c.CTX)
|
machines, err = c.Ent.Machine.Query().Where(machine.IsValidatedEQ(false)).All(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Log.Warningf("QueryPendingMachine : %s", err)
|
c.Log.Warningf("QueryPendingMachine : %s", err)
|
||||||
return []*ent.Machine{}, errors.Wrapf(QueryFail, "querying pending machines: %s", err)
|
return nil, errors.Wrapf(QueryFail, "querying pending machines: %s", err)
|
||||||
}
|
}
|
||||||
return machines, nil
|
return machines, nil
|
||||||
}
|
}
|
||||||
|
@ -190,12 +190,13 @@ func (c *Client) IsMachineRegistered(machineID string) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
if len(exist) > 1 {
|
if len(exist) > 1 {
|
||||||
return false, fmt.Errorf("More than one item with the same machineID in database")
|
return false, fmt.Errorf("more than one item with the same machineID in database")
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) QueryLastValidatedHeartbeatLT(t time.Time) ([]*ent.Machine, error) {
|
func (c *Client) QueryLastValidatedHeartbeatLT(t time.Time) ([]*ent.Machine, error) {
|
||||||
return c.Ent.Machine.Query().Where(machine.LastHeartbeatLT(t), machine.IsValidatedEQ(true)).All(c.CTX)
|
return c.Ent.Machine.Query().Where(machine.LastHeartbeatLT(t), machine.IsValidatedEQ(true)).All(c.CTX)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
@ -93,14 +92,6 @@ func (c *Container) Create() error {
|
||||||
Tty: true,
|
Tty: true,
|
||||||
Env: env,
|
Env: env,
|
||||||
}
|
}
|
||||||
os := runtime.GOOS
|
|
||||||
switch os {
|
|
||||||
case "linux":
|
|
||||||
case "windows", "darwin":
|
|
||||||
return fmt.Errorf("mac and windows are not supported yet")
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("OS '%s' is not supported", os)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("creating container '%s'", c.Name)
|
log.Infof("creating container '%s'", c.Name)
|
||||||
resp, err := c.CLI.ContainerCreate(ctx, dockerConfig, hostConfig, nil, nil, c.Name)
|
resp, err := c.CLI.ContainerCreate(ctx, dockerConfig, hostConfig, nil, nil, c.Name)
|
||||||
|
|
|
@ -227,3 +227,16 @@ teardown() {
|
||||||
assert_stderr --partial "crowdsec init: while loading acquisition config: no datasource enabled"
|
assert_stderr --partial "crowdsec init: while loading acquisition config: no datasource enabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "crowdsec -t (error in acquisition file)" {
|
||||||
|
# we can verify the acquisition configuration without running crowdsec
|
||||||
|
ACQUIS_YAML=$(config_get '.crowdsec_service.acquisition_path')
|
||||||
|
config_set "$ACQUIS_YAML" 'del(.filenames)'
|
||||||
|
|
||||||
|
rune -1 wait-for "${CROWDSEC}"
|
||||||
|
assert_stderr --partial "failed to configure datasource file: no filename or filenames configuration provided"
|
||||||
|
|
||||||
|
config_set "$ACQUIS_YAML" '.filenames=["file.log"]'
|
||||||
|
config_set "$ACQUIS_YAML" '.meh=3'
|
||||||
|
rune -1 wait-for "${CROWDSEC}"
|
||||||
|
assert_stderr --partial "field meh not found in type fileacquisition.FileConfiguration"
|
||||||
|
}
|
||||||
|
|
|
@ -108,6 +108,20 @@ teardown() {
|
||||||
rune -0 cscli config show -o json
|
rune -0 cscli config show -o json
|
||||||
rune -0 jq -c '.API.Client.Credentials | [.url,.login[0:32]]' <(output)
|
rune -0 jq -c '.API.Client.Credentials | [.url,.login[0:32]]' <(output)
|
||||||
assert_json '["http://127.0.0.1:8080/","githubciXXXXXXXXXXXXXXXXXXXXXXXX"]'
|
assert_json '["http://127.0.0.1:8080/","githubciXXXXXXXXXXXXXXXXXXXXXXXX"]'
|
||||||
|
|
||||||
|
# pointer to boolean
|
||||||
|
|
||||||
|
rune -0 cscli config show --key Config.API.Client.InsecureSkipVerify
|
||||||
|
assert_output "&false"
|
||||||
|
|
||||||
|
# complex type
|
||||||
|
rune -0 cscli config show --key Config.PluginConfig
|
||||||
|
assert_output - <<-EOT
|
||||||
|
&csconfig.PluginCfg{
|
||||||
|
User: "nobody",
|
||||||
|
Group: "nogroup",
|
||||||
|
}
|
||||||
|
EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "cscli - required configuration paths" {
|
@test "cscli - required configuration paths" {
|
||||||
|
|
|
@ -34,22 +34,22 @@ teardown() {
|
||||||
@test "config.yaml.local - cscli (log_level)" {
|
@test "config.yaml.local - cscli (log_level)" {
|
||||||
config_set '.common.log_level="warning"'
|
config_set '.common.log_level="warning"'
|
||||||
rune -0 cscli config show --key Config.Common.LogLevel
|
rune -0 cscli config show --key Config.Common.LogLevel
|
||||||
assert_output "warning"
|
assert_output "&3"
|
||||||
|
|
||||||
echo "{'common':{'log_level':'debug'}}" >"${CONFIG_YAML}.local"
|
echo "{'common':{'log_level':'debug'}}" >"${CONFIG_YAML}.local"
|
||||||
rune -0 cscli config show --key Config.Common.LogLevel
|
rune -0 cscli config show --key Config.Common.LogLevel
|
||||||
assert_output "debug"
|
assert_output "&5"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "config.yaml.local - cscli (log_level - with envvar)" {
|
@test "config.yaml.local - cscli (log_level - with envvar)" {
|
||||||
config_set '.common.log_level="warning"'
|
config_set '.common.log_level="warning"'
|
||||||
rune -0 cscli config show --key Config.Common.LogLevel
|
rune -0 cscli config show --key Config.Common.LogLevel
|
||||||
assert_output "warning"
|
assert_output "&3"
|
||||||
|
|
||||||
export CROWDSEC_LOG_LEVEL=debug
|
export CROWDSEC_LOG_LEVEL=debug
|
||||||
echo "{'common':{'log_level':'${CROWDSEC_LOG_LEVEL}'}}" >"${CONFIG_YAML}.local"
|
echo "{'common':{'log_level':'${CROWDSEC_LOG_LEVEL}'}}" >"${CONFIG_YAML}.local"
|
||||||
rune -0 cscli config show --key Config.Common.LogLevel
|
rune -0 cscli config show --key Config.Common.LogLevel
|
||||||
assert_output "debug"
|
assert_output "&5"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "config.yaml.local - crowdsec (listen_url)" {
|
@test "config.yaml.local - crowdsec (listen_url)" {
|
||||||
|
|
|
@ -34,17 +34,19 @@ teardown() {
|
||||||
|
|
||||||
# no items
|
# no items
|
||||||
rune -0 cscli hub list
|
rune -0 cscli hub list
|
||||||
assert_output --regexp "APPSEC-CONFIGS.*APPSEC-RULES.*PARSERS.*POSTOVERFLOWS.*SCENARIOS.*COLLECTIONS.*"
|
assert_output "No items to display"
|
||||||
rune -0 cscli hub list -o json
|
rune -0 cscli hub list -o json
|
||||||
assert_json '{appsec-configs:[],appsec-rules:[],parsers:[],scenarios:[],collections:[],postoverflows:[]}'
|
assert_json '{appsec-configs:[],appsec-rules:[],parsers:[],scenarios:[],collections:[],postoverflows:[]}'
|
||||||
rune -0 cscli hub list -o raw
|
rune -0 cscli hub list -o raw
|
||||||
assert_output 'name,status,version,description,type'
|
assert_output 'name,status,version,description,type'
|
||||||
|
|
||||||
# some items
|
# some items: with output=human, show only non-empty tables
|
||||||
rune -0 cscli parsers install crowdsecurity/whitelists
|
rune -0 cscli parsers install crowdsecurity/whitelists
|
||||||
rune -0 cscli scenarios install crowdsecurity/telnet-bf
|
rune -0 cscli scenarios install crowdsecurity/telnet-bf
|
||||||
rune -0 cscli hub list
|
rune -0 cscli hub list
|
||||||
assert_output --regexp ".*PARSERS.*crowdsecurity/whitelists.*POSTOVERFLOWS.*SCENARIOS.*crowdsecurity/telnet-bf.*COLLECTIONS.*"
|
assert_output --regexp ".*PARSERS.*crowdsecurity/whitelists.*SCENARIOS.*crowdsecurity/telnet-bf.*"
|
||||||
|
refute_output --partial 'POSTOVERFLOWS'
|
||||||
|
refute_output --partial 'COLLECTIONS'
|
||||||
rune -0 cscli hub list -o json
|
rune -0 cscli hub list -o json
|
||||||
rune -0 jq -e '(.parsers | length == 1) and (.scenarios | length == 1)' <(output)
|
rune -0 jq -e '(.parsers | length == 1) and (.scenarios | length == 1)' <(output)
|
||||||
rune -0 cscli hub list -o raw
|
rune -0 cscli hub list -o raw
|
||||||
|
|
|
@ -23,20 +23,24 @@ teardown() {
|
||||||
|
|
||||||
#----------
|
#----------
|
||||||
|
|
||||||
@test "can list machines as regular user" {
|
|
||||||
rune -0 cscli machines list
|
|
||||||
}
|
|
||||||
|
|
||||||
@test "we have exactly one machine" {
|
@test "we have exactly one machine" {
|
||||||
rune -0 cscli machines list -o json
|
rune -0 cscli machines list -o json
|
||||||
rune -0 jq -c '[. | length, .[0].machineId[0:32], .[0].isValidated]' <(output)
|
rune -0 jq -c '[. | length, .[0].machineId[0:32], .[0].isValidated]' <(output)
|
||||||
assert_output '[1,"githubciXXXXXXXXXXXXXXXXXXXXXXXX",true]'
|
assert_output '[1,"githubciXXXXXXXXXXXXXXXXXXXXXXXX",true]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "don't overwrite local credentials by default" {
|
||||||
|
rune -1 cscli machines add local -a -o json
|
||||||
|
rune -0 jq -r '.msg' <(stderr)
|
||||||
|
assert_output --partial 'already exists: please remove it, use "--force" or specify a different file with "-f"'
|
||||||
|
rune -0 cscli machines add local -a --force
|
||||||
|
assert_stderr --partial "Machine 'local' successfully added to the local API"
|
||||||
|
}
|
||||||
|
|
||||||
@test "add a new machine and delete it" {
|
@test "add a new machine and delete it" {
|
||||||
rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human
|
rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human
|
||||||
assert_stderr --partial "Machine 'CiTestMachine' successfully added to the local API"
|
assert_stderr --partial "Machine 'CiTestMachine' successfully added to the local API"
|
||||||
assert_stderr --partial "API credentials dumped to '/dev/null'"
|
assert_stderr --partial "API credentials written to '/dev/null'"
|
||||||
|
|
||||||
# we now have two machines
|
# we now have two machines
|
||||||
rune -0 cscli machines list -o json
|
rune -0 cscli machines list -o json
|
||||||
|
@ -56,7 +60,7 @@ teardown() {
|
||||||
@test "register, validate and then remove a machine" {
|
@test "register, validate and then remove a machine" {
|
||||||
rune -0 cscli lapi register --machine CiTestMachineRegister -f /dev/null -o human
|
rune -0 cscli lapi register --machine CiTestMachineRegister -f /dev/null -o human
|
||||||
assert_stderr --partial "Successfully registered to Local API (LAPI)"
|
assert_stderr --partial "Successfully registered to Local API (LAPI)"
|
||||||
assert_stderr --partial "Local API credentials dumped to '/dev/null'"
|
assert_stderr --partial "Local API credentials written to '/dev/null'"
|
||||||
|
|
||||||
# the machine is not validated yet
|
# the machine is not validated yet
|
||||||
rune -0 cscli machines list -o json
|
rune -0 cscli machines list -o json
|
||||||
|
|
|
@ -61,12 +61,12 @@ make_init_data() {
|
||||||
./instance-db config-yaml
|
./instance-db config-yaml
|
||||||
./instance-db setup
|
./instance-db setup
|
||||||
|
|
||||||
|
./bin/preload-hub-items
|
||||||
|
|
||||||
# when installed packages are always using sqlite, so no need to regenerate
|
# when installed packages are always using sqlite, so no need to regenerate
|
||||||
# local credz for sqlite
|
# local credz for sqlite
|
||||||
|
|
||||||
./bin/preload-hub-items
|
[[ "${DB_BACKEND}" == "sqlite" ]] || ${CSCLI} machines add githubciXXXXXXXXXXXXXXXXXXXXXXXX --auto --force
|
||||||
|
|
||||||
[[ "${DB_BACKEND}" == "sqlite" ]] || ${CSCLI} machines add --auto
|
|
||||||
|
|
||||||
mkdir -p "$LOCAL_INIT_DIR"
|
mkdir -p "$LOCAL_INIT_DIR"
|
||||||
|
|
||||||
|
|
|
@ -115,11 +115,12 @@ make_init_data() {
|
||||||
./instance-db config-yaml
|
./instance-db config-yaml
|
||||||
./instance-db setup
|
./instance-db setup
|
||||||
|
|
||||||
"$CSCLI" --warning machines add githubciXXXXXXXXXXXXXXXXXXXXXXXX --auto
|
|
||||||
"$CSCLI" --warning hub update
|
"$CSCLI" --warning hub update
|
||||||
|
|
||||||
./bin/preload-hub-items
|
./bin/preload-hub-items
|
||||||
|
|
||||||
|
"$CSCLI" --warning machines add githubciXXXXXXXXXXXXXXXXXXXXXXXX --auto --force
|
||||||
|
|
||||||
mkdir -p "$LOCAL_INIT_DIR"
|
mkdir -p "$LOCAL_INIT_DIR"
|
||||||
|
|
||||||
./instance-db dump "${LOCAL_INIT_DIR}/database"
|
./instance-db dump "${LOCAL_INIT_DIR}/database"
|
||||||
|
|
Loading…
Reference in a new issue