Compare commits
43 commits
master
...
lapi_to_ca
Author | SHA1 | Date | |
---|---|---|---|
|
42a57e0a5f | ||
|
d50ea5f994 | ||
|
40103275df | ||
|
ff4f653fc8 | ||
|
7691fbef62 | ||
|
774a8a681a | ||
|
0917d82340 | ||
|
42f1802a9b | ||
|
52cb64e9f5 | ||
|
708e7b5d8a | ||
|
8a485b09f2 | ||
|
816edfa007 | ||
|
0b4d812b90 | ||
|
03a058dff6 | ||
|
3e8ce3ce19 | ||
|
23f7499836 | ||
|
f0cee89211 | ||
|
21d279c66b | ||
|
ed010fe7d6 | ||
|
e41a334af4 | ||
|
9acb0e7e7e | ||
|
e96a267144 | ||
|
21f83b6a0f | ||
|
3046181316 | ||
|
45df3c9526 | ||
|
50a065dc5d | ||
|
5d6422b8fa | ||
|
7432c54254 | ||
|
8994e57bdc | ||
|
64123b7249 | ||
|
7af17baa8a | ||
|
28f13e4b82 | ||
|
11b1843070 | ||
|
ab141f7f3a | ||
|
553e833d75 | ||
|
0c32f2574b | ||
|
dd9dca624b | ||
|
04f7dbd1f5 | ||
|
908b04028e | ||
|
1372c49505 | ||
|
03dbce1e50 | ||
|
2c6a279b9b | ||
|
52edfba2b3 |
|
@ -2,17 +2,59 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
|
"github.com/enescakir/emoji"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func IsInSlice(a string, b []string) bool {
|
||||||
|
for _, v := range b {
|
||||||
|
if a == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchScenariosListFromDB() ([]string, error) {
|
||||||
|
scenarios := make([]string, 0)
|
||||||
|
machines, err := dbClient.ListMachines()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("while listing machines: %s", err)
|
||||||
|
}
|
||||||
|
//merge all scenarios together
|
||||||
|
for _, v := range machines {
|
||||||
|
machineScenarios := strings.Split(v.Scenarios, ",")
|
||||||
|
log.Debugf("%d scenarios for machine %d", len(machineScenarios), v.ID)
|
||||||
|
for _, sv := range machineScenarios {
|
||||||
|
if !IsInSlice(sv, scenarios) && sv != "" {
|
||||||
|
scenarios = append(scenarios, sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("Returning list of scenarios : %+v", scenarios)
|
||||||
|
return scenarios, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewConsoleCmd() *cobra.Command {
|
func NewConsoleCmd() *cobra.Command {
|
||||||
var cmdConsole = &cobra.Command{
|
var cmdConsole = &cobra.Command{
|
||||||
Use: "console [action]",
|
Use: "console [action]",
|
||||||
|
@ -20,12 +62,24 @@ func NewConsoleCmd() *cobra.Command {
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
|
if err := csConfig.LoadAPIServer(); err != nil {
|
||||||
|
var fdErr *fs.PathError
|
||||||
|
if errors.As(err, &fdErr) {
|
||||||
|
log.Fatalf("Unable to load Local API : %s", fdErr)
|
||||||
|
} else {
|
||||||
|
log.Fatalf("Unable to load required Local API Configuration : %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if csConfig.DisableAPI {
|
||||||
log.Fatal("Local API is disabled, please run this command on the local API machine")
|
log.Fatal("Local API is disabled, please run this command on the local API machine")
|
||||||
}
|
}
|
||||||
if csConfig.API.Server.OnlineClient == nil {
|
if csConfig.API.Server.OnlineClient == nil {
|
||||||
log.Fatalf("no configuration for Central API (CAPI) in '%s'", *csConfig.FilePath)
|
log.Fatalf("no configuration for Central API (CAPI) in '%s'", *csConfig.FilePath)
|
||||||
}
|
}
|
||||||
|
if csConfig.API.Server.OnlineClient.Credentials == nil {
|
||||||
|
log.Fatal("You must configure Central API (CAPI) with `cscli capi register` before enrolling your instance")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -39,21 +93,10 @@ Enroll this instance to https://app.crowdsec.net
|
||||||
|
|
||||||
You can get your enrollment key by creating an account on https://app.crowdsec.net.
|
You can get your enrollment key by creating an account on https://app.crowdsec.net.
|
||||||
After running this command your will need to validate the enrollment in the webapp.`,
|
After running this command your will need to validate the enrollment in the webapp.`,
|
||||||
Example: "cscli console enroll YOUR-ENROLL-KEY",
|
Example: `cscli console enroll YOUR-ENROLL-KEY
|
||||||
|
`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
|
|
||||||
log.Fatal("Local API is disabled, please run this command on the local API machine")
|
|
||||||
}
|
|
||||||
if csConfig.API.Server.OnlineClient == nil {
|
|
||||||
log.Fatalf("no configuration for Central API (CAPI) in '%s'", *csConfig.FilePath)
|
|
||||||
}
|
|
||||||
if csConfig.API.Server.OnlineClient.Credentials == nil {
|
|
||||||
log.Fatal("You must configure Central API (CAPI) with `cscli capi register` before enrolling your instance")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
|
password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
|
||||||
apiURL, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
|
apiURL, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
|
||||||
|
@ -94,7 +137,314 @@ After running this command your will need to validate the enrollment in the weba
|
||||||
log.Infof("Watcher successfully enrolled. Visit https://app.crowdsec.net to accept it.")
|
log.Infof("Watcher successfully enrolled. Visit https://app.crowdsec.net to accept it.")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdConsole.AddCommand(cmdEnroll)
|
cmdConsole.AddCommand(cmdEnroll)
|
||||||
|
|
||||||
|
var enableAll, disableAll bool
|
||||||
|
|
||||||
|
cmdEnable := &cobra.Command{
|
||||||
|
Use: "enable [feature-flag]",
|
||||||
|
Short: "Enable a feature flag",
|
||||||
|
Example: "enable alerts-tainted",
|
||||||
|
Long: `
|
||||||
|
Enable given information push to the central API. Allows to empower the console`,
|
||||||
|
ValidArgs: csconfig.CONSOLE_CONFIGS,
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if enableAll {
|
||||||
|
SetConsoleOpts(csconfig.CONSOLE_CONFIGS, true)
|
||||||
|
} else {
|
||||||
|
SetConsoleOpts(args, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
|
||||||
|
log.Fatalf("failed writing console config : %s", err)
|
||||||
|
}
|
||||||
|
if enableAll {
|
||||||
|
log.Infof("All features have been enabled successfully")
|
||||||
|
} else {
|
||||||
|
log.Infof("%v have been enabled", args)
|
||||||
|
}
|
||||||
|
log.Infof(ReloadMessage())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmdEnable.Flags().BoolVarP(&enableAll, "all", "a", false, "Enable all feature flags")
|
||||||
|
cmdConsole.AddCommand(cmdEnable)
|
||||||
|
|
||||||
|
cmdDisable := &cobra.Command{
|
||||||
|
Use: "disable [feature-flag]",
|
||||||
|
Short: "Disable a feature flag",
|
||||||
|
Example: "disable alerts-tainted",
|
||||||
|
Long: `
|
||||||
|
Disable given information push to the central API.`,
|
||||||
|
ValidArgs: csconfig.CONSOLE_CONFIGS,
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if disableAll {
|
||||||
|
SetConsoleOpts(csconfig.CONSOLE_CONFIGS, false)
|
||||||
|
} else {
|
||||||
|
SetConsoleOpts(args, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := csConfig.API.Server.DumpConsoleConfig(); err != nil {
|
||||||
|
log.Fatalf("failed writing console config : %s", err)
|
||||||
|
}
|
||||||
|
if disableAll {
|
||||||
|
log.Infof("All features have been disabled")
|
||||||
|
} else {
|
||||||
|
log.Infof("%v have been disabled", args)
|
||||||
|
}
|
||||||
|
log.Infof(ReloadMessage())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmdDisable.Flags().BoolVarP(&disableAll, "all", "a", false, "Enable all feature flags")
|
||||||
|
cmdConsole.AddCommand(cmdDisable)
|
||||||
|
|
||||||
|
cmdConsoleStatus := &cobra.Command{
|
||||||
|
Use: "status [feature-flag]",
|
||||||
|
Short: "Shows status of one or all feature flags",
|
||||||
|
Example: "status alerts-tainted",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
switch csConfig.Cscli.Output {
|
||||||
|
case "human":
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
|
||||||
|
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||||
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||||
|
table.SetHeader([]string{"Option Name", "Activated", "Description"})
|
||||||
|
for _, option := range csconfig.CONSOLE_CONFIGS {
|
||||||
|
switch option {
|
||||||
|
case csconfig.SEND_CUSTOM_SCENARIOS:
|
||||||
|
activated := fmt.Sprintf("%s", emoji.CrossMark)
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios == true {
|
||||||
|
activated = fmt.Sprintf("%s", emoji.CheckMarkButton)
|
||||||
|
}
|
||||||
|
table.Append([]string{option, activated, "Send alerts from custom scenarios to the console"})
|
||||||
|
case csconfig.SEND_MANUAL_SCENARIOS:
|
||||||
|
activated := fmt.Sprintf("%s", emoji.CrossMark)
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareManualDecisions == true {
|
||||||
|
activated = fmt.Sprintf("%s", emoji.CheckMarkButton)
|
||||||
|
}
|
||||||
|
table.Append([]string{option, activated, "Send manual decisions to the console"})
|
||||||
|
case csconfig.SEND_TAINTED_SCENARIOS:
|
||||||
|
activated := fmt.Sprintf("%s", emoji.CrossMark)
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios == true {
|
||||||
|
activated = fmt.Sprintf("%s", emoji.CheckMarkButton)
|
||||||
|
}
|
||||||
|
table.Append([]string{option, activated, "Send alerts from tainted scenarios to the console"})
|
||||||
|
case csconfig.SEND_SIMULATED_DECISIONS:
|
||||||
|
activated := fmt.Sprintf("%s", emoji.CrossMark)
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareSimulatedDecisions == true {
|
||||||
|
activated = fmt.Sprintf("%s", emoji.CheckMarkButton)
|
||||||
|
}
|
||||||
|
table.Append([]string{option, activated, "Send alerts from scenarios in simulation mode to the console"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
case "json":
|
||||||
|
data, err := json.MarshalIndent(csConfig.API.Server.ConsoleConfig, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to marshal configuration: %s", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", string(data))
|
||||||
|
case "raw":
|
||||||
|
data, err := yaml.Marshal(csConfig.API.Server.ConsoleConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to marshal configuration: %s", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", string(data))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdConsole.AddCommand(cmdConsoleStatus)
|
||||||
|
|
||||||
|
cmdConsoleSync := &cobra.Command{
|
||||||
|
Use: "sync",
|
||||||
|
Short: "Sync current decisions to console",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
var err error
|
||||||
|
if err := csConfig.LoadDBConfig(); err != nil {
|
||||||
|
log.Errorf("This command requires direct database access (must be run on the local API machine)")
|
||||||
|
log.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
dbClient, err = database.NewClient(csConfig.DbConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unable to create new database client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
|
||||||
|
apiurl, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("parsing api url ('%s'): %s", csConfig.API.Server.OnlineClient.Credentials.URL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := csConfig.LoadHub(); err != nil {
|
||||||
|
log.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
|
||||||
|
log.Fatalf("Failed to load hub index : %s", err)
|
||||||
|
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
|
||||||
|
}
|
||||||
|
scenarios, err := cwhub.GetUpstreamInstalledScenariosAsString()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to get scenarios : %s", err.Error())
|
||||||
|
}
|
||||||
|
if len(scenarios) == 0 {
|
||||||
|
log.Fatalf("no scenarios installed, abort")
|
||||||
|
}
|
||||||
|
|
||||||
|
Client, err = apiclient.NewClient(&apiclient.Config{
|
||||||
|
MachineID: csConfig.API.Server.OnlineClient.Credentials.Login,
|
||||||
|
Password: password,
|
||||||
|
UserAgent: fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
|
||||||
|
URL: apiurl,
|
||||||
|
VersionPrefix: "v2",
|
||||||
|
Scenarios: scenarios,
|
||||||
|
UpdateScenario: FetchScenariosListFromDB,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("init default client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := make(map[string][]string)
|
||||||
|
filter["has_active_decision"] = []string{"true"}
|
||||||
|
alertsWithDecisions, err := dbClient.QueryAlertWithFilter(filter)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
alertList := make([]*models.Alert, 0)
|
||||||
|
for _, alert := range alertsWithDecisions {
|
||||||
|
startAt := alert.StartedAt.String()
|
||||||
|
StopAt := alert.StoppedAt.String()
|
||||||
|
formatedAlert := models.Alert{
|
||||||
|
ID: int64(alert.ID),
|
||||||
|
MachineID: machineID,
|
||||||
|
CreatedAt: alert.CreatedAt.Format(time.RFC3339),
|
||||||
|
Scenario: &alert.Scenario,
|
||||||
|
ScenarioVersion: &alert.ScenarioVersion,
|
||||||
|
ScenarioHash: &alert.ScenarioHash,
|
||||||
|
Message: &alert.Message,
|
||||||
|
EventsCount: &alert.EventsCount,
|
||||||
|
StartAt: &startAt,
|
||||||
|
StopAt: &StopAt,
|
||||||
|
Capacity: &alert.Capacity,
|
||||||
|
Leakspeed: &alert.LeakSpeed,
|
||||||
|
Simulated: &alert.Simulated,
|
||||||
|
Source: &models.Source{
|
||||||
|
Scope: &alert.SourceScope,
|
||||||
|
Value: &alert.SourceValue,
|
||||||
|
IP: alert.SourceIp,
|
||||||
|
Range: alert.SourceRange,
|
||||||
|
AsNumber: alert.SourceAsNumber,
|
||||||
|
AsName: alert.SourceAsName,
|
||||||
|
Cn: alert.SourceCountry,
|
||||||
|
Latitude: alert.SourceLatitude,
|
||||||
|
Longitude: alert.SourceLongitude,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, decisionItem := range alert.Edges.Decisions {
|
||||||
|
duration := decisionItem.Until.Sub(time.Now()).String()
|
||||||
|
formatedAlert.Decisions = append(formatedAlert.Decisions, &models.Decision{
|
||||||
|
Duration: &duration, // transform into time.Time ?
|
||||||
|
Scenario: &decisionItem.Scenario,
|
||||||
|
Type: &decisionItem.Type,
|
||||||
|
Scope: &decisionItem.Scope,
|
||||||
|
Value: &decisionItem.Value,
|
||||||
|
Origin: &decisionItem.Origin,
|
||||||
|
Simulated: formatedAlert.Simulated,
|
||||||
|
ID: int64(decisionItem.ID),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
alertList = append(alertList, &formatedAlert)
|
||||||
|
}
|
||||||
|
_, _, err = Client.Decisions.SyncDecisions(context.Background(), alertList)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unable to sync decisions with console: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Infof("Decisions have been synchronized successfully")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdConsole.AddCommand(cmdConsoleSync)
|
||||||
|
|
||||||
return cmdConsole
|
return cmdConsole
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetConsoleOpts(args []string, wanted bool) {
|
||||||
|
for _, arg := range args {
|
||||||
|
switch arg {
|
||||||
|
case csconfig.SEND_CUSTOM_SCENARIOS:
|
||||||
|
/*for each flag check if it's already set before setting it*/
|
||||||
|
if csConfig.API.Server.ConsoleConfig.ShareCustomScenarios != nil {
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios == wanted {
|
||||||
|
log.Infof("%s already set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
|
||||||
|
*csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = wanted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
|
||||||
|
csConfig.API.Server.ConsoleConfig.ShareCustomScenarios = types.BoolPtr(wanted)
|
||||||
|
}
|
||||||
|
case csconfig.SEND_TAINTED_SCENARIOS:
|
||||||
|
/*for each flag check if it's already set before setting it*/
|
||||||
|
if csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios != nil {
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios == wanted {
|
||||||
|
log.Infof("%s already set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
|
||||||
|
*csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = wanted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
|
||||||
|
csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios = types.BoolPtr(wanted)
|
||||||
|
}
|
||||||
|
case csconfig.SEND_MANUAL_SCENARIOS:
|
||||||
|
/*for each flag check if it's already set before setting it*/
|
||||||
|
if csConfig.API.Server.ConsoleConfig.ShareManualDecisions != nil {
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareManualDecisions == wanted {
|
||||||
|
log.Infof("%s already set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
|
||||||
|
*csConfig.API.Server.ConsoleConfig.ShareManualDecisions = wanted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
|
||||||
|
csConfig.API.Server.ConsoleConfig.ShareManualDecisions = types.BoolPtr(wanted)
|
||||||
|
}
|
||||||
|
case csconfig.SEND_LIVE_DECISIONS:
|
||||||
|
/*for each flag check if it's already set before setting it*/
|
||||||
|
if csConfig.API.Server.ConsoleConfig.ShareDecisions != nil {
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareDecisions == wanted {
|
||||||
|
log.Infof("%s already set to %t", csconfig.SEND_LIVE_DECISIONS, wanted)
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_LIVE_DECISIONS, wanted)
|
||||||
|
*csConfig.API.Server.ConsoleConfig.ShareDecisions = wanted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_LIVE_DECISIONS, wanted)
|
||||||
|
csConfig.API.Server.ConsoleConfig.ShareDecisions = types.BoolPtr(wanted)
|
||||||
|
}
|
||||||
|
case csconfig.SEND_SIMULATED_DECISIONS:
|
||||||
|
/*for each flag check if it's already set before setting it*/
|
||||||
|
if csConfig.API.Server.ConsoleConfig.ShareSimulatedDecisions != nil {
|
||||||
|
if *csConfig.API.Server.ConsoleConfig.ShareSimulatedDecisions == wanted {
|
||||||
|
log.Infof("%s already set to %t", csconfig.SEND_SIMULATED_DECISIONS, wanted)
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_SIMULATED_DECISIONS, wanted)
|
||||||
|
*csConfig.API.Server.ConsoleConfig.ShareSimulatedDecisions = wanted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("%s set to %t", csconfig.SEND_SIMULATED_DECISIONS, wanted)
|
||||||
|
csConfig.API.Server.ConsoleConfig.ShareSimulatedDecisions = types.BoolPtr(wanted)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Fatalf("unknown flag %s", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -26,8 +26,7 @@ func DecisionsToTable(alerts *models.GetAlertsResponse) error {
|
||||||
/*here we cheat a bit : to make it more readable for the user, we dedup some entries*/
|
/*here we cheat a bit : to make it more readable for the user, we dedup some entries*/
|
||||||
var spamLimit map[string]bool = make(map[string]bool)
|
var spamLimit map[string]bool = make(map[string]bool)
|
||||||
|
|
||||||
/*process in reverse order to keep the latest item only*/
|
for aIdx := 0; aIdx < len(*alerts); aIdx++ {
|
||||||
for aIdx := len(*alerts) - 1; aIdx >= 0; aIdx-- {
|
|
||||||
alertItem := (*alerts)[aIdx]
|
alertItem := (*alerts)[aIdx]
|
||||||
newDecisions := make([]*models.Decision, 0)
|
newDecisions := make([]*models.Decision, 0)
|
||||||
for _, decisionItem := range alertItem.Decisions {
|
for _, decisionItem := range alertItem.Decisions {
|
||||||
|
@ -227,7 +226,6 @@ cscli decisions list -t ban
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to list decisions : %v", err.Error())
|
log.Fatalf("Unable to list decisions : %v", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = DecisionsToTable(alerts)
|
err = DecisionsToTable(alerts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to list decisions : %v", err.Error())
|
log.Fatalf("unable to list decisions : %v", err.Error())
|
||||||
|
@ -273,7 +271,7 @@ cscli decisions add --scope username --value foobar
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
var err error
|
var err error
|
||||||
var ip, ipRange string
|
var ipRange string
|
||||||
alerts := models.AddAlertsRequest{}
|
alerts := models.AddAlertsRequest{}
|
||||||
origin := "cscli"
|
origin := "cscli"
|
||||||
capacity := int32(0)
|
capacity := int32(0)
|
||||||
|
@ -329,7 +327,7 @@ cscli decisions add --scope username --value foobar
|
||||||
AsName: empty,
|
AsName: empty,
|
||||||
AsNumber: empty,
|
AsNumber: empty,
|
||||||
Cn: empty,
|
Cn: empty,
|
||||||
IP: ip,
|
IP: addValue,
|
||||||
Range: ipRange,
|
Range: ipRange,
|
||||||
Scope: &addScope,
|
Scope: &addScope,
|
||||||
Value: &addValue,
|
Value: &addValue,
|
||||||
|
|
|
@ -41,6 +41,7 @@ api:
|
||||||
log_level: info
|
log_level: info
|
||||||
listen_uri: 127.0.0.1:8080
|
listen_uri: 127.0.0.1:8080
|
||||||
profiles_path: /etc/crowdsec/profiles.yaml
|
profiles_path: /etc/crowdsec/profiles.yaml
|
||||||
|
console_path: /etc/crowdsec/console_config.yaml
|
||||||
online_client: # Central API credentials (to push signals and receive bad IPs)
|
online_client: # Central API credentials (to push signals and receive bad IPs)
|
||||||
credentials_path: /etc/crowdsec/online_api_credentials.yaml
|
credentials_path: /etc/crowdsec/online_api_credentials.yaml
|
||||||
# tls:
|
# tls:
|
||||||
|
|
4
config/console_config.yaml
Normal file
4
config/console_config.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
share_manual_decisions: false
|
||||||
|
share_custom: false
|
||||||
|
share_tainted: false
|
||||||
|
share_decisions: false
|
1
debian/rules
vendored
1
debian/rules
vendored
|
@ -47,4 +47,5 @@ override_dh_auto_install:
|
||||||
cp config/config.yaml debian/crowdsec/etc/crowdsec/config.yaml
|
cp config/config.yaml debian/crowdsec/etc/crowdsec/config.yaml
|
||||||
cp config/simulation.yaml debian/crowdsec/etc/crowdsec/simulation.yaml
|
cp config/simulation.yaml debian/crowdsec/etc/crowdsec/simulation.yaml
|
||||||
cp config/profiles.yaml debian/crowdsec/etc/crowdsec/profiles.yaml
|
cp config/profiles.yaml debian/crowdsec/etc/crowdsec/profiles.yaml
|
||||||
|
cp config/console_config.yaml debian/crowdsec/etc/crowdsec/console_config.yaml
|
||||||
cp -a config/patterns debian/crowdsec/etc/crowdsec
|
cp -a config/patterns debian/crowdsec/etc/crowdsec
|
||||||
|
|
|
@ -230,7 +230,7 @@ func TestWatcherEnroll(t *testing.T) {
|
||||||
|
|
||||||
_, err = client.Auth.EnrollWatcher(context.Background(), "goodkey")
|
_, err = client.Auth.EnrollWatcher(context.Background(), "goodkey")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpect auth err: %s", err)
|
t.Fatalf("unexpect enroll err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.Auth.EnrollWatcher(context.Background(), "badkey")
|
_, err = client.Auth.EnrollWatcher(context.Background(), "badkey")
|
||||||
|
|
|
@ -32,6 +32,10 @@ type DecisionsDeleteOpts struct {
|
||||||
ListOpts
|
ListOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SuccessReponse struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
//to demo query arguments
|
//to demo query arguments
|
||||||
func (s *DecisionsService) List(ctx context.Context, opts DecisionsListOpts) (*models.GetDecisionsResponse, *Response, error) {
|
func (s *DecisionsService) List(ctx context.Context, opts DecisionsListOpts) (*models.GetDecisionsResponse, *Response, error) {
|
||||||
var decisions models.GetDecisionsResponse
|
var decisions models.GetDecisionsResponse
|
||||||
|
@ -122,3 +126,35 @@ func (s *DecisionsService) DeleteOne(ctx context.Context, decision_id string) (*
|
||||||
}
|
}
|
||||||
return &deleteDecisionResponse, resp, nil
|
return &deleteDecisionResponse, resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send to CAPI manually deleted decisions
|
||||||
|
func (s *DecisionsService) DeleteManualDecisions(ctx context.Context, decisionsID []string) (*SuccessReponse, *Response, error) {
|
||||||
|
var successReponse SuccessReponse
|
||||||
|
u := fmt.Sprintf("%s/decisions/delete", s.client.URLPrefix)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, &decisionsID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
resp, err := s.client.Do(ctx, req, &successReponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return &successReponse, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DecisionsService) SyncDecisions(ctx context.Context, decisions []*models.Alert) (*SuccessReponse, *Response, error) {
|
||||||
|
var successReponse SuccessReponse
|
||||||
|
|
||||||
|
u := fmt.Sprintf("%s/decisions/sync", s.client.URLPrefix)
|
||||||
|
req, err := s.client.NewRequest("POST", u, &decisions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
resp, err := s.client.Do(ctx, req, &successReponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &successReponse, resp, nil
|
||||||
|
}
|
||||||
|
|
|
@ -31,19 +31,22 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type apic struct {
|
type apic struct {
|
||||||
pullInterval time.Duration
|
pullInterval time.Duration
|
||||||
pushInterval time.Duration
|
pushInterval time.Duration
|
||||||
metricsInterval time.Duration
|
metricsInterval time.Duration
|
||||||
dbClient *database.Client
|
dbClient *database.Client
|
||||||
apiClient *apiclient.ApiClient
|
apiClient *apiclient.ApiClient
|
||||||
alertToPush chan []*models.Alert
|
alertToPush chan []*models.Alert
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pushTomb tomb.Tomb
|
pushTomb tomb.Tomb
|
||||||
pullTomb tomb.Tomb
|
pullTomb tomb.Tomb
|
||||||
metricsTomb tomb.Tomb
|
metricsTomb tomb.Tomb
|
||||||
startup bool
|
deleteDecisionsTomb tomb.Tomb
|
||||||
credentials *csconfig.ApiCredentialsCfg
|
startup bool
|
||||||
scenarioList []string
|
credentials *csconfig.ApiCredentialsCfg
|
||||||
|
scenarioList []string
|
||||||
|
consoleConfig *csconfig.ConsoleConfig
|
||||||
|
decisionsToDelete chan []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsInSlice(a string, b []string) bool {
|
func IsInSlice(a string, b []string) bool {
|
||||||
|
@ -75,8 +78,8 @@ func (a *apic) FetchScenariosListFromDB() ([]string, error) {
|
||||||
return scenarios, nil
|
return scenarios, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AlertToSignal(alert *models.Alert) *models.AddSignalsRequestItem {
|
func AlertToSignal(alert *models.Alert, scenarioTrust string, keepDecisions bool) *models.AddSignalsRequestItem {
|
||||||
return &models.AddSignalsRequestItem{
|
signal := &models.AddSignalsRequestItem{
|
||||||
Message: alert.Message,
|
Message: alert.Message,
|
||||||
Scenario: alert.Scenario,
|
Scenario: alert.Scenario,
|
||||||
ScenarioHash: alert.ScenarioHash,
|
ScenarioHash: alert.ScenarioHash,
|
||||||
|
@ -86,21 +89,30 @@ func AlertToSignal(alert *models.Alert) *models.AddSignalsRequestItem {
|
||||||
StopAt: alert.StopAt,
|
StopAt: alert.StopAt,
|
||||||
CreatedAt: alert.CreatedAt,
|
CreatedAt: alert.CreatedAt,
|
||||||
MachineID: alert.MachineID,
|
MachineID: alert.MachineID,
|
||||||
|
ScenarioTrust: &scenarioTrust,
|
||||||
|
AlertID: &alert.ID,
|
||||||
}
|
}
|
||||||
|
if keepDecisions {
|
||||||
|
log.Debugf("Keeping decisions to send to CAPI")
|
||||||
|
signal.Decisions = alert.Decisions
|
||||||
|
}
|
||||||
|
return signal
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPIC(config *csconfig.OnlineApiClientCfg, dbClient *database.Client) (*apic, error) {
|
func NewAPIC(config *csconfig.OnlineApiClientCfg, dbClient *database.Client, consoleConfig *csconfig.ConsoleConfig) (*apic, error) {
|
||||||
var err error
|
var err error
|
||||||
ret := &apic{
|
ret := &apic{
|
||||||
alertToPush: make(chan []*models.Alert),
|
alertToPush: make(chan []*models.Alert),
|
||||||
dbClient: dbClient,
|
dbClient: dbClient,
|
||||||
mu: sync.Mutex{},
|
mu: sync.Mutex{},
|
||||||
startup: true,
|
startup: true,
|
||||||
credentials: config.Credentials,
|
credentials: config.Credentials,
|
||||||
pullTomb: tomb.Tomb{},
|
pullTomb: tomb.Tomb{},
|
||||||
pushTomb: tomb.Tomb{},
|
pushTomb: tomb.Tomb{},
|
||||||
metricsTomb: tomb.Tomb{},
|
metricsTomb: tomb.Tomb{},
|
||||||
scenarioList: make([]string, 0),
|
scenarioList: make([]string, 0),
|
||||||
|
decisionsToDelete: make(chan []string),
|
||||||
|
consoleConfig: consoleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.pullInterval, err = time.ParseDuration(PullInterval)
|
ret.pullInterval, err = time.ParseDuration(PullInterval)
|
||||||
|
@ -149,6 +161,7 @@ func (a *apic) Push() error {
|
||||||
case <-a.pushTomb.Dying(): // if one apic routine is dying, do we kill the others?
|
case <-a.pushTomb.Dying(): // if one apic routine is dying, do we kill the others?
|
||||||
a.pullTomb.Kill(nil)
|
a.pullTomb.Kill(nil)
|
||||||
a.metricsTomb.Kill(nil)
|
a.metricsTomb.Kill(nil)
|
||||||
|
a.deleteDecisionsTomb.Kill(nil)
|
||||||
log.Infof("push tomb is dying, sending cache (%d elements) before exiting", len(cache))
|
log.Infof("push tomb is dying, sending cache (%d elements) before exiting", len(cache))
|
||||||
if len(cache) == 0 {
|
if len(cache) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -167,20 +180,42 @@ func (a *apic) Push() error {
|
||||||
case alerts := <-a.alertToPush:
|
case alerts := <-a.alertToPush:
|
||||||
var signals []*models.AddSignalsRequestItem
|
var signals []*models.AddSignalsRequestItem
|
||||||
for _, alert := range alerts {
|
for _, alert := range alerts {
|
||||||
/*we're only interested into decisions coming from scenarios of the hub*/
|
|
||||||
if alert.ScenarioHash == nil || *alert.ScenarioHash == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
/*and we're not interested into tainted scenarios neither*/
|
|
||||||
if alert.ScenarioVersion == nil || *alert.ScenarioVersion == "" || *alert.ScenarioVersion == "?" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
/*we also ignore alerts in simulated mode*/
|
|
||||||
if *alert.Simulated {
|
if *alert.Simulated {
|
||||||
log.Debugf("simulation enabled for alert (id:%d), will not be sent to CAPI", alert.ID)
|
log.Debugf("simulation enabled for alert (id:%d), will not be sent to CAPI", alert.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
signals = append(signals, AlertToSignal(alert))
|
scenarioTrust := "certified"
|
||||||
|
if alert.ScenarioHash == nil || *alert.ScenarioHash == "" {
|
||||||
|
scenarioTrust = "custom"
|
||||||
|
}
|
||||||
|
if alert.ScenarioVersion == nil || *alert.ScenarioVersion == "" || *alert.ScenarioVersion == "?" {
|
||||||
|
scenarioTrust = "tainted"
|
||||||
|
}
|
||||||
|
if len(alert.Decisions) > 0 {
|
||||||
|
if *alert.Decisions[0].Origin == "cscli" {
|
||||||
|
scenarioTrust = "manual"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch scenarioTrust {
|
||||||
|
case "manual":
|
||||||
|
if !*a.consoleConfig.ShareManualDecisions {
|
||||||
|
log.Debugf("manual decision generated an alert, doesn't send it to CAPI because options is disabled")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case "tainted":
|
||||||
|
if !*a.consoleConfig.ShareTaintedScenarios {
|
||||||
|
log.Debugf("tainted scenario generated an alert, doesn't send it to CAPI because options is disabled")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case "custom":
|
||||||
|
if !*a.consoleConfig.ShareCustomScenarios {
|
||||||
|
log.Debugf("custom scenario generated an alert, doesn't send it to CAPI because options is disabled")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Add signals for '%s' alert", scenarioTrust)
|
||||||
|
signals = append(signals, AlertToSignal(alert, scenarioTrust, *a.consoleConfig.ShareDecisions))
|
||||||
}
|
}
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
cache = append(cache, signals...)
|
cache = append(cache, signals...)
|
||||||
|
@ -189,6 +224,57 @@ func (a *apic) Push() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *apic) DeleteDecisions() error {
|
||||||
|
defer types.CatchPanic("lapi/deleteDecisionsCAPI")
|
||||||
|
|
||||||
|
log.Infof("start goroutine for manual deleted decisions")
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-a.pushTomb.Dying(): // if one apic routine is dying, do we kill the others?
|
||||||
|
a.pullTomb.Kill(nil)
|
||||||
|
a.metricsTomb.Kill(nil)
|
||||||
|
a.pushTomb.Kill(nil)
|
||||||
|
return nil
|
||||||
|
case decisions := <-a.decisionsToDelete:
|
||||||
|
go a.SendDeletedDecisions(decisions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *apic) SendDeletedDecisions(deletedDecisions []string) {
|
||||||
|
var send []string
|
||||||
|
|
||||||
|
bulkSize := 50
|
||||||
|
pageStart := 0
|
||||||
|
pageEnd := bulkSize
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
if pageEnd >= len(deletedDecisions) {
|
||||||
|
send = deletedDecisions[pageStart:]
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
_, _, err := a.apiClient.Decisions.DeleteManualDecisions(ctx, send)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error while sending final chunk to central API : %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
send = deletedDecisions[pageStart:pageEnd]
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
_, _, err := a.apiClient.Decisions.DeleteManualDecisions(ctx, send)
|
||||||
|
if err != nil {
|
||||||
|
//we log it here as well, because the return value of func might be discarded
|
||||||
|
log.Errorf("Error while sending chunk to central API : %s", err)
|
||||||
|
}
|
||||||
|
pageStart += bulkSize
|
||||||
|
pageEnd += bulkSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *apic) Send(cacheOrig *models.AddSignalsRequest) {
|
func (a *apic) Send(cacheOrig *models.AddSignalsRequest) {
|
||||||
/*we do have a problem with this :
|
/*we do have a problem with this :
|
||||||
The apic.Push background routine reads from alertToPush chan.
|
The apic.Push background routine reads from alertToPush chan.
|
||||||
|
@ -361,6 +447,7 @@ func (a *apic) Pull() error {
|
||||||
case <-a.pullTomb.Dying(): // if one apic routine is dying, do we kill the others?
|
case <-a.pullTomb.Dying(): // if one apic routine is dying, do we kill the others?
|
||||||
a.metricsTomb.Kill(nil)
|
a.metricsTomb.Kill(nil)
|
||||||
a.pushTomb.Kill(nil)
|
a.pushTomb.Kill(nil)
|
||||||
|
a.deleteDecisionsTomb.Kill(nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,8 +464,8 @@ func (a *apic) SendMetrics() error {
|
||||||
version := cwversion.VersionStr()
|
version := cwversion.VersionStr()
|
||||||
metric := &models.Metrics{
|
metric := &models.Metrics{
|
||||||
ApilVersion: &version,
|
ApilVersion: &version,
|
||||||
Machines: make([]*models.MetricsSoftInfo, 0),
|
Machines: make([]*models.MetricsAgentInfo, 0),
|
||||||
Bouncers: make([]*models.MetricsSoftInfo, 0),
|
Bouncers: make([]*models.MetricsBouncerInfo, 0),
|
||||||
}
|
}
|
||||||
machines, err := a.dbClient.ListMachines()
|
machines, err := a.dbClient.ListMachines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -389,17 +476,21 @@ func (a *apic) SendMetrics() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, machine := range machines {
|
for _, machine := range machines {
|
||||||
m := &models.MetricsSoftInfo{
|
m := &models.MetricsAgentInfo{
|
||||||
Version: machine.Version,
|
Version: machine.Version,
|
||||||
Name: machine.MachineId,
|
Name: machine.MachineId,
|
||||||
|
LastUpdate: machine.UpdatedAt.String(),
|
||||||
|
LastPush: machine.LastPush.String(),
|
||||||
}
|
}
|
||||||
metric.Machines = append(metric.Machines, m)
|
metric.Machines = append(metric.Machines, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bouncer := range bouncers {
|
for _, bouncer := range bouncers {
|
||||||
m := &models.MetricsSoftInfo{
|
m := &models.MetricsBouncerInfo{
|
||||||
Version: bouncer.Version,
|
Version: bouncer.Version,
|
||||||
Name: bouncer.Type,
|
CustomName: bouncer.Name,
|
||||||
|
Name: bouncer.Type,
|
||||||
|
LastPull: bouncer.LastPull.String(),
|
||||||
}
|
}
|
||||||
metric.Bouncers = append(metric.Bouncers, m)
|
metric.Bouncers = append(metric.Bouncers, m)
|
||||||
}
|
}
|
||||||
|
@ -411,6 +502,7 @@ func (a *apic) SendMetrics() error {
|
||||||
case <-a.metricsTomb.Dying(): // if one apic routine is dying, do we kill the others?
|
case <-a.metricsTomb.Dying(): // if one apic routine is dying, do we kill the others?
|
||||||
a.pullTomb.Kill(nil)
|
a.pullTomb.Kill(nil)
|
||||||
a.pushTomb.Kill(nil)
|
a.pushTomb.Kill(nil)
|
||||||
|
a.deleteDecisionsTomb.Kill(nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ type APIServer struct {
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
apic *apic
|
apic *apic
|
||||||
httpServerTomb tomb.Tomb
|
httpServerTomb tomb.Tomb
|
||||||
|
consoleConfig *csconfig.ConsoleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
|
// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
|
||||||
|
@ -165,26 +166,34 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
router.Use(CustomRecoveryWithWriter())
|
router.Use(CustomRecoveryWithWriter())
|
||||||
|
|
||||||
controller := &controllers.Controller{
|
controller := &controllers.Controller{
|
||||||
DBClient: dbClient,
|
DBClient: dbClient,
|
||||||
Ectx: context.Background(),
|
Ectx: context.Background(),
|
||||||
Router: router,
|
Router: router,
|
||||||
Profiles: config.Profiles,
|
Profiles: config.Profiles,
|
||||||
Log: clog,
|
Log: clog,
|
||||||
|
ConsoleConfig: config.ConsoleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiClient *apic
|
var apiClient *apic
|
||||||
|
|
||||||
if config.OnlineClient != nil && config.OnlineClient.Credentials != nil {
|
if config.OnlineClient != nil && config.OnlineClient.Credentials != nil {
|
||||||
log.Printf("Loading CAPI pusher")
|
log.Printf("Loading CAPI pusher")
|
||||||
apiClient, err = NewAPIC(config.OnlineClient, dbClient)
|
apiClient, err = NewAPIC(config.OnlineClient, dbClient, config.ConsoleConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &APIServer{}, err
|
return &APIServer{}, err
|
||||||
}
|
}
|
||||||
controller.CAPIChan = apiClient.alertToPush
|
controller.CAPIChan = apiClient.alertToPush
|
||||||
|
if *config.ConsoleConfig.ShareDecisions {
|
||||||
|
controller.DeleteDecisionChannel = apiClient.decisionsToDelete
|
||||||
|
} else {
|
||||||
|
controller.DeleteDecisionChannel = nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
apiClient = nil
|
apiClient = nil
|
||||||
controller.CAPIChan = nil
|
controller.CAPIChan = nil
|
||||||
|
controller.DeleteDecisionChannel = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &APIServer{
|
return &APIServer{
|
||||||
|
@ -197,6 +206,7 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
router: router,
|
router: router,
|
||||||
apic: apiClient,
|
apic: apiClient,
|
||||||
httpServerTomb: tomb.Tomb{},
|
httpServerTomb: tomb.Tomb{},
|
||||||
|
consoleConfig: config.ConsoleConfig,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -235,6 +245,15 @@ func (s *APIServer) Run() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if *s.apic.consoleConfig.ShareDecisions {
|
||||||
|
s.apic.deleteDecisionsTomb.Go(func() error {
|
||||||
|
if err := s.apic.DeleteDecisions(); err != nil {
|
||||||
|
log.Errorf("capi send deleted decisions: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.httpServerTomb.Go(func() error {
|
s.httpServerTomb.Go(func() error {
|
||||||
|
|
|
@ -49,6 +49,13 @@ func LoadTestConfig() csconfig.Config {
|
||||||
ListenURI: "http://127.0.0.1:8080",
|
ListenURI: "http://127.0.0.1:8080",
|
||||||
DbConfig: &dbconfig,
|
DbConfig: &dbconfig,
|
||||||
ProfilesPath: "./tests/profiles.yaml",
|
ProfilesPath: "./tests/profiles.yaml",
|
||||||
|
ConsoleConfig: &csconfig.ConsoleConfig{
|
||||||
|
ShareManualDecisions: new(bool),
|
||||||
|
ShareTaintedScenarios: new(bool),
|
||||||
|
ShareCustomScenarios: new(bool),
|
||||||
|
ShareDecisions: new(bool),
|
||||||
|
ShareSimulatedDecisions: new(bool),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
apiConfig := csconfig.APICfg{
|
apiConfig := csconfig.APICfg{
|
||||||
Server: &apiServerConfig,
|
Server: &apiServerConfig,
|
||||||
|
@ -76,6 +83,13 @@ func LoadTestConfigForwardedFor() csconfig.Config {
|
||||||
DbConfig: &dbconfig,
|
DbConfig: &dbconfig,
|
||||||
ProfilesPath: "./tests/profiles.yaml",
|
ProfilesPath: "./tests/profiles.yaml",
|
||||||
UseForwardedForHeaders: true,
|
UseForwardedForHeaders: true,
|
||||||
|
ConsoleConfig: &csconfig.ConsoleConfig{
|
||||||
|
ShareManualDecisions: new(bool),
|
||||||
|
ShareTaintedScenarios: new(bool),
|
||||||
|
ShareCustomScenarios: new(bool),
|
||||||
|
ShareDecisions: new(bool),
|
||||||
|
ShareSimulatedDecisions: new(bool),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
apiConfig := csconfig.APICfg{
|
apiConfig := csconfig.APICfg{
|
||||||
Server: &apiServerConfig,
|
Server: &apiServerConfig,
|
||||||
|
|
|
@ -2,6 +2,8 @@ package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/alexliesenfeld/health"
|
"github.com/alexliesenfeld/health"
|
||||||
v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1"
|
v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
|
@ -10,17 +12,18 @@ import (
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
Ectx context.Context
|
Ectx context.Context
|
||||||
DBClient *database.Client
|
DBClient *database.Client
|
||||||
Router *gin.Engine
|
Router *gin.Engine
|
||||||
Profiles []*csconfig.ProfileCfg
|
Profiles []*csconfig.ProfileCfg
|
||||||
CAPIChan chan []*models.Alert
|
CAPIChan chan []*models.Alert
|
||||||
PluginChannel chan csplugin.ProfileAlert
|
PluginChannel chan csplugin.ProfileAlert
|
||||||
Log *log.Logger
|
Log *log.Logger
|
||||||
|
ConsoleConfig *csconfig.ConsoleConfig
|
||||||
|
DeleteDecisionChannel chan []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) Init() error {
|
func (c *Controller) Init() error {
|
||||||
|
@ -51,7 +54,7 @@ func serveHealth() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) NewV1() error {
|
func (c *Controller) NewV1() error {
|
||||||
handlerV1, err := v1.New(c.DBClient, c.Ectx, c.Profiles, c.CAPIChan, c.PluginChannel)
|
handlerV1, err := v1.New(c.DBClient, c.Ectx, c.Profiles, c.CAPIChan, c.PluginChannel, *c.ConsoleConfig, c.DeleteDecisionChannel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,36 +126,27 @@ func (c *Controller) CreateAlert(gctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.DBClient.UpdateMachineLastPush(machineID); err != nil {
|
||||||
|
c.HandleDBErrors(gctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, alert := range input {
|
for _, alert := range input {
|
||||||
alert.MachineID = machineID
|
alert.MachineID = machineID
|
||||||
if len(alert.Decisions) != 0 {
|
|
||||||
for pIdx, profile := range c.Profiles {
|
|
||||||
_, matched, err := csprofiles.EvaluateProfile(profile, alert)
|
|
||||||
if err != nil {
|
|
||||||
gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !matched {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.sendAlertToPluginChannel(alert, uint(pIdx))
|
|
||||||
if profile.OnSuccess == "break" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for pIdx, profile := range c.Profiles {
|
for pIdx, profile := range c.Profiles {
|
||||||
profileDecisions, matched, err := csprofiles.EvaluateProfile(profile, alert)
|
profileDecisions, matched, err := csprofiles.EvaluateProfile(profile, alert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matched {
|
if !matched {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
alert.Decisions = append(alert.Decisions, profileDecisions...)
|
|
||||||
|
if len(alert.Decisions) == 0 { // non manual decision
|
||||||
|
alert.Decisions = append(alert.Decisions, profileDecisions...)
|
||||||
|
}
|
||||||
profileAlert := *alert
|
profileAlert := *alert
|
||||||
c.sendAlertToPluginChannel(&profileAlert, uint(pIdx))
|
c.sendAlertToPluginChannel(&profileAlert, uint(pIdx))
|
||||||
if profile.OnSuccess == "break" {
|
if profile.OnSuccess == "break" {
|
||||||
|
@ -164,7 +155,7 @@ func (c *Controller) CreateAlert(gctx *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
alerts, err := c.DBClient.CreateAlert(machineID, input)
|
alertsID, alertsToSend, err := c.DBClient.CreateAlert(machineID, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.HandleDBErrors(gctx, err)
|
c.HandleDBErrors(gctx, err)
|
||||||
return
|
return
|
||||||
|
@ -172,14 +163,14 @@ func (c *Controller) CreateAlert(gctx *gin.Context) {
|
||||||
|
|
||||||
if c.CAPIChan != nil {
|
if c.CAPIChan != nil {
|
||||||
select {
|
select {
|
||||||
case c.CAPIChan <- input:
|
case c.CAPIChan <- alertsToSend:
|
||||||
log.Debug("alert sent to CAPI channel")
|
log.Debug("alert sent to CAPI channel")
|
||||||
default:
|
default:
|
||||||
log.Warning("Cannot send alert to Central API channel")
|
log.Warning("Cannot send alert to Central API channel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gctx.JSON(http.StatusCreated, alerts)
|
gctx.JSON(http.StatusCreated, alertsID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,24 +11,28 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
Ectx context.Context
|
Ectx context.Context
|
||||||
DBClient *database.Client
|
DBClient *database.Client
|
||||||
APIKeyHeader string
|
APIKeyHeader string
|
||||||
Middlewares *middlewares.Middlewares
|
Middlewares *middlewares.Middlewares
|
||||||
Profiles []*csconfig.ProfileCfg
|
Profiles []*csconfig.ProfileCfg
|
||||||
CAPIChan chan []*models.Alert
|
CAPIChan chan []*models.Alert
|
||||||
PluginChannel chan csplugin.ProfileAlert
|
PluginChannel chan csplugin.ProfileAlert
|
||||||
|
DeleteDecisionsChannel chan []string
|
||||||
|
ConsoleConfig csconfig.ConsoleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(dbClient *database.Client, ctx context.Context, profiles []*csconfig.ProfileCfg, capiChan chan []*models.Alert, pluginChannel chan csplugin.ProfileAlert) (*Controller, error) {
|
func New(dbClient *database.Client, ctx context.Context, profiles []*csconfig.ProfileCfg, capiChan chan []*models.Alert, pluginChannel chan csplugin.ProfileAlert, consoleConfig csconfig.ConsoleConfig, deleteDecisionsChannel chan []string) (*Controller, error) {
|
||||||
var err error
|
var err error
|
||||||
v1 := &Controller{
|
v1 := &Controller{
|
||||||
Ectx: ctx,
|
Ectx: ctx,
|
||||||
DBClient: dbClient,
|
DBClient: dbClient,
|
||||||
APIKeyHeader: middlewares.APIKeyHeader,
|
APIKeyHeader: middlewares.APIKeyHeader,
|
||||||
Profiles: profiles,
|
Profiles: profiles,
|
||||||
CAPIChan: capiChan,
|
CAPIChan: capiChan,
|
||||||
PluginChannel: pluginChannel,
|
PluginChannel: pluginChannel,
|
||||||
|
DeleteDecisionsChannel: deleteDecisionsChannel,
|
||||||
|
ConsoleConfig: consoleConfig,
|
||||||
}
|
}
|
||||||
v1.Middlewares, err = middlewares.NewMiddlewares(dbClient)
|
v1.Middlewares, err = middlewares.NewMiddlewares(dbClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -83,12 +83,34 @@ func (c *Controller) DeleteDecisionById(gctx *gin.Context) {
|
||||||
NbDeleted: "1",
|
NbDeleted: "1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.ConsoleConfig.ShareDecisions {
|
||||||
|
if c.DeleteDecisionsChannel != nil {
|
||||||
|
select {
|
||||||
|
case c.DeleteDecisionsChannel <- []string{decisionIDStr}:
|
||||||
|
log.Debug("alert sent to delete decisions channel")
|
||||||
|
default:
|
||||||
|
log.Warning("Cannot send alert to delete decisions channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gctx.JSON(http.StatusOK, deleteDecisionResp)
|
gctx.JSON(http.StatusOK, deleteDecisionResp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) DeleteDecisions(gctx *gin.Context) {
|
func (c *Controller) DeleteDecisions(gctx *gin.Context) {
|
||||||
var err error
|
var err error
|
||||||
|
decisionsIDToDelete := make([]string, 0)
|
||||||
|
|
||||||
|
if *c.ConsoleConfig.ShareDecisions {
|
||||||
|
decisionsToDelete, err := c.DBClient.QueryDecisionWithFilter(gctx.Request.URL.Query())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to list decisions to delete to send to console: %s", err)
|
||||||
|
}
|
||||||
|
for _, decision := range decisionsToDelete {
|
||||||
|
decisionsIDToDelete = append(decisionsIDToDelete, strconv.Itoa(decision.ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nbDeleted, err := c.DBClient.SoftDeleteDecisionsWithFilter(gctx.Request.URL.Query())
|
nbDeleted, err := c.DBClient.SoftDeleteDecisionsWithFilter(gctx.Request.URL.Query())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -99,6 +121,17 @@ func (c *Controller) DeleteDecisions(gctx *gin.Context) {
|
||||||
NbDeleted: nbDeleted,
|
NbDeleted: nbDeleted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(decisionsIDToDelete) > 0 {
|
||||||
|
if c.DeleteDecisionsChannel != nil {
|
||||||
|
select {
|
||||||
|
case c.DeleteDecisionsChannel <- decisionsIDToDelete:
|
||||||
|
log.Debug("alert sent to delete decisions channel")
|
||||||
|
default:
|
||||||
|
log.Warning("Cannot send alert to delete decisions channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gctx.JSON(http.StatusOK, deleteDecisionResp)
|
gctx.JSON(http.StatusOK, deleteDecisionResp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,9 +85,11 @@ type LocalApiServerCfg struct {
|
||||||
LogMedia string `yaml:"-"`
|
LogMedia string `yaml:"-"`
|
||||||
OnlineClient *OnlineApiClientCfg `yaml:"online_client"`
|
OnlineClient *OnlineApiClientCfg `yaml:"online_client"`
|
||||||
ProfilesPath string `yaml:"profiles_path,omitempty"`
|
ProfilesPath string `yaml:"profiles_path,omitempty"`
|
||||||
|
ConsoleConfigPath string `yaml:"console_path,omitempty"`
|
||||||
Profiles []*ProfileCfg `yaml:"-"`
|
Profiles []*ProfileCfg `yaml:"-"`
|
||||||
LogLevel *log.Level `yaml:"log_level"`
|
LogLevel *log.Level `yaml:"log_level"`
|
||||||
UseForwardedForHeaders bool `yaml:"use_forwarded_for_headers,omitempty"`
|
UseForwardedForHeaders bool `yaml:"use_forwarded_for_headers,omitempty"`
|
||||||
|
ConsoleConfig *ConsoleConfig `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TLSCfg struct {
|
type TLSCfg struct {
|
||||||
|
@ -105,6 +107,13 @@ func (c *Config) LoadAPIServer() error {
|
||||||
if err := c.API.Server.LoadProfiles(); err != nil {
|
if err := c.API.Server.LoadProfiles(); err != nil {
|
||||||
return errors.Wrap(err, "while loading profiles for LAPI")
|
return errors.Wrap(err, "while loading profiles for LAPI")
|
||||||
}
|
}
|
||||||
|
if c.API.Server.ConsoleConfigPath == "" {
|
||||||
|
c.API.Server.ConsoleConfigPath = DefaultConsoleConfgFilePath
|
||||||
|
}
|
||||||
|
if err := c.API.Server.LoadConsoleConfig(); err != nil {
|
||||||
|
return errors.Wrap(err, "while loading console options")
|
||||||
|
}
|
||||||
|
|
||||||
if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
|
if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
|
||||||
if err := c.API.Server.OnlineClient.Load(); err != nil {
|
if err := c.API.Server.OnlineClient.Load(); err != nil {
|
||||||
return errors.Wrap(err, "loading online client credentials")
|
return errors.Wrap(err, "loading online client credentials")
|
||||||
|
|
|
@ -206,6 +206,14 @@ func TestLoadAPIServer(t *testing.T) {
|
||||||
DbPath: "./tests/test.db",
|
DbPath: "./tests/test.db",
|
||||||
Type: "sqlite",
|
Type: "sqlite",
|
||||||
},
|
},
|
||||||
|
ConsoleConfigPath: "/etc/crowdsec/console_config.yaml",
|
||||||
|
ConsoleConfig: &ConsoleConfig{
|
||||||
|
ShareManualDecisions: new(bool),
|
||||||
|
ShareTaintedScenarios: new(bool),
|
||||||
|
ShareCustomScenarios: new(bool),
|
||||||
|
ShareDecisions: new(bool),
|
||||||
|
ShareSimulatedDecisions: new(bool),
|
||||||
|
},
|
||||||
LogDir: LogDirFullPath,
|
LogDir: LogDirFullPath,
|
||||||
LogMedia: "stdout",
|
LogMedia: "stdout",
|
||||||
OnlineClient: &OnlineApiClientCfg{
|
OnlineClient: &OnlineApiClientCfg{
|
||||||
|
|
96
pkg/csconfig/console.go
Normal file
96
pkg/csconfig/console.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package csconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SEND_CUSTOM_SCENARIOS = "custom"
|
||||||
|
SEND_TAINTED_SCENARIOS = "tainted"
|
||||||
|
SEND_MANUAL_SCENARIOS = "manual"
|
||||||
|
SEND_LIVE_DECISIONS = "live_decisions"
|
||||||
|
SEND_SIMULATED_DECISIONS = "simulated_decisions"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultConsoleConfgFilePath = "/etc/crowdsec/console_config.yaml"
|
||||||
|
|
||||||
|
var CONSOLE_CONFIGS = []string{SEND_CUSTOM_SCENARIOS, SEND_LIVE_DECISIONS, SEND_MANUAL_SCENARIOS, SEND_TAINTED_SCENARIOS, SEND_SIMULATED_DECISIONS}
|
||||||
|
|
||||||
|
type ConsoleConfig struct {
|
||||||
|
ShareManualDecisions *bool `yaml:"share_manual_decisions"`
|
||||||
|
ShareTaintedScenarios *bool `yaml:"share_custom"`
|
||||||
|
ShareCustomScenarios *bool `yaml:"share_tainted"`
|
||||||
|
ShareDecisions *bool `yaml:"share_decisions"`
|
||||||
|
ShareSimulatedDecisions *bool `yaml:"share_simulated_decisions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LocalApiServerCfg) LoadConsoleConfig() error {
|
||||||
|
c.ConsoleConfig = &ConsoleConfig{}
|
||||||
|
if _, err := os.Stat(c.ConsoleConfigPath); err != nil && os.IsNotExist(err) {
|
||||||
|
log.Debugf("no console configuration to load")
|
||||||
|
c.ConsoleConfig.ShareCustomScenarios = new(bool)
|
||||||
|
c.ConsoleConfig.ShareTaintedScenarios = new(bool)
|
||||||
|
c.ConsoleConfig.ShareManualDecisions = new(bool)
|
||||||
|
c.ConsoleConfig.ShareDecisions = new(bool)
|
||||||
|
c.ConsoleConfig.ShareSimulatedDecisions = new(bool)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
yamlFile, err := ioutil.ReadFile(c.ConsoleConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("reading console config file '%s': %s", c.ConsoleConfigPath, err)
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(yamlFile, c.ConsoleConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshaling console config file '%s': %s", c.ConsoleConfigPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ConsoleConfig.ShareCustomScenarios == nil {
|
||||||
|
log.Debugf("no share_custom scenarios found, setting to false")
|
||||||
|
c.ConsoleConfig.ShareCustomScenarios = new(bool)
|
||||||
|
}
|
||||||
|
if c.ConsoleConfig.ShareTaintedScenarios == nil {
|
||||||
|
log.Debugf("no share_tainted scenarios found, setting to false")
|
||||||
|
c.ConsoleConfig.ShareTaintedScenarios = new(bool)
|
||||||
|
}
|
||||||
|
if c.ConsoleConfig.ShareManualDecisions == nil {
|
||||||
|
log.Debugf("no share_manual scenarios found, setting to false")
|
||||||
|
c.ConsoleConfig.ShareManualDecisions = new(bool)
|
||||||
|
}
|
||||||
|
if c.ConsoleConfig.ShareDecisions == nil {
|
||||||
|
log.Debugf("no share_decisions scenarios found, setting to false")
|
||||||
|
c.ConsoleConfig.ShareDecisions = new(bool)
|
||||||
|
}
|
||||||
|
if c.ConsoleConfig.ShareSimulatedDecisions == nil {
|
||||||
|
log.Debugf("no share_simulated_decisions scenarios found, setting to false")
|
||||||
|
c.ConsoleConfig.ShareSimulatedDecisions = new(bool)
|
||||||
|
}
|
||||||
|
log.Debugf("Console configuration '%s' loaded successfully", c.ConsoleConfigPath)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LocalApiServerCfg) DumpConsoleConfig() error {
|
||||||
|
var out []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if out, err = yaml.Marshal(c.ConsoleConfig); err != nil {
|
||||||
|
return errors.Wrapf(err, "while marshaling ConsoleConfig (for %s)", c.ConsoleConfigPath)
|
||||||
|
}
|
||||||
|
if c.ConsoleConfigPath == "" {
|
||||||
|
log.Debugf("Empty console_path, defaulting to %s", DefaultConsoleConfgFilePath)
|
||||||
|
c.ConsoleConfigPath = DefaultConsoleConfgFilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(c.ConsoleConfigPath, out, 0600); err != nil {
|
||||||
|
return errors.Wrapf(err, "while dumping console config to %s", c.ConsoleConfigPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -90,28 +90,31 @@ func formatAlertAsString(machineId string, alert *models.Alert) []string {
|
||||||
return retStr
|
return retStr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateAlert(machineID string, alertList []*models.Alert) ([]string, error) {
|
func (c *Client) CreateAlert(machineID string, alertList []*models.Alert) ([]string, []*models.Alert, error) {
|
||||||
pageStart := 0
|
pageStart := 0
|
||||||
pageEnd := bulkSize
|
pageEnd := bulkSize
|
||||||
ret := []string{}
|
ret := []string{}
|
||||||
|
alertListRet := make([]*models.Alert, 0)
|
||||||
for {
|
for {
|
||||||
if pageEnd >= len(alertList) {
|
if pageEnd >= len(alertList) {
|
||||||
results, err := c.CreateAlertBulk(machineID, alertList[pageStart:])
|
results, alerts, err := c.CreateAlertBulk(machineID, alertList[pageStart:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, fmt.Errorf("unable to create alerts: %s", err)
|
return []string{}, alertListRet, fmt.Errorf("unable to create alerts: %s", err)
|
||||||
}
|
}
|
||||||
ret = append(ret, results...)
|
ret = append(ret, results...)
|
||||||
|
alertListRet = append(alertListRet, alerts...)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
results, err := c.CreateAlertBulk(machineID, alertList[pageStart:pageEnd])
|
results, alerts, err := c.CreateAlertBulk(machineID, alertList[pageStart:pageEnd])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, fmt.Errorf("unable to create alerts: %s", err)
|
return []string{}, alertListRet, fmt.Errorf("unable to create alerts: %s", err)
|
||||||
}
|
}
|
||||||
ret = append(ret, results...)
|
ret = append(ret, results...)
|
||||||
|
alertListRet = append(alertListRet, alerts...)
|
||||||
pageStart += bulkSize
|
pageStart += bulkSize
|
||||||
pageEnd += bulkSize
|
pageEnd += bulkSize
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, alertListRet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*We can't bulk both the alert and the decision at the same time. With new consensus, we want to bulk a single alert with a lot of decisions.*/
|
/*We can't bulk both the alert and the decision at the same time. With new consensus, we want to bulk a single alert with a lot of decisions.*/
|
||||||
|
@ -278,7 +281,7 @@ func (c *Client) UpdateCommunityBlocklist(alertItem *models.Alert) (int, int, in
|
||||||
return alertRef.ID, inserted, deleted, nil
|
return alertRef.ID, inserted, deleted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([]string, error) {
|
func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([]string, []*models.Alert, error) {
|
||||||
|
|
||||||
ret := []string{}
|
ret := []string{}
|
||||||
bulkSize := 20
|
bulkSize := 20
|
||||||
|
@ -293,19 +296,19 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
owner, err := c.QueryMachineByID(machineId)
|
owner, err := c.QueryMachineByID(machineId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Cause(err) != UserNotExists {
|
if errors.Cause(err) != UserNotExists {
|
||||||
return []string{}, errors.Wrapf(QueryFail, "machine '%s': %s", alertItem.MachineID, err)
|
return []string{}, alertList, errors.Wrapf(QueryFail, "machine '%s': %s", alertItem.MachineID, err)
|
||||||
}
|
}
|
||||||
c.Log.Debugf("CreateAlertBulk: Machine Id %s doesn't exist", machineId)
|
c.Log.Debugf("CreateAlertBulk: Machine Id %s doesn't exist", machineId)
|
||||||
owner = nil
|
owner = nil
|
||||||
}
|
}
|
||||||
startAtTime, err := time.Parse(time.RFC3339, *alertItem.StartAt)
|
startAtTime, err := time.Parse(time.RFC3339, *alertItem.StartAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(ParseTimeFail, "start_at field time '%s': %s", *alertItem.StartAt, err)
|
return []string{}, alertList, errors.Wrapf(ParseTimeFail, "start_at field time '%s': %s", *alertItem.StartAt, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopAtTime, err := time.Parse(time.RFC3339, *alertItem.StopAt)
|
stopAtTime, err := time.Parse(time.RFC3339, *alertItem.StopAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(ParseTimeFail, "stop_at field time '%s': %s", *alertItem.StopAt, err)
|
return []string{}, alertList, errors.Wrapf(ParseTimeFail, "stop_at field time '%s': %s", *alertItem.StopAt, err)
|
||||||
}
|
}
|
||||||
/*display proper alert in logs*/
|
/*display proper alert in logs*/
|
||||||
for _, disp := range formatAlertAsString(machineId, alertItem) {
|
for _, disp := range formatAlertAsString(machineId, alertItem) {
|
||||||
|
@ -321,11 +324,11 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
for i, eventItem := range alertItem.Events {
|
for i, eventItem := range alertItem.Events {
|
||||||
ts, err := time.Parse(time.RFC3339, *eventItem.Timestamp)
|
ts, err := time.Parse(time.RFC3339, *eventItem.Timestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(ParseTimeFail, "event timestamp '%s' : %s", *eventItem.Timestamp, err)
|
return []string{}, alertList, errors.Wrapf(ParseTimeFail, "event timestamp '%s' : %s", *eventItem.Timestamp, err)
|
||||||
}
|
}
|
||||||
marshallMetas, err := json.Marshal(eventItem.Meta)
|
marshallMetas, err := json.Marshal(eventItem.Meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(MarshalFail, "event meta '%v' : %s", eventItem.Meta, err)
|
return []string{}, alertList, errors.Wrapf(MarshalFail, "event meta '%v' : %s", eventItem.Meta, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//the serialized field is too big, let's try to progressively strip it
|
//the serialized field is too big, let's try to progressively strip it
|
||||||
|
@ -343,7 +346,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
|
|
||||||
marshallMetas, err = json.Marshal(eventItem.Meta)
|
marshallMetas, err = json.Marshal(eventItem.Meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(MarshalFail, "event meta '%v' : %s", eventItem.Meta, err)
|
return []string{}, alertList, errors.Wrapf(MarshalFail, "event meta '%v' : %s", eventItem.Meta, err)
|
||||||
}
|
}
|
||||||
if event.SerializedValidator(string(marshallMetas)) == nil {
|
if event.SerializedValidator(string(marshallMetas)) == nil {
|
||||||
valid = true
|
valid = true
|
||||||
|
@ -372,7 +375,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
}
|
}
|
||||||
events, err = c.Ent.Event.CreateBulk(eventBulk...).Save(c.CTX)
|
events, err = c.Ent.Event.CreateBulk(eventBulk...).Save(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(BulkError, "creating alert events: %s", err)
|
return []string{}, alertList, errors.Wrapf(BulkError, "creating alert events: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +388,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
}
|
}
|
||||||
metas, err = c.Ent.Meta.CreateBulk(metaBulk...).Save(c.CTX)
|
metas, err = c.Ent.Meta.CreateBulk(metaBulk...).Save(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(BulkError, "creating alert meta: %s", err)
|
return []string{}, alertList, errors.Wrapf(BulkError, "creating alert meta: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,14 +405,14 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
|
|
||||||
duration, err := time.ParseDuration(*decisionItem.Duration)
|
duration, err := time.ParseDuration(*decisionItem.Duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(ParseDurationFail, "decision duration '%v' : %s", decisionItem.Duration, err)
|
return []string{}, alertList, errors.Wrapf(ParseDurationFail, "decision duration '%v' : %s", decisionItem.Duration, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if the scope is IP or Range, convert the value to integers */
|
/*if the scope is IP or Range, convert the value to integers */
|
||||||
if strings.ToLower(*decisionItem.Scope) == "ip" || strings.ToLower(*decisionItem.Scope) == "range" {
|
if strings.ToLower(*decisionItem.Scope) == "ip" || strings.ToLower(*decisionItem.Scope) == "range" {
|
||||||
sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decisionItem.Value)
|
sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decisionItem.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(ParseDurationFail, "invalid addr/range %s : %s", *decisionItem.Value, err)
|
return []string{}, alertList, errors.Wrapf(ParseDurationFail, "invalid addr/range %s : %s", *decisionItem.Value, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
decisionBulk[i] = c.Ent.Decision.Create().
|
decisionBulk[i] = c.Ent.Decision.Create().
|
||||||
|
@ -428,9 +431,13 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
}
|
}
|
||||||
decisions, err = c.Ent.Decision.CreateBulk(decisionBulk...).Save(c.CTX)
|
decisions, err = c.Ent.Decision.CreateBulk(decisionBulk...).Save(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(BulkError, "creating alert decisions: %s", err)
|
return []string{}, alertList, errors.Wrapf(BulkError, "creating alert decisions: %s", err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
for i, decisionItem := range alertItem.Decisions {
|
||||||
|
decisionItem.ID = int64(decisions[i].ID)
|
||||||
|
decisionItem.Until = decisions[i].Until.String()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
alertB := c.Ent.Alert.
|
alertB := c.Ent.Alert.
|
||||||
|
@ -466,7 +473,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
if len(bulk) == bulkSize {
|
if len(bulk) == bulkSize {
|
||||||
alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX)
|
alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(BulkError, "bulk creating alert : %s", err)
|
return []string{}, alertList, errors.Wrapf(BulkError, "bulk creating alert : %s", err)
|
||||||
}
|
}
|
||||||
for _, alert := range alerts {
|
for _, alert := range alerts {
|
||||||
ret = append(ret, strconv.Itoa(alert.ID))
|
ret = append(ret, strconv.Itoa(alert.ID))
|
||||||
|
@ -482,14 +489,22 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
|
||||||
|
|
||||||
alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX)
|
alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, errors.Wrapf(BulkError, "leftovers creating alert : %s", err)
|
return []string{}, alertList, errors.Wrapf(BulkError, "leftovers creating alert : %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, alert := range alerts {
|
for _, alert := range alerts {
|
||||||
ret = append(ret, strconv.Itoa(alert.ID))
|
ret = append(ret, strconv.Itoa(alert.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, nil
|
for i, alertID := range ret {
|
||||||
|
alertIDInt, err := strconv.Atoi(alertID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to convert alert ID '%s' to int: %s", alertID, err)
|
||||||
|
}
|
||||||
|
alertList[i].ID = int64(alertIDInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, alertList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]string) (*ent.AlertQuery, error) {
|
func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]string) (*ent.AlertQuery, error) {
|
||||||
|
|
|
@ -20,6 +20,8 @@ type Machine struct {
|
||||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
// UpdatedAt holds the value of the "updated_at" field.
|
// UpdatedAt holds the value of the "updated_at" field.
|
||||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||||
|
// LastPush holds the value of the "last_push" field.
|
||||||
|
LastPush time.Time `json:"last_push,omitempty"`
|
||||||
// MachineId holds the value of the "machineId" field.
|
// MachineId holds the value of the "machineId" field.
|
||||||
MachineId string `json:"machineId,omitempty"`
|
MachineId string `json:"machineId,omitempty"`
|
||||||
// Password holds the value of the "password" field.
|
// Password holds the value of the "password" field.
|
||||||
|
@ -68,7 +70,7 @@ func (*Machine) scanValues(columns []string) ([]interface{}, error) {
|
||||||
values[i] = new(sql.NullInt64)
|
values[i] = new(sql.NullInt64)
|
||||||
case machine.FieldMachineId, machine.FieldPassword, machine.FieldIpAddress, machine.FieldScenarios, machine.FieldVersion, machine.FieldStatus:
|
case machine.FieldMachineId, machine.FieldPassword, machine.FieldIpAddress, machine.FieldScenarios, machine.FieldVersion, machine.FieldStatus:
|
||||||
values[i] = new(sql.NullString)
|
values[i] = new(sql.NullString)
|
||||||
case machine.FieldCreatedAt, machine.FieldUpdatedAt:
|
case machine.FieldCreatedAt, machine.FieldUpdatedAt, machine.FieldLastPush:
|
||||||
values[i] = new(sql.NullTime)
|
values[i] = new(sql.NullTime)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unexpected column %q for type Machine", columns[i])
|
return nil, fmt.Errorf("unexpected column %q for type Machine", columns[i])
|
||||||
|
@ -103,6 +105,12 @@ func (m *Machine) assignValues(columns []string, values []interface{}) error {
|
||||||
} else if value.Valid {
|
} else if value.Valid {
|
||||||
m.UpdatedAt = value.Time
|
m.UpdatedAt = value.Time
|
||||||
}
|
}
|
||||||
|
case machine.FieldLastPush:
|
||||||
|
if value, ok := values[i].(*sql.NullTime); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field last_push", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
m.LastPush = value.Time
|
||||||
|
}
|
||||||
case machine.FieldMachineId:
|
case machine.FieldMachineId:
|
||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
return fmt.Errorf("unexpected type %T for field machineId", values[i])
|
return fmt.Errorf("unexpected type %T for field machineId", values[i])
|
||||||
|
@ -182,6 +190,8 @@ func (m *Machine) String() string {
|
||||||
builder.WriteString(m.CreatedAt.Format(time.ANSIC))
|
builder.WriteString(m.CreatedAt.Format(time.ANSIC))
|
||||||
builder.WriteString(", updated_at=")
|
builder.WriteString(", updated_at=")
|
||||||
builder.WriteString(m.UpdatedAt.Format(time.ANSIC))
|
builder.WriteString(m.UpdatedAt.Format(time.ANSIC))
|
||||||
|
builder.WriteString(", last_push=")
|
||||||
|
builder.WriteString(m.LastPush.Format(time.ANSIC))
|
||||||
builder.WriteString(", machineId=")
|
builder.WriteString(", machineId=")
|
||||||
builder.WriteString(m.MachineId)
|
builder.WriteString(m.MachineId)
|
||||||
builder.WriteString(", password=<sensitive>")
|
builder.WriteString(", password=<sensitive>")
|
||||||
|
|
|
@ -15,6 +15,8 @@ const (
|
||||||
FieldCreatedAt = "created_at"
|
FieldCreatedAt = "created_at"
|
||||||
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
|
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
|
||||||
FieldUpdatedAt = "updated_at"
|
FieldUpdatedAt = "updated_at"
|
||||||
|
// FieldLastPush holds the string denoting the last_push field in the database.
|
||||||
|
FieldLastPush = "last_push"
|
||||||
// FieldMachineId holds the string denoting the machineid field in the database.
|
// FieldMachineId holds the string denoting the machineid field in the database.
|
||||||
FieldMachineId = "machine_id"
|
FieldMachineId = "machine_id"
|
||||||
// FieldPassword holds the string denoting the password field in the database.
|
// FieldPassword holds the string denoting the password field in the database.
|
||||||
|
@ -47,6 +49,7 @@ var Columns = []string{
|
||||||
FieldID,
|
FieldID,
|
||||||
FieldCreatedAt,
|
FieldCreatedAt,
|
||||||
FieldUpdatedAt,
|
FieldUpdatedAt,
|
||||||
|
FieldLastPush,
|
||||||
FieldMachineId,
|
FieldMachineId,
|
||||||
FieldPassword,
|
FieldPassword,
|
||||||
FieldIpAddress,
|
FieldIpAddress,
|
||||||
|
@ -71,6 +74,8 @@ var (
|
||||||
DefaultCreatedAt func() time.Time
|
DefaultCreatedAt func() time.Time
|
||||||
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
|
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
|
||||||
DefaultUpdatedAt func() time.Time
|
DefaultUpdatedAt func() time.Time
|
||||||
|
// DefaultLastPush holds the default value on creation for the "last_push" field.
|
||||||
|
DefaultLastPush func() time.Time
|
||||||
// ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
|
// ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
|
||||||
ScenariosValidator func(string) error
|
ScenariosValidator func(string) error
|
||||||
// DefaultIsValidated holds the default value on creation for the "isValidated" field.
|
// DefaultIsValidated holds the default value on creation for the "isValidated" field.
|
||||||
|
|
|
@ -107,6 +107,13 @@ func UpdatedAt(v time.Time) predicate.Machine {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastPush applies equality check predicate on the "last_push" field. It's identical to LastPushEQ.
|
||||||
|
func LastPush(v time.Time) predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.EQ(s.C(FieldLastPush), v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// MachineId applies equality check predicate on the "machineId" field. It's identical to MachineIdEQ.
|
// MachineId applies equality check predicate on the "machineId" field. It's identical to MachineIdEQ.
|
||||||
func MachineId(v string) predicate.Machine {
|
func MachineId(v string) predicate.Machine {
|
||||||
return predicate.Machine(func(s *sql.Selector) {
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
@ -308,6 +315,96 @@ func UpdatedAtLTE(v time.Time) predicate.Machine {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastPushEQ applies the EQ predicate on the "last_push" field.
|
||||||
|
func LastPushEQ(v time.Time) predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.EQ(s.C(FieldLastPush), v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushNEQ applies the NEQ predicate on the "last_push" field.
|
||||||
|
func LastPushNEQ(v time.Time) predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.NEQ(s.C(FieldLastPush), v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushIn applies the In predicate on the "last_push" field.
|
||||||
|
func LastPushIn(vs ...time.Time) predicate.Machine {
|
||||||
|
v := make([]interface{}, len(vs))
|
||||||
|
for i := range v {
|
||||||
|
v[i] = vs[i]
|
||||||
|
}
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
// if not arguments were provided, append the FALSE constants,
|
||||||
|
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||||
|
if len(v) == 0 {
|
||||||
|
s.Where(sql.False())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Where(sql.In(s.C(FieldLastPush), v...))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushNotIn applies the NotIn predicate on the "last_push" field.
|
||||||
|
func LastPushNotIn(vs ...time.Time) predicate.Machine {
|
||||||
|
v := make([]interface{}, len(vs))
|
||||||
|
for i := range v {
|
||||||
|
v[i] = vs[i]
|
||||||
|
}
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
// if not arguments were provided, append the FALSE constants,
|
||||||
|
// since we can't apply "IN ()". This will make this predicate falsy.
|
||||||
|
if len(v) == 0 {
|
||||||
|
s.Where(sql.False())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Where(sql.NotIn(s.C(FieldLastPush), v...))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushGT applies the GT predicate on the "last_push" field.
|
||||||
|
func LastPushGT(v time.Time) predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.GT(s.C(FieldLastPush), v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushGTE applies the GTE predicate on the "last_push" field.
|
||||||
|
func LastPushGTE(v time.Time) predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.GTE(s.C(FieldLastPush), v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushLT applies the LT predicate on the "last_push" field.
|
||||||
|
func LastPushLT(v time.Time) predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.LT(s.C(FieldLastPush), v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushLTE applies the LTE predicate on the "last_push" field.
|
||||||
|
func LastPushLTE(v time.Time) predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.LTE(s.C(FieldLastPush), v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushIsNil applies the IsNil predicate on the "last_push" field.
|
||||||
|
func LastPushIsNil() predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.IsNull(s.C(FieldLastPush)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushNotNil applies the NotNil predicate on the "last_push" field.
|
||||||
|
func LastPushNotNil() predicate.Machine {
|
||||||
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
s.Where(sql.NotNull(s.C(FieldLastPush)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// MachineIdEQ applies the EQ predicate on the "machineId" field.
|
// MachineIdEQ applies the EQ predicate on the "machineId" field.
|
||||||
func MachineIdEQ(v string) predicate.Machine {
|
func MachineIdEQ(v string) predicate.Machine {
|
||||||
return predicate.Machine(func(s *sql.Selector) {
|
return predicate.Machine(func(s *sql.Selector) {
|
||||||
|
|
|
@ -49,6 +49,20 @@ func (mc *MachineCreate) SetNillableUpdatedAt(t *time.Time) *MachineCreate {
|
||||||
return mc
|
return mc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLastPush sets the "last_push" field.
|
||||||
|
func (mc *MachineCreate) SetLastPush(t time.Time) *MachineCreate {
|
||||||
|
mc.mutation.SetLastPush(t)
|
||||||
|
return mc
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableLastPush sets the "last_push" field if the given value is not nil.
|
||||||
|
func (mc *MachineCreate) SetNillableLastPush(t *time.Time) *MachineCreate {
|
||||||
|
if t != nil {
|
||||||
|
mc.SetLastPush(*t)
|
||||||
|
}
|
||||||
|
return mc
|
||||||
|
}
|
||||||
|
|
||||||
// SetMachineId sets the "machineId" field.
|
// SetMachineId sets the "machineId" field.
|
||||||
func (mc *MachineCreate) SetMachineId(s string) *MachineCreate {
|
func (mc *MachineCreate) SetMachineId(s string) *MachineCreate {
|
||||||
mc.mutation.SetMachineId(s)
|
mc.mutation.SetMachineId(s)
|
||||||
|
@ -217,6 +231,10 @@ func (mc *MachineCreate) defaults() {
|
||||||
v := machine.DefaultUpdatedAt()
|
v := machine.DefaultUpdatedAt()
|
||||||
mc.mutation.SetUpdatedAt(v)
|
mc.mutation.SetUpdatedAt(v)
|
||||||
}
|
}
|
||||||
|
if _, ok := mc.mutation.LastPush(); !ok {
|
||||||
|
v := machine.DefaultLastPush()
|
||||||
|
mc.mutation.SetLastPush(v)
|
||||||
|
}
|
||||||
if _, ok := mc.mutation.IsValidated(); !ok {
|
if _, ok := mc.mutation.IsValidated(); !ok {
|
||||||
v := machine.DefaultIsValidated
|
v := machine.DefaultIsValidated
|
||||||
mc.mutation.SetIsValidated(v)
|
mc.mutation.SetIsValidated(v)
|
||||||
|
@ -291,6 +309,14 @@ func (mc *MachineCreate) createSpec() (*Machine, *sqlgraph.CreateSpec) {
|
||||||
})
|
})
|
||||||
_node.UpdatedAt = value
|
_node.UpdatedAt = value
|
||||||
}
|
}
|
||||||
|
if value, ok := mc.mutation.LastPush(); ok {
|
||||||
|
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeTime,
|
||||||
|
Value: value,
|
||||||
|
Column: machine.FieldLastPush,
|
||||||
|
})
|
||||||
|
_node.LastPush = value
|
||||||
|
}
|
||||||
if value, ok := mc.mutation.MachineId(); ok {
|
if value, ok := mc.mutation.MachineId(); ok {
|
||||||
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
|
||||||
Type: field.TypeString,
|
Type: field.TypeString,
|
||||||
|
|
|
@ -56,6 +56,26 @@ func (mu *MachineUpdate) SetNillableUpdatedAt(t *time.Time) *MachineUpdate {
|
||||||
return mu
|
return mu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLastPush sets the "last_push" field.
|
||||||
|
func (mu *MachineUpdate) SetLastPush(t time.Time) *MachineUpdate {
|
||||||
|
mu.mutation.SetLastPush(t)
|
||||||
|
return mu
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableLastPush sets the "last_push" field if the given value is not nil.
|
||||||
|
func (mu *MachineUpdate) SetNillableLastPush(t *time.Time) *MachineUpdate {
|
||||||
|
if t != nil {
|
||||||
|
mu.SetLastPush(*t)
|
||||||
|
}
|
||||||
|
return mu
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearLastPush clears the value of the "last_push" field.
|
||||||
|
func (mu *MachineUpdate) ClearLastPush() *MachineUpdate {
|
||||||
|
mu.mutation.ClearLastPush()
|
||||||
|
return mu
|
||||||
|
}
|
||||||
|
|
||||||
// SetMachineId sets the "machineId" field.
|
// SetMachineId sets the "machineId" field.
|
||||||
func (mu *MachineUpdate) SetMachineId(s string) *MachineUpdate {
|
func (mu *MachineUpdate) SetMachineId(s string) *MachineUpdate {
|
||||||
mu.mutation.SetMachineId(s)
|
mu.mutation.SetMachineId(s)
|
||||||
|
@ -291,6 +311,19 @@ func (mu *MachineUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||||
Column: machine.FieldUpdatedAt,
|
Column: machine.FieldUpdatedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if value, ok := mu.mutation.LastPush(); ok {
|
||||||
|
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeTime,
|
||||||
|
Value: value,
|
||||||
|
Column: machine.FieldLastPush,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if mu.mutation.LastPushCleared() {
|
||||||
|
_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeTime,
|
||||||
|
Column: machine.FieldLastPush,
|
||||||
|
})
|
||||||
|
}
|
||||||
if value, ok := mu.mutation.MachineId(); ok {
|
if value, ok := mu.mutation.MachineId(); ok {
|
||||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||||
Type: field.TypeString,
|
Type: field.TypeString,
|
||||||
|
@ -459,6 +492,26 @@ func (muo *MachineUpdateOne) SetNillableUpdatedAt(t *time.Time) *MachineUpdateOn
|
||||||
return muo
|
return muo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLastPush sets the "last_push" field.
|
||||||
|
func (muo *MachineUpdateOne) SetLastPush(t time.Time) *MachineUpdateOne {
|
||||||
|
muo.mutation.SetLastPush(t)
|
||||||
|
return muo
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableLastPush sets the "last_push" field if the given value is not nil.
|
||||||
|
func (muo *MachineUpdateOne) SetNillableLastPush(t *time.Time) *MachineUpdateOne {
|
||||||
|
if t != nil {
|
||||||
|
muo.SetLastPush(*t)
|
||||||
|
}
|
||||||
|
return muo
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearLastPush clears the value of the "last_push" field.
|
||||||
|
func (muo *MachineUpdateOne) ClearLastPush() *MachineUpdateOne {
|
||||||
|
muo.mutation.ClearLastPush()
|
||||||
|
return muo
|
||||||
|
}
|
||||||
|
|
||||||
// SetMachineId sets the "machineId" field.
|
// SetMachineId sets the "machineId" field.
|
||||||
func (muo *MachineUpdateOne) SetMachineId(s string) *MachineUpdateOne {
|
func (muo *MachineUpdateOne) SetMachineId(s string) *MachineUpdateOne {
|
||||||
muo.mutation.SetMachineId(s)
|
muo.mutation.SetMachineId(s)
|
||||||
|
@ -718,6 +771,19 @@ func (muo *MachineUpdateOne) sqlSave(ctx context.Context) (_node *Machine, err e
|
||||||
Column: machine.FieldUpdatedAt,
|
Column: machine.FieldUpdatedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if value, ok := muo.mutation.LastPush(); ok {
|
||||||
|
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeTime,
|
||||||
|
Value: value,
|
||||||
|
Column: machine.FieldLastPush,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if muo.mutation.LastPushCleared() {
|
||||||
|
_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeTime,
|
||||||
|
Column: machine.FieldLastPush,
|
||||||
|
})
|
||||||
|
}
|
||||||
if value, ok := muo.mutation.MachineId(); ok {
|
if value, ok := muo.mutation.MachineId(); ok {
|
||||||
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
|
||||||
Type: field.TypeString,
|
Type: field.TypeString,
|
||||||
|
|
|
@ -137,6 +137,7 @@ var (
|
||||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||||
{Name: "created_at", Type: field.TypeTime},
|
{Name: "created_at", Type: field.TypeTime},
|
||||||
{Name: "updated_at", Type: field.TypeTime},
|
{Name: "updated_at", Type: field.TypeTime},
|
||||||
|
{Name: "last_push", Type: field.TypeTime, Nullable: true},
|
||||||
{Name: "machine_id", Type: field.TypeString, Unique: true},
|
{Name: "machine_id", Type: field.TypeString, Unique: true},
|
||||||
{Name: "password", Type: field.TypeString},
|
{Name: "password", Type: field.TypeString},
|
||||||
{Name: "ip_address", Type: field.TypeString},
|
{Name: "ip_address", Type: field.TypeString},
|
||||||
|
|
|
@ -4986,6 +4986,7 @@ type MachineMutation struct {
|
||||||
id *int
|
id *int
|
||||||
created_at *time.Time
|
created_at *time.Time
|
||||||
updated_at *time.Time
|
updated_at *time.Time
|
||||||
|
last_push *time.Time
|
||||||
machineId *string
|
machineId *string
|
||||||
password *string
|
password *string
|
||||||
ipAddress *string
|
ipAddress *string
|
||||||
|
@ -5153,6 +5154,55 @@ func (m *MachineMutation) ResetUpdatedAt() {
|
||||||
m.updated_at = nil
|
m.updated_at = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLastPush sets the "last_push" field.
|
||||||
|
func (m *MachineMutation) SetLastPush(t time.Time) {
|
||||||
|
m.last_push = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPush returns the value of the "last_push" field in the mutation.
|
||||||
|
func (m *MachineMutation) LastPush() (r time.Time, exists bool) {
|
||||||
|
v := m.last_push
|
||||||
|
if v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return *v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// OldLastPush returns the old "last_push" field's value of the Machine entity.
|
||||||
|
// If the Machine object wasn't provided to the builder, the object is fetched from the database.
|
||||||
|
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||||
|
func (m *MachineMutation) OldLastPush(ctx context.Context) (v time.Time, err error) {
|
||||||
|
if !m.op.Is(OpUpdateOne) {
|
||||||
|
return v, fmt.Errorf("OldLastPush is only allowed on UpdateOne operations")
|
||||||
|
}
|
||||||
|
if m.id == nil || m.oldValue == nil {
|
||||||
|
return v, fmt.Errorf("OldLastPush requires an ID field in the mutation")
|
||||||
|
}
|
||||||
|
oldValue, err := m.oldValue(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return v, fmt.Errorf("querying old value for OldLastPush: %w", err)
|
||||||
|
}
|
||||||
|
return oldValue.LastPush, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearLastPush clears the value of the "last_push" field.
|
||||||
|
func (m *MachineMutation) ClearLastPush() {
|
||||||
|
m.last_push = nil
|
||||||
|
m.clearedFields[machine.FieldLastPush] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPushCleared returns if the "last_push" field was cleared in this mutation.
|
||||||
|
func (m *MachineMutation) LastPushCleared() bool {
|
||||||
|
_, ok := m.clearedFields[machine.FieldLastPush]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetLastPush resets all changes to the "last_push" field.
|
||||||
|
func (m *MachineMutation) ResetLastPush() {
|
||||||
|
m.last_push = nil
|
||||||
|
delete(m.clearedFields, machine.FieldLastPush)
|
||||||
|
}
|
||||||
|
|
||||||
// SetMachineId sets the "machineId" field.
|
// SetMachineId sets the "machineId" field.
|
||||||
func (m *MachineMutation) SetMachineId(s string) {
|
func (m *MachineMutation) SetMachineId(s string) {
|
||||||
m.machineId = &s
|
m.machineId = &s
|
||||||
|
@ -5517,13 +5567,16 @@ func (m *MachineMutation) Type() string {
|
||||||
// order to get all numeric fields that were incremented/decremented, call
|
// order to get all numeric fields that were incremented/decremented, call
|
||||||
// AddedFields().
|
// AddedFields().
|
||||||
func (m *MachineMutation) Fields() []string {
|
func (m *MachineMutation) Fields() []string {
|
||||||
fields := make([]string, 0, 9)
|
fields := make([]string, 0, 10)
|
||||||
if m.created_at != nil {
|
if m.created_at != nil {
|
||||||
fields = append(fields, machine.FieldCreatedAt)
|
fields = append(fields, machine.FieldCreatedAt)
|
||||||
}
|
}
|
||||||
if m.updated_at != nil {
|
if m.updated_at != nil {
|
||||||
fields = append(fields, machine.FieldUpdatedAt)
|
fields = append(fields, machine.FieldUpdatedAt)
|
||||||
}
|
}
|
||||||
|
if m.last_push != nil {
|
||||||
|
fields = append(fields, machine.FieldLastPush)
|
||||||
|
}
|
||||||
if m.machineId != nil {
|
if m.machineId != nil {
|
||||||
fields = append(fields, machine.FieldMachineId)
|
fields = append(fields, machine.FieldMachineId)
|
||||||
}
|
}
|
||||||
|
@ -5557,6 +5610,8 @@ func (m *MachineMutation) Field(name string) (ent.Value, bool) {
|
||||||
return m.CreatedAt()
|
return m.CreatedAt()
|
||||||
case machine.FieldUpdatedAt:
|
case machine.FieldUpdatedAt:
|
||||||
return m.UpdatedAt()
|
return m.UpdatedAt()
|
||||||
|
case machine.FieldLastPush:
|
||||||
|
return m.LastPush()
|
||||||
case machine.FieldMachineId:
|
case machine.FieldMachineId:
|
||||||
return m.MachineId()
|
return m.MachineId()
|
||||||
case machine.FieldPassword:
|
case machine.FieldPassword:
|
||||||
|
@ -5584,6 +5639,8 @@ func (m *MachineMutation) OldField(ctx context.Context, name string) (ent.Value,
|
||||||
return m.OldCreatedAt(ctx)
|
return m.OldCreatedAt(ctx)
|
||||||
case machine.FieldUpdatedAt:
|
case machine.FieldUpdatedAt:
|
||||||
return m.OldUpdatedAt(ctx)
|
return m.OldUpdatedAt(ctx)
|
||||||
|
case machine.FieldLastPush:
|
||||||
|
return m.OldLastPush(ctx)
|
||||||
case machine.FieldMachineId:
|
case machine.FieldMachineId:
|
||||||
return m.OldMachineId(ctx)
|
return m.OldMachineId(ctx)
|
||||||
case machine.FieldPassword:
|
case machine.FieldPassword:
|
||||||
|
@ -5621,6 +5678,13 @@ func (m *MachineMutation) SetField(name string, value ent.Value) error {
|
||||||
}
|
}
|
||||||
m.SetUpdatedAt(v)
|
m.SetUpdatedAt(v)
|
||||||
return nil
|
return nil
|
||||||
|
case machine.FieldLastPush:
|
||||||
|
v, ok := value.(time.Time)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||||
|
}
|
||||||
|
m.SetLastPush(v)
|
||||||
|
return nil
|
||||||
case machine.FieldMachineId:
|
case machine.FieldMachineId:
|
||||||
v, ok := value.(string)
|
v, ok := value.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -5700,6 +5764,9 @@ func (m *MachineMutation) AddField(name string, value ent.Value) error {
|
||||||
// mutation.
|
// mutation.
|
||||||
func (m *MachineMutation) ClearedFields() []string {
|
func (m *MachineMutation) ClearedFields() []string {
|
||||||
var fields []string
|
var fields []string
|
||||||
|
if m.FieldCleared(machine.FieldLastPush) {
|
||||||
|
fields = append(fields, machine.FieldLastPush)
|
||||||
|
}
|
||||||
if m.FieldCleared(machine.FieldScenarios) {
|
if m.FieldCleared(machine.FieldScenarios) {
|
||||||
fields = append(fields, machine.FieldScenarios)
|
fields = append(fields, machine.FieldScenarios)
|
||||||
}
|
}
|
||||||
|
@ -5723,6 +5790,9 @@ func (m *MachineMutation) FieldCleared(name string) bool {
|
||||||
// error if the field is not defined in the schema.
|
// error if the field is not defined in the schema.
|
||||||
func (m *MachineMutation) ClearField(name string) error {
|
func (m *MachineMutation) ClearField(name string) error {
|
||||||
switch name {
|
switch name {
|
||||||
|
case machine.FieldLastPush:
|
||||||
|
m.ClearLastPush()
|
||||||
|
return nil
|
||||||
case machine.FieldScenarios:
|
case machine.FieldScenarios:
|
||||||
m.ClearScenarios()
|
m.ClearScenarios()
|
||||||
return nil
|
return nil
|
||||||
|
@ -5746,6 +5816,9 @@ func (m *MachineMutation) ResetField(name string) error {
|
||||||
case machine.FieldUpdatedAt:
|
case machine.FieldUpdatedAt:
|
||||||
m.ResetUpdatedAt()
|
m.ResetUpdatedAt()
|
||||||
return nil
|
return nil
|
||||||
|
case machine.FieldLastPush:
|
||||||
|
m.ResetLastPush()
|
||||||
|
return nil
|
||||||
case machine.FieldMachineId:
|
case machine.FieldMachineId:
|
||||||
m.ResetMachineId()
|
m.ResetMachineId()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -112,12 +112,16 @@ func init() {
|
||||||
machineDescUpdatedAt := machineFields[1].Descriptor()
|
machineDescUpdatedAt := machineFields[1].Descriptor()
|
||||||
// machine.DefaultUpdatedAt holds the default value on creation for the updated_at field.
|
// machine.DefaultUpdatedAt holds the default value on creation for the updated_at field.
|
||||||
machine.DefaultUpdatedAt = machineDescUpdatedAt.Default.(func() time.Time)
|
machine.DefaultUpdatedAt = machineDescUpdatedAt.Default.(func() time.Time)
|
||||||
|
// machineDescLastPush is the schema descriptor for last_push field.
|
||||||
|
machineDescLastPush := machineFields[2].Descriptor()
|
||||||
|
// machine.DefaultLastPush holds the default value on creation for the last_push field.
|
||||||
|
machine.DefaultLastPush = machineDescLastPush.Default.(func() time.Time)
|
||||||
// machineDescScenarios is the schema descriptor for scenarios field.
|
// machineDescScenarios is the schema descriptor for scenarios field.
|
||||||
machineDescScenarios := machineFields[5].Descriptor()
|
machineDescScenarios := machineFields[6].Descriptor()
|
||||||
// machine.ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
|
// machine.ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
|
||||||
machine.ScenariosValidator = machineDescScenarios.Validators[0].(func(string) error)
|
machine.ScenariosValidator = machineDescScenarios.Validators[0].(func(string) error)
|
||||||
// machineDescIsValidated is the schema descriptor for isValidated field.
|
// machineDescIsValidated is the schema descriptor for isValidated field.
|
||||||
machineDescIsValidated := machineFields[7].Descriptor()
|
machineDescIsValidated := machineFields[8].Descriptor()
|
||||||
// machine.DefaultIsValidated holds the default value on creation for the isValidated field.
|
// machine.DefaultIsValidated holds the default value on creation for the isValidated field.
|
||||||
machine.DefaultIsValidated = machineDescIsValidated.Default.(bool)
|
machine.DefaultIsValidated = machineDescIsValidated.Default.(bool)
|
||||||
metaFields := schema.Meta{}.Fields()
|
metaFields := schema.Meta{}.Fields()
|
||||||
|
|
|
@ -20,6 +20,8 @@ func (Machine) Fields() []ent.Field {
|
||||||
Default(time.Now),
|
Default(time.Now),
|
||||||
field.Time("updated_at").
|
field.Time("updated_at").
|
||||||
Default(time.Now),
|
Default(time.Now),
|
||||||
|
field.Time("last_push").
|
||||||
|
Default(time.Now).Optional(),
|
||||||
field.String("machineId").Unique(),
|
field.String("machineId").Unique(),
|
||||||
field.String("password").Sensitive(),
|
field.String("password").Sensitive(),
|
||||||
field.String("ipAddress"),
|
field.String("ipAddress"),
|
||||||
|
|
|
@ -107,6 +107,14 @@ func (c *Client) DeleteWatcher(name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) UpdateMachineLastPush(machineID string) error {
|
||||||
|
_, err := c.Ent.Machine.Update().Where(machine.MachineIdEQ(machineID)).SetLastPush(time.Now()).Save(c.CTX)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(UpdateFail, "updating machine last_push: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateMachineScenarios(scenarios string, ID int) error {
|
func (c *Client) UpdateMachineScenarios(scenarios string, ID int) error {
|
||||||
_, err := c.Ent.Machine.UpdateOneID(ID).
|
_, err := c.Ent.Machine.UpdateOneID(ID).
|
||||||
SetUpdatedAt(time.Now()).
|
SetUpdatedAt(time.Now()).
|
||||||
|
|
|
@ -6,6 +6,8 @@ package models
|
||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
"github.com/go-openapi/errors"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
|
@ -17,9 +19,16 @@ import (
|
||||||
// swagger:model AddSignalsRequestItem
|
// swagger:model AddSignalsRequestItem
|
||||||
type AddSignalsRequestItem struct {
|
type AddSignalsRequestItem struct {
|
||||||
|
|
||||||
|
// alert id
|
||||||
|
// Required: true
|
||||||
|
AlertID *int64 `json:"alert_id"`
|
||||||
|
|
||||||
// created at
|
// created at
|
||||||
CreatedAt string `json:"created_at,omitempty"`
|
CreatedAt string `json:"created_at,omitempty"`
|
||||||
|
|
||||||
|
// decisions
|
||||||
|
Decisions []*Decision `json:"decisions"`
|
||||||
|
|
||||||
// machine id
|
// machine id
|
||||||
MachineID string `json:"machine_id,omitempty"`
|
MachineID string `json:"machine_id,omitempty"`
|
||||||
|
|
||||||
|
@ -35,6 +44,10 @@ type AddSignalsRequestItem struct {
|
||||||
// Required: true
|
// Required: true
|
||||||
ScenarioHash *string `json:"scenario_hash"`
|
ScenarioHash *string `json:"scenario_hash"`
|
||||||
|
|
||||||
|
// scenario trust
|
||||||
|
// Required: true
|
||||||
|
ScenarioTrust *string `json:"scenario_trust"`
|
||||||
|
|
||||||
// scenario version
|
// scenario version
|
||||||
// Required: true
|
// Required: true
|
||||||
ScenarioVersion *string `json:"scenario_version"`
|
ScenarioVersion *string `json:"scenario_version"`
|
||||||
|
@ -56,6 +69,14 @@ type AddSignalsRequestItem struct {
|
||||||
func (m *AddSignalsRequestItem) Validate(formats strfmt.Registry) error {
|
func (m *AddSignalsRequestItem) Validate(formats strfmt.Registry) error {
|
||||||
var res []error
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateAlertID(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateDecisions(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := m.validateMessage(formats); err != nil {
|
if err := m.validateMessage(formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
@ -68,6 +89,10 @@ func (m *AddSignalsRequestItem) Validate(formats strfmt.Registry) error {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := m.validateScenarioTrust(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := m.validateScenarioVersion(formats); err != nil {
|
if err := m.validateScenarioVersion(formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
@ -90,6 +115,39 @@ func (m *AddSignalsRequestItem) Validate(formats strfmt.Registry) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *AddSignalsRequestItem) validateAlertID(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("alert_id", "body", m.AlertID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddSignalsRequestItem) validateDecisions(formats strfmt.Registry) error {
|
||||||
|
if swag.IsZero(m.Decisions) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(m.Decisions); i++ {
|
||||||
|
if swag.IsZero(m.Decisions[i]) { // not required
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Decisions[i] != nil {
|
||||||
|
if err := m.Decisions[i].Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("decisions" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *AddSignalsRequestItem) validateMessage(formats strfmt.Registry) error {
|
func (m *AddSignalsRequestItem) validateMessage(formats strfmt.Registry) error {
|
||||||
|
|
||||||
if err := validate.Required("message", "body", m.Message); err != nil {
|
if err := validate.Required("message", "body", m.Message); err != nil {
|
||||||
|
@ -117,6 +175,15 @@ func (m *AddSignalsRequestItem) validateScenarioHash(formats strfmt.Registry) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *AddSignalsRequestItem) validateScenarioTrust(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("scenario_trust", "body", m.ScenarioTrust); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *AddSignalsRequestItem) validateScenarioVersion(formats strfmt.Registry) error {
|
func (m *AddSignalsRequestItem) validateScenarioVersion(formats strfmt.Registry) error {
|
||||||
|
|
||||||
if err := validate.Required("scenario_version", "body", m.ScenarioVersion); err != nil {
|
if err := validate.Required("scenario_version", "body", m.ScenarioVersion); err != nil {
|
||||||
|
|
|
@ -6,6 +6,8 @@ package models
|
||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
"github.com/go-openapi/errors"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
|
@ -17,7 +19,7 @@ import (
|
||||||
// swagger:model Decision
|
// swagger:model Decision
|
||||||
type Decision struct {
|
type Decision struct {
|
||||||
|
|
||||||
// duration
|
// the duration of the decisions
|
||||||
// Required: true
|
// Required: true
|
||||||
Duration *string `json:"duration"`
|
Duration *string `json:"duration"`
|
||||||
|
|
||||||
|
@ -45,6 +47,9 @@ type Decision struct {
|
||||||
// Required: true
|
// Required: true
|
||||||
Type *string `json:"type"`
|
Type *string `json:"type"`
|
||||||
|
|
||||||
|
// the date until the decisions must be active
|
||||||
|
Until string `json:"until,omitempty"`
|
||||||
|
|
||||||
// the value of the decision scope : an IP, a range, a username, etc
|
// the value of the decision scope : an IP, a range, a username, etc
|
||||||
// Required: true
|
// Required: true
|
||||||
Value *string `json:"value"`
|
Value *string `json:"value"`
|
||||||
|
@ -138,6 +143,42 @@ func (m *Decision) validateValue(formats strfmt.Registry) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContextValidate validate this decision based on the context it is used
|
||||||
|
func (m *Decision) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.contextValidateID(ctx, formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.contextValidateSimulated(ctx, formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Decision) contextValidateID(ctx context.Context, formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.ReadOnly(ctx, "id", "body", int64(m.ID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Decision) contextValidateSimulated(ctx context.Context, formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.ReadOnly(ctx, "simulated", "body", m.Simulated); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary interface implementation
|
// MarshalBinary interface implementation
|
||||||
func (m *Decision) MarshalBinary() ([]byte, error) {
|
func (m *Decision) MarshalBinary() ([]byte, error) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
|
|
|
@ -45,7 +45,6 @@ paths:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
description: 'Comma separated scopes of decisions to fetch'
|
description: 'Comma separated scopes of decisions to fetch'
|
||||||
example: ip,range,country
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: successful operation
|
description: successful operation
|
||||||
|
@ -768,17 +767,34 @@ definitions:
|
||||||
bouncers:
|
bouncers:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/MetricsSoftInfo'
|
$ref: '#/definitions/MetricsBouncerInfo'
|
||||||
machines:
|
machines:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/MetricsSoftInfo'
|
$ref: '#/definitions/MetricsAgentInfo'
|
||||||
required:
|
required:
|
||||||
- apil_version
|
- apil_version
|
||||||
- bouncers
|
- bouncers
|
||||||
- machines
|
- machines
|
||||||
MetricsSoftInfo:
|
MetricsBouncerInfo:
|
||||||
title: MetricsSoftInfo
|
title: MetricsBouncerInfo
|
||||||
|
description: Software version info (so we can warn users about out-of-date software). The software name and the version are "guessed" from the user-agent
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
custom_name:
|
||||||
|
type: string
|
||||||
|
description: name of the component
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: bouncer type (firewall, php ...)
|
||||||
|
version:
|
||||||
|
type: string
|
||||||
|
description: software version
|
||||||
|
last_pull:
|
||||||
|
type: string
|
||||||
|
description: last bouncer pull date
|
||||||
|
MetricsAgentInfo:
|
||||||
|
title: MetricsAgentInfo
|
||||||
description: Software version info (so we can warn users about out-of-date software). The software name and the version are "guessed" from the user-agent
|
description: Software version info (so we can warn users about out-of-date software). The software name and the version are "guessed" from the user-agent
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -788,6 +804,12 @@ definitions:
|
||||||
version:
|
version:
|
||||||
type: string
|
type: string
|
||||||
description: software version
|
description: software version
|
||||||
|
last_update:
|
||||||
|
type: string
|
||||||
|
description: last agent update date
|
||||||
|
last_push:
|
||||||
|
type: string
|
||||||
|
description: last agent push date
|
||||||
Decision:
|
Decision:
|
||||||
title: Decision
|
title: Decision
|
||||||
type: object
|
type: object
|
||||||
|
@ -809,7 +831,11 @@ definitions:
|
||||||
description: 'the value of the decision scope : an IP, a range, a username, etc'
|
description: 'the value of the decision scope : an IP, a range, a username, etc'
|
||||||
type: string
|
type: string
|
||||||
duration:
|
duration:
|
||||||
|
description: 'the duration of the decisions'
|
||||||
type: string
|
type: string
|
||||||
|
until:
|
||||||
|
type: string
|
||||||
|
description: 'the date until the decisions must be active'
|
||||||
scenario:
|
scenario:
|
||||||
type: string
|
type: string
|
||||||
simulated:
|
simulated:
|
||||||
|
@ -917,9 +943,13 @@ definitions:
|
||||||
- "source"
|
- "source"
|
||||||
- "start_at"
|
- "start_at"
|
||||||
- "stop_at"
|
- "stop_at"
|
||||||
|
- "scenario_trust"
|
||||||
|
- "alert_id"
|
||||||
properties:
|
properties:
|
||||||
scenario_hash:
|
scenario_hash:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
alert_id:
|
||||||
|
type: "integer"
|
||||||
scenario:
|
scenario:
|
||||||
type: "string"
|
type: "string"
|
||||||
created_at:
|
created_at:
|
||||||
|
@ -930,6 +960,8 @@ definitions:
|
||||||
$ref: "#/definitions/Source"
|
$ref: "#/definitions/Source"
|
||||||
scenario_version:
|
scenario_version:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
scenario_trust:
|
||||||
|
type: "string"
|
||||||
message:
|
message:
|
||||||
type: "string"
|
type: "string"
|
||||||
description: "a human readable message"
|
description: "a human readable message"
|
||||||
|
@ -937,6 +969,10 @@ definitions:
|
||||||
type: "string"
|
type: "string"
|
||||||
stop_at:
|
stop_at:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
decisions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Decision'
|
||||||
title: "Signal"
|
title: "Signal"
|
||||||
tags:
|
tags:
|
||||||
- name: bouncers
|
- name: bouncers
|
||||||
|
|
|
@ -6,6 +6,7 @@ package models
|
||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
"github.com/go-openapi/errors"
|
||||||
|
@ -25,11 +26,11 @@ type Metrics struct {
|
||||||
|
|
||||||
// bouncers
|
// bouncers
|
||||||
// Required: true
|
// Required: true
|
||||||
Bouncers []*MetricsSoftInfo `json:"bouncers"`
|
Bouncers []*MetricsBouncerInfo `json:"bouncers"`
|
||||||
|
|
||||||
// machines
|
// machines
|
||||||
// Required: true
|
// Required: true
|
||||||
Machines []*MetricsSoftInfo `json:"machines"`
|
Machines []*MetricsAgentInfo `json:"machines"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates this metrics
|
// Validate validates this metrics
|
||||||
|
@ -113,6 +114,60 @@ func (m *Metrics) validateMachines(formats strfmt.Registry) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContextValidate validate this metrics based on the context it is used
|
||||||
|
func (m *Metrics) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.contextValidateBouncers(ctx, formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.contextValidateMachines(ctx, formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) contextValidateBouncers(ctx context.Context, formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
for i := 0; i < len(m.Bouncers); i++ {
|
||||||
|
|
||||||
|
if m.Bouncers[i] != nil {
|
||||||
|
if err := m.Bouncers[i].ContextValidate(ctx, formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("bouncers" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) contextValidateMachines(ctx context.Context, formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
for i := 0; i < len(m.Machines); i++ {
|
||||||
|
|
||||||
|
if m.Machines[i] != nil {
|
||||||
|
if err := m.Machines[i].ContextValidate(ctx, formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("machines" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary interface implementation
|
// MarshalBinary interface implementation
|
||||||
func (m *Metrics) MarshalBinary() ([]byte, error) {
|
func (m *Metrics) MarshalBinary() ([]byte, error) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
|
|
|
@ -6,16 +6,24 @@ package models
|
||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MetricsSoftInfo MetricsSoftInfo
|
// MetricsAgentInfo MetricsAgentInfo
|
||||||
//
|
//
|
||||||
// Software version info (so we can warn users about out-of-date software). The software name and the version are "guessed" from the user-agent
|
// Software version info (so we can warn users about out-of-date software). The software name and the version are "guessed" from the user-agent
|
||||||
//
|
//
|
||||||
// swagger:model MetricsSoftInfo
|
// swagger:model MetricsAgentInfo
|
||||||
type MetricsSoftInfo struct {
|
type MetricsAgentInfo struct {
|
||||||
|
|
||||||
|
// last agent push date
|
||||||
|
LastPush string `json:"last_push,omitempty"`
|
||||||
|
|
||||||
|
// last agent update date
|
||||||
|
LastUpdate string `json:"last_update,omitempty"`
|
||||||
|
|
||||||
// name of the component
|
// name of the component
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
@ -24,13 +32,18 @@ type MetricsSoftInfo struct {
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates this metrics soft info
|
// Validate validates this metrics agent info
|
||||||
func (m *MetricsSoftInfo) Validate(formats strfmt.Registry) error {
|
func (m *MetricsAgentInfo) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextValidate validates this metrics agent info based on context it is used
|
||||||
|
func (m *MetricsAgentInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalBinary interface implementation
|
// MarshalBinary interface implementation
|
||||||
func (m *MetricsSoftInfo) MarshalBinary() ([]byte, error) {
|
func (m *MetricsAgentInfo) MarshalBinary() ([]byte, error) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -38,8 +51,8 @@ func (m *MetricsSoftInfo) MarshalBinary() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalBinary interface implementation
|
// UnmarshalBinary interface implementation
|
||||||
func (m *MetricsSoftInfo) UnmarshalBinary(b []byte) error {
|
func (m *MetricsAgentInfo) UnmarshalBinary(b []byte) error {
|
||||||
var res MetricsSoftInfo
|
var res MetricsAgentInfo
|
||||||
if err := swag.ReadJSON(b, &res); err != nil {
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
61
pkg/models/metrics_bouncer_info.go
Normal file
61
pkg/models/metrics_bouncer_info.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// Code generated by go-swagger; DO NOT EDIT.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetricsBouncerInfo MetricsBouncerInfo
|
||||||
|
//
|
||||||
|
// Software version info (so we can warn users about out-of-date software). The software name and the version are "guessed" from the user-agent
|
||||||
|
//
|
||||||
|
// swagger:model MetricsBouncerInfo
|
||||||
|
type MetricsBouncerInfo struct {
|
||||||
|
|
||||||
|
// name of the component
|
||||||
|
CustomName string `json:"custom_name,omitempty"`
|
||||||
|
|
||||||
|
// last bouncer pull date
|
||||||
|
LastPull string `json:"last_pull,omitempty"`
|
||||||
|
|
||||||
|
// bouncer type (firewall, php ...)
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// software version
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this metrics bouncer info
|
||||||
|
func (m *MetricsBouncerInfo) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextValidate validates this metrics bouncer info based on context it is used
|
||||||
|
func (m *MetricsBouncerInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *MetricsBouncerInfo) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *MetricsBouncerInfo) UnmarshalBinary(b []byte) error {
|
||||||
|
var res MetricsBouncerInfo
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -199,3 +199,12 @@ func Int32Ptr(i int32) *int32 {
|
||||||
func BoolPtr(b bool) *bool {
|
func BoolPtr(b bool) *bool {
|
||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InSlice(str string, slice []string) bool {
|
||||||
|
for _, item := range slice {
|
||||||
|
if str == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ install -m 644 -D config/patterns/* -t %{buildroot}%{_sysconfdir}/crowdsec/patte
|
||||||
install -m 644 -D config/config.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
install -m 644 -D config/config.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
||||||
install -m 644 -D config/simulation.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
install -m 644 -D config/simulation.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
||||||
install -m 644 -D config/profiles.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
install -m 644 -D config/profiles.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
||||||
|
install -m 644 -D config/console_config.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
||||||
install -m 644 -D %{SOURCE1} %{buildroot}%{_presetdir}
|
install -m 644 -D %{SOURCE1} %{buildroot}%{_presetdir}
|
||||||
|
|
||||||
install -m 551 plugins/notifications/slack/notification-slack %{buildroot}%{_libdir}/%{name}/plugins/
|
install -m 551 plugins/notifications/slack/notification-slack %{buildroot}%{_libdir}/%{name}/plugins/
|
||||||
|
|
|
@ -31,6 +31,8 @@ CSCLI_BIN="./cmd/crowdsec-cli/cscli"
|
||||||
CLIENT_SECRETS="local_api_credentials.yaml"
|
CLIENT_SECRETS="local_api_credentials.yaml"
|
||||||
LAPI_SECRETS="online_api_credentials.yaml"
|
LAPI_SECRETS="online_api_credentials.yaml"
|
||||||
|
|
||||||
|
CONSOLE_FILE="console_config.yaml"
|
||||||
|
|
||||||
BIN_INSTALL_PATH="/usr/local/bin"
|
BIN_INSTALL_PATH="/usr/local/bin"
|
||||||
CROWDSEC_BIN_INSTALLED="${BIN_INSTALL_PATH}/crowdsec"
|
CROWDSEC_BIN_INSTALLED="${BIN_INSTALL_PATH}/crowdsec"
|
||||||
|
|
||||||
|
@ -405,6 +407,7 @@ install_crowdsec() {
|
||||||
install -v -m 644 -D ./config/acquis.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
|
install -v -m 644 -D ./config/acquis.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
|
||||||
install -v -m 644 -D ./config/profiles.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
|
install -v -m 644 -D ./config/profiles.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
|
||||||
install -v -m 644 -D ./config/simulation.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
|
install -v -m 644 -D ./config/simulation.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
|
||||||
|
install -v -m 644 -D ./config/"${CONSOLE_FILE}" "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
|
||||||
|
|
||||||
mkdir -p ${PID_DIR} || exit
|
mkdir -p ${PID_DIR} || exit
|
||||||
PID=${PID_DIR} DATA=${CROWDSEC_DATA_DIR} CFG=${CROWDSEC_CONFIG_PATH} envsubst '$CFG $PID $DATA' < ./config/user.yaml > ${CROWDSEC_CONFIG_PATH}"/user.yaml" || log_fatal "unable to generate user configuration file"
|
PID=${PID_DIR} DATA=${CROWDSEC_DATA_DIR} CFG=${CROWDSEC_CONFIG_PATH} envsubst '$CFG $PID $DATA' < ./config/user.yaml > ${CROWDSEC_CONFIG_PATH}"/user.yaml" || log_fatal "unable to generate user configuration file"
|
||||||
|
|
Loading…
Reference in a new issue