2020-11-30 09:37:17 +00:00
package main
import (
"context"
2021-12-29 13:08:47 +00:00
"encoding/csv"
2020-11-30 09:37:17 +00:00
"encoding/json"
"fmt"
"net/url"
"os"
2021-12-15 10:39:37 +00:00
"path/filepath"
2020-11-30 09:37:17 +00:00
"strconv"
"strings"
"time"
2022-10-13 10:28:24 +00:00
"github.com/fatih/color"
2020-11-30 09:37:17 +00:00
"github.com/go-openapi/strfmt"
2021-12-15 10:39:37 +00:00
"github.com/jszwec/csvutil"
2020-11-30 09:37:17 +00:00
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
2022-10-07 09:05:35 +00:00
2023-05-25 13:43:39 +00:00
"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
2023-05-23 08:52:47 +00:00
"github.com/crowdsecurity/go-cs-lib/pkg/version"
2022-10-07 09:05:35 +00:00
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/crowdsecurity/crowdsec/pkg/types"
2020-11-30 09:37:17 +00:00
)
var Client * apiclient . ApiClient
2022-03-16 16:29:31 +00:00
func DecisionsToTable ( alerts * models . GetAlertsResponse , printMachine bool ) error {
2020-11-30 09:37:17 +00:00
/*here we cheat a bit : to make it more readable for the user, we dedup some entries*/
2023-03-09 10:56:02 +00:00
spamLimit := make ( map [ string ] bool )
skipped := 0
2020-11-30 09:37:17 +00:00
2022-01-13 15:46:16 +00:00
for aIdx := 0 ; aIdx < len ( * alerts ) ; aIdx ++ {
2020-11-30 09:37:17 +00:00
alertItem := ( * alerts ) [ aIdx ]
newDecisions := make ( [ ] * models . Decision , 0 )
for _ , decisionItem := range alertItem . Decisions {
spamKey := fmt . Sprintf ( "%t:%s:%s:%s" , * decisionItem . Simulated , * decisionItem . Type , * decisionItem . Scope , * decisionItem . Value )
if _ , ok := spamLimit [ spamKey ] ; ok {
2021-12-22 14:45:41 +00:00
skipped ++
2020-11-30 09:37:17 +00:00
continue
}
spamLimit [ spamKey ] = true
newDecisions = append ( newDecisions , decisionItem )
}
alertItem . Decisions = newDecisions
}
if csConfig . Cscli . Output == "raw" {
2021-12-29 13:08:47 +00:00
csvwriter := csv . NewWriter ( os . Stdout )
2022-03-16 16:29:31 +00:00
header := [ ] string { "id" , "source" , "ip" , "reason" , "action" , "country" , "as" , "events_count" , "expiration" , "simulated" , "alert_id" }
if printMachine {
header = append ( header , "machine" )
}
err := csvwriter . Write ( header )
2021-12-29 13:08:47 +00:00
if err != nil {
return err
}
2020-11-30 09:37:17 +00:00
for _ , alertItem := range * alerts {
for _ , decisionItem := range alertItem . Decisions {
2022-03-16 16:29:31 +00:00
raw := [ ] string {
2021-12-29 13:08:47 +00:00
fmt . Sprintf ( "%d" , decisionItem . ID ) ,
2020-11-30 09:37:17 +00:00
* decisionItem . Origin ,
2021-12-29 13:08:47 +00:00
* decisionItem . Scope + ":" + * decisionItem . Value ,
2020-11-30 09:37:17 +00:00
* decisionItem . Scenario ,
* decisionItem . Type ,
alertItem . Source . Cn ,
2022-11-14 08:55:53 +00:00
alertItem . Source . GetAsNumberName ( ) ,
2021-12-29 13:08:47 +00:00
fmt . Sprintf ( "%d" , * alertItem . EventsCount ) ,
2020-11-30 09:37:17 +00:00
* decisionItem . Duration ,
2021-12-29 13:08:47 +00:00
fmt . Sprintf ( "%t" , * decisionItem . Simulated ) ,
fmt . Sprintf ( "%d" , alertItem . ID ) ,
2022-03-16 16:29:31 +00:00
}
if printMachine {
raw = append ( raw , alertItem . MachineID )
}
err := csvwriter . Write ( raw )
2021-12-29 13:08:47 +00:00
if err != nil {
return err
}
2020-11-30 09:37:17 +00:00
}
}
2021-12-29 13:08:47 +00:00
csvwriter . Flush ( )
2020-11-30 09:37:17 +00:00
} else if csConfig . Cscli . Output == "json" {
x , _ := json . MarshalIndent ( alerts , "" , " " )
fmt . Printf ( "%s" , string ( x ) )
} else if csConfig . Cscli . Output == "human" {
if len ( * alerts ) == 0 {
fmt . Println ( "No active decisions" )
return nil
}
2022-10-13 10:28:24 +00:00
decisionsTable ( color . Output , alerts , printMachine )
2021-12-22 14:45:41 +00:00
if skipped > 0 {
fmt . Printf ( "%d duplicated entries skipped\n" , skipped )
}
2020-11-30 09:37:17 +00:00
}
return nil
}
func NewDecisionsCmd ( ) * cobra . Command {
var cmdDecisions = & cobra . Command {
Use : "decisions [action]" ,
Short : "Manage decisions" ,
2021-12-15 10:39:37 +00:00
Long : ` Add/List/Delete/Import decisions from LAPI ` ,
2020-11-30 09:37:17 +00:00
Example : ` cscli decisions [action] [filter] ` ,
2022-04-20 13:44:48 +00:00
Aliases : [ ] string { "decision" } ,
2020-11-30 09:37:17 +00:00
/*TBD example*/
2021-08-31 13:03:47 +00:00
Args : cobra . MinimumNArgs ( 1 ) ,
DisableAutoGenTag : true ,
2022-05-19 08:48:08 +00:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
2021-03-24 17:16:17 +00:00
if err := csConfig . LoadAPIClient ( ) ; err != nil {
2023-06-22 13:01:34 +00:00
return fmt . Errorf ( "loading api client: %w" , err )
2020-11-30 09:37:17 +00:00
}
password := strfmt . Password ( csConfig . API . Client . Credentials . Password )
apiurl , err := url . Parse ( csConfig . API . Client . Credentials . URL )
if err != nil {
2023-06-22 13:01:34 +00:00
return fmt . Errorf ( "parsing api url %s: %w" , csConfig . API . Client . Credentials . URL , err )
2020-11-30 09:37:17 +00:00
}
Client , err = apiclient . NewClient ( & apiclient . Config {
MachineID : csConfig . API . Client . Credentials . Login ,
Password : password ,
2023-05-23 08:52:47 +00:00
UserAgent : fmt . Sprintf ( "crowdsec/%s" , version . String ( ) ) ,
2020-11-30 09:37:17 +00:00
URL : apiurl ,
VersionPrefix : "v1" ,
} )
if err != nil {
2023-06-22 13:01:34 +00:00
return fmt . Errorf ( "creating api client: %w" , err )
2020-11-30 09:37:17 +00:00
}
2022-05-19 08:48:08 +00:00
return nil
2020-11-30 09:37:17 +00:00
} ,
}
2023-01-19 10:02:00 +00:00
cmdDecisions . AddCommand ( NewDecisionsListCmd ( ) )
cmdDecisions . AddCommand ( NewDecisionsAddCmd ( ) )
cmdDecisions . AddCommand ( NewDecisionsDeleteCmd ( ) )
cmdDecisions . AddCommand ( NewDecisionsImportCmd ( ) )
return cmdDecisions
}
func NewDecisionsListCmd ( ) * cobra . Command {
2020-11-30 09:37:17 +00:00
var filter = apiclient . AlertsListOpts {
ValueEquals : new ( string ) ,
ScopeEquals : new ( string ) ,
ScenarioEquals : new ( string ) ,
2022-01-11 13:31:51 +00:00
OriginEquals : new ( string ) ,
2020-11-30 09:37:17 +00:00
IPEquals : new ( string ) ,
RangeEquals : new ( string ) ,
Since : new ( string ) ,
Until : new ( string ) ,
TypeEquals : new ( string ) ,
IncludeCAPI : new ( bool ) ,
2021-10-26 11:33:59 +00:00
Limit : new ( int ) ,
2020-11-30 09:37:17 +00:00
}
NoSimu := new ( bool )
2021-01-15 08:48:39 +00:00
contained := new ( bool )
2022-03-16 16:29:31 +00:00
var printMachine bool
2023-01-19 10:02:00 +00:00
2020-11-30 09:37:17 +00:00
var cmdDecisionsList = & cobra . Command {
Use : "list [options]" ,
Short : "List decisions from LAPI" ,
Example : ` cscli decisions list - i 1.2 .3 .4
cscli decisions list - r 1.2 .3 .0 / 24
cscli decisions list - s crowdsecurity / ssh - bf
cscli decisions list - t ban
` ,
2021-08-31 13:03:47 +00:00
Args : cobra . ExactArgs ( 0 ) ,
DisableAutoGenTag : true ,
2020-11-30 09:37:17 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
var err error
/*take care of shorthand options*/
if err := manageCliDecisionAlerts ( filter . IPEquals , filter . RangeEquals , filter . ScopeEquals , filter . ValueEquals ) ; err != nil {
log . Fatalf ( "%s" , err )
}
filter . ActiveDecisionEquals = new ( bool )
* filter . ActiveDecisionEquals = true
if NoSimu != nil && * NoSimu {
2021-01-15 08:48:39 +00:00
filter . IncludeSimulated = new ( bool )
2020-11-30 09:37:17 +00:00
}
2022-02-14 12:47:15 +00:00
/* nullify the empty entries to avoid bad filter */
2020-11-30 09:37:17 +00:00
if * filter . Until == "" {
filter . Until = nil
2022-11-07 09:36:50 +00:00
} else if strings . HasSuffix ( * filter . Until , "d" ) {
2020-11-30 09:37:17 +00:00
/*time.ParseDuration support hours 'h' as bigger unit, let's make the user's life easier*/
2022-11-07 09:36:50 +00:00
realDuration := strings . TrimSuffix ( * filter . Until , "d" )
days , err := strconv . Atoi ( realDuration )
if err != nil {
printHelp ( cmd )
log . Fatalf ( "Can't parse duration %s, valid durations format: 1d, 4h, 4h15m" , * filter . Until )
2020-11-30 09:37:17 +00:00
}
2022-11-07 09:36:50 +00:00
* filter . Until = fmt . Sprintf ( "%d%s" , days * 24 , "h" )
2020-11-30 09:37:17 +00:00
}
2022-11-07 09:36:50 +00:00
2020-11-30 09:37:17 +00:00
if * filter . Since == "" {
filter . Since = nil
2022-11-07 09:36:50 +00:00
} else if strings . HasSuffix ( * filter . Since , "d" ) {
2020-11-30 09:37:17 +00:00
/*time.ParseDuration support hours 'h' as bigger unit, let's make the user's life easier*/
2022-11-07 09:36:50 +00:00
realDuration := strings . TrimSuffix ( * filter . Since , "d" )
days , err := strconv . Atoi ( realDuration )
if err != nil {
printHelp ( cmd )
log . Fatalf ( "Can't parse duration %s, valid durations format: 1d, 4h, 4h15m" , * filter . Until )
2020-11-30 09:37:17 +00:00
}
2022-11-07 09:36:50 +00:00
* filter . Since = fmt . Sprintf ( "%d%s" , days * 24 , "h" )
2020-11-30 09:37:17 +00:00
}
2021-10-26 11:33:59 +00:00
if * filter . IncludeCAPI {
* filter . Limit = 0
}
2020-11-30 09:37:17 +00:00
if * filter . TypeEquals == "" {
filter . TypeEquals = nil
}
if * filter . ValueEquals == "" {
filter . ValueEquals = nil
}
if * filter . ScopeEquals == "" {
filter . ScopeEquals = nil
}
if * filter . ScenarioEquals == "" {
filter . ScenarioEquals = nil
}
if * filter . IPEquals == "" {
filter . IPEquals = nil
}
if * filter . RangeEquals == "" {
filter . RangeEquals = nil
}
2021-01-15 08:48:39 +00:00
2022-01-11 13:31:51 +00:00
if * filter . OriginEquals == "" {
filter . OriginEquals = nil
}
2021-01-15 08:48:39 +00:00
if contained != nil && * contained {
filter . Contains = new ( bool )
}
2020-11-30 09:37:17 +00:00
alerts , _ , err := Client . Alerts . List ( context . Background ( ) , filter )
if err != nil {
2022-06-22 13:53:53 +00:00
log . Fatalf ( "Unable to list decisions : %v" , err )
2020-11-30 09:37:17 +00:00
}
2022-03-16 16:29:31 +00:00
err = DecisionsToTable ( alerts , printMachine )
2020-11-30 09:37:17 +00:00
if err != nil {
2022-06-22 13:53:53 +00:00
log . Fatalf ( "unable to list decisions : %v" , err )
2020-11-30 09:37:17 +00:00
}
} ,
}
cmdDecisionsList . Flags ( ) . SortFlags = false
cmdDecisionsList . Flags ( ) . BoolVarP ( filter . IncludeCAPI , "all" , "a" , false , "Include decisions from Central API" )
cmdDecisionsList . Flags ( ) . StringVar ( filter . Since , "since" , "" , "restrict to alerts newer than since (ie. 4h, 30d)" )
cmdDecisionsList . Flags ( ) . StringVar ( filter . Until , "until" , "" , "restrict to alerts older than until (ie. 4h, 30d)" )
cmdDecisionsList . Flags ( ) . StringVarP ( filter . TypeEquals , "type" , "t" , "" , "restrict to this decision type (ie. ban,captcha)" )
cmdDecisionsList . Flags ( ) . StringVar ( filter . ScopeEquals , "scope" , "" , "restrict to this scope (ie. ip,range,session)" )
2023-01-31 13:47:44 +00:00
cmdDecisionsList . Flags ( ) . StringVar ( filter . OriginEquals , "origin" , "" , fmt . Sprintf ( "the value to match for the specified origin (%s ...)" , strings . Join ( types . GetOrigins ( ) , "," ) ) )
2020-11-30 09:37:17 +00:00
cmdDecisionsList . Flags ( ) . StringVarP ( filter . ValueEquals , "value" , "v" , "" , "restrict to this value (ie. 1.2.3.4,userName)" )
cmdDecisionsList . Flags ( ) . StringVarP ( filter . ScenarioEquals , "scenario" , "s" , "" , "restrict to this scenario (ie. crowdsecurity/ssh-bf)" )
cmdDecisionsList . Flags ( ) . StringVarP ( filter . IPEquals , "ip" , "i" , "" , "restrict to alerts from this source ip (shorthand for --scope ip --value <IP>)" )
cmdDecisionsList . Flags ( ) . StringVarP ( filter . RangeEquals , "range" , "r" , "" , "restrict to alerts from this source range (shorthand for --scope range --value <RANGE>)" )
2021-10-26 11:33:59 +00:00
cmdDecisionsList . Flags ( ) . IntVarP ( filter . Limit , "limit" , "l" , 100 , "number of alerts to get (use 0 to remove the limit)" )
2020-11-30 09:37:17 +00:00
cmdDecisionsList . Flags ( ) . BoolVar ( NoSimu , "no-simu" , false , "exclude decisions in simulation mode" )
2022-03-16 16:29:31 +00:00
cmdDecisionsList . Flags ( ) . BoolVarP ( & printMachine , "machine" , "m" , false , "print machines that triggered decisions" )
2021-01-15 08:48:39 +00:00
cmdDecisionsList . Flags ( ) . BoolVar ( contained , "contained" , false , "query decisions contained by range" )
2021-01-14 15:27:45 +00:00
2023-01-19 10:02:00 +00:00
return cmdDecisionsList
}
func NewDecisionsAddCmd ( ) * cobra . Command {
2020-11-30 09:37:17 +00:00
var (
addIP string
addRange string
addDuration string
addValue string
addScope string
addReason string
addType string
)
var cmdDecisionsAdd = & cobra . Command {
Use : "add [options]" ,
Short : "Add decision to LAPI" ,
Example : ` cscli decisions add -- ip 1.2 .3 .4
cscli decisions add -- range 1.2 .3 .0 / 24
cscli decisions add -- ip 1.2 .3 .4 -- duration 24 h -- type captcha
cscli decisions add -- scope username -- value foobar
` ,
/*TBD : fix long and example*/
2021-08-31 13:03:47 +00:00
Args : cobra . ExactArgs ( 0 ) ,
DisableAutoGenTag : true ,
2020-11-30 09:37:17 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
var err error
alerts := models . AddAlertsRequest { }
2023-01-31 13:47:44 +00:00
origin := types . CscliOrigin
2020-11-30 09:37:17 +00:00
capacity := int32 ( 0 )
leakSpeed := "0"
eventsCount := int32 ( 1 )
empty := ""
simulated := false
2022-01-19 13:56:05 +00:00
startAt := time . Now ( ) . UTC ( ) . Format ( time . RFC3339 )
stopAt := time . Now ( ) . UTC ( ) . Format ( time . RFC3339 )
createdAt := time . Now ( ) . UTC ( ) . Format ( time . RFC3339 )
2020-11-30 09:37:17 +00:00
/*take care of shorthand options*/
if err := manageCliDecisionAlerts ( & addIP , & addRange , & addScope , & addValue ) ; err != nil {
log . Fatalf ( "%s" , err )
}
if addIP != "" {
addValue = addIP
addScope = types . Ip
} else if addRange != "" {
addValue = addRange
addScope = types . Range
} else if addValue == "" {
2022-03-10 12:55:25 +00:00
printHelp ( cmd )
log . Fatalf ( "Missing arguments, a value is required (--ip, --range or --scope and --value)" )
2020-11-30 09:37:17 +00:00
}
if addReason == "" {
addReason = fmt . Sprintf ( "manual '%s' from '%s'" , addType , csConfig . API . Client . Credentials . Login )
}
decision := models . Decision {
Duration : & addDuration ,
Scope : & addScope ,
Value : & addValue ,
Type : & addType ,
Scenario : & addReason ,
Origin : & origin ,
}
alert := models . Alert {
Capacity : & capacity ,
Decisions : [ ] * models . Decision { & decision } ,
Events : [ ] * models . Event { } ,
EventsCount : & eventsCount ,
Leakspeed : & leakSpeed ,
Message : & addReason ,
ScenarioHash : & empty ,
Scenario : & addReason ,
ScenarioVersion : & empty ,
Simulated : & simulated ,
2023-01-31 13:47:44 +00:00
//setting empty scope/value broke plugins, and it didn't seem to be needed anymore w/ latest papi changes
2020-11-30 09:37:17 +00:00
Source : & models . Source {
AsName : empty ,
AsNumber : empty ,
Cn : empty ,
2022-01-13 15:46:16 +00:00
IP : addValue ,
2023-01-31 13:47:44 +00:00
Range : "" ,
2020-11-30 09:37:17 +00:00
Scope : & addScope ,
Value : & addValue ,
} ,
StartAt : & startAt ,
StopAt : & stopAt ,
CreatedAt : createdAt ,
}
alerts = append ( alerts , & alert )
_ , _ , err = Client . Alerts . Add ( context . Background ( ) , alerts )
if err != nil {
2022-11-29 08:16:07 +00:00
log . Fatal ( err )
2020-11-30 09:37:17 +00:00
}
log . Info ( "Decision successfully added" )
} ,
}
cmdDecisionsAdd . Flags ( ) . SortFlags = false
cmdDecisionsAdd . Flags ( ) . StringVarP ( & addIP , "ip" , "i" , "" , "Source ip (shorthand for --scope ip --value <IP>)" )
cmdDecisionsAdd . Flags ( ) . StringVarP ( & addRange , "range" , "r" , "" , "Range source ip (shorthand for --scope range --value <RANGE>)" )
cmdDecisionsAdd . Flags ( ) . StringVarP ( & addDuration , "duration" , "d" , "4h" , "Decision duration (ie. 1h,4h,30m)" )
cmdDecisionsAdd . Flags ( ) . StringVarP ( & addValue , "value" , "v" , "" , "The value (ie. --scope username --value foobar)" )
cmdDecisionsAdd . Flags ( ) . StringVar ( & addScope , "scope" , types . Ip , "Decision scope (ie. ip,range,username)" )
cmdDecisionsAdd . Flags ( ) . StringVarP ( & addReason , "reason" , "R" , "" , "Decision reason (ie. scenario-name)" )
cmdDecisionsAdd . Flags ( ) . StringVarP ( & addType , "type" , "t" , "ban" , "Decision type (ie. ban,captcha,throttle)" )
2023-01-19 10:02:00 +00:00
return cmdDecisionsAdd
}
func NewDecisionsDeleteCmd ( ) * cobra . Command {
2020-11-30 09:37:17 +00:00
var delFilter = apiclient . DecisionsDeleteOpts {
2022-10-19 12:37:27 +00:00
ScopeEquals : new ( string ) ,
ValueEquals : new ( string ) ,
TypeEquals : new ( string ) ,
IPEquals : new ( string ) ,
RangeEquals : new ( string ) ,
ScenarioEquals : new ( string ) ,
2023-03-08 17:29:20 +00:00
OriginEquals : new ( string ) ,
2020-11-30 09:37:17 +00:00
}
var delDecisionId string
var delDecisionAll bool
2023-01-19 10:02:00 +00:00
contained := new ( bool )
2020-11-30 09:37:17 +00:00
var cmdDecisionsDelete = & cobra . Command {
2021-08-31 13:03:47 +00:00
Use : "delete [options]" ,
Short : "Delete decisions" ,
DisableAutoGenTag : true ,
2022-04-20 13:44:48 +00:00
Aliases : [ ] string { "remove" } ,
2020-11-30 09:37:17 +00:00
Example : ` cscli decisions delete - r 1.2 .3 .0 / 24
cscli decisions delete - i 1.2 .3 .4
cscli decisions delete -- id 42
cscli decisions delete -- type captcha
` ,
/*TBD : refaire le Long/Example*/
PreRun : func ( cmd * cobra . Command , args [ ] string ) {
if delDecisionAll {
return
}
if * delFilter . ScopeEquals == "" && * delFilter . ValueEquals == "" &&
* delFilter . TypeEquals == "" && * delFilter . IPEquals == "" &&
2023-03-08 17:29:20 +00:00
* delFilter . RangeEquals == "" && * delFilter . ScenarioEquals == "" &&
* delFilter . OriginEquals == "" && delDecisionId == "" {
2020-11-30 09:37:17 +00:00
cmd . Usage ( )
log . Fatalln ( "At least one filter or --all must be specified" )
}
} ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
var err error
var decisions * models . DeleteDecisionResponse
/*take care of shorthand options*/
if err := manageCliDecisionAlerts ( delFilter . IPEquals , delFilter . RangeEquals , delFilter . ScopeEquals , delFilter . ValueEquals ) ; err != nil {
log . Fatalf ( "%s" , err )
}
if * delFilter . ScopeEquals == "" {
delFilter . ScopeEquals = nil
}
2023-03-08 17:29:20 +00:00
if * delFilter . OriginEquals == "" {
delFilter . OriginEquals = nil
}
2020-11-30 09:37:17 +00:00
if * delFilter . ValueEquals == "" {
delFilter . ValueEquals = nil
}
2022-10-19 12:37:27 +00:00
if * delFilter . ScenarioEquals == "" {
delFilter . ScenarioEquals = nil
}
2020-11-30 09:37:17 +00:00
if * delFilter . TypeEquals == "" {
delFilter . TypeEquals = nil
}
if * delFilter . IPEquals == "" {
delFilter . IPEquals = nil
}
if * delFilter . RangeEquals == "" {
delFilter . RangeEquals = nil
}
2021-01-15 08:48:39 +00:00
if contained != nil && * contained {
delFilter . Contains = new ( bool )
}
2020-11-30 09:37:17 +00:00
if delDecisionId == "" {
decisions , _ , err = Client . Decisions . Delete ( context . Background ( ) , delFilter )
if err != nil {
2022-06-22 13:53:53 +00:00
log . Fatalf ( "Unable to delete decisions : %v" , err )
2020-11-30 09:37:17 +00:00
}
} else {
2022-08-30 10:38:28 +00:00
if _ , err = strconv . Atoi ( delDecisionId ) ; err != nil {
log . Fatalf ( "id '%s' is not an integer: %v" , delDecisionId , err )
}
2020-11-30 09:37:17 +00:00
decisions , _ , err = Client . Decisions . DeleteOne ( context . Background ( ) , delDecisionId )
if err != nil {
2022-06-22 13:53:53 +00:00
log . Fatalf ( "Unable to delete decision : %v" , err )
2020-11-30 09:37:17 +00:00
}
}
log . Infof ( "%s decision(s) deleted" , decisions . NbDeleted )
} ,
}
cmdDecisionsDelete . Flags ( ) . SortFlags = false
cmdDecisionsDelete . Flags ( ) . StringVarP ( delFilter . IPEquals , "ip" , "i" , "" , "Source ip (shorthand for --scope ip --value <IP>)" )
cmdDecisionsDelete . Flags ( ) . StringVarP ( delFilter . RangeEquals , "range" , "r" , "" , "Range source ip (shorthand for --scope range --value <RANGE>)" )
cmdDecisionsDelete . Flags ( ) . StringVarP ( delFilter . TypeEquals , "type" , "t" , "" , "the decision type (ie. ban,captcha)" )
cmdDecisionsDelete . Flags ( ) . StringVarP ( delFilter . ValueEquals , "value" , "v" , "" , "the value to match for in the specified scope" )
2022-10-19 12:37:27 +00:00
cmdDecisionsDelete . Flags ( ) . StringVarP ( delFilter . ScenarioEquals , "scenario" , "s" , "" , "the scenario name (ie. crowdsecurity/ssh-bf)" )
2023-03-08 17:29:20 +00:00
cmdDecisionsDelete . Flags ( ) . StringVar ( delFilter . OriginEquals , "origin" , "" , fmt . Sprintf ( "the value to match for the specified origin (%s ...)" , strings . Join ( types . GetOrigins ( ) , "," ) ) )
2022-10-19 12:37:27 +00:00
cmdDecisionsDelete . Flags ( ) . StringVar ( & delDecisionId , "id" , "" , "decision id" )
2020-11-30 09:37:17 +00:00
cmdDecisionsDelete . Flags ( ) . BoolVar ( & delDecisionAll , "all" , false , "delete all decisions" )
2021-01-15 08:48:39 +00:00
cmdDecisionsDelete . Flags ( ) . BoolVar ( contained , "contained" , false , "query decisions contained by range" )
2021-01-14 15:27:45 +00:00
2023-01-19 10:02:00 +00:00
return cmdDecisionsDelete
}
func NewDecisionsImportCmd ( ) * cobra . Command {
2021-12-15 10:39:37 +00:00
var (
2023-01-19 10:02:00 +00:00
defaultDuration = "4h"
defaultScope = "ip"
defaultType = "ban"
defaultReason = "manual"
2023-01-31 13:47:44 +00:00
importDuration string
importScope string
importReason string
importType string
importFile string
2023-05-11 12:33:18 +00:00
batchSize int
2021-12-15 10:39:37 +00:00
)
var cmdDecisionImport = & cobra . Command {
Use : "import [options]" ,
Short : "Import decisions from json or csv file" ,
Long : "expected format :\n" +
"csv : any of duration,origin,reason,scope,type,value, with a header line\n" +
` json : { "duration" : "24h", "origin" : "my-list", "reason" : "my_scenario", "scope" : "ip", "type" : "ban", "value" : "x.y.z.z"} ` ,
DisableAutoGenTag : true ,
Example : ` decisions . csv :
duration , scope , value
24 h , ip , 1.2 .3 .4
cscsli decisions import - i decisions . csv
decisions . json :
[ { "duration" : "4h" , "scope" : "ip" , "type" : "ban" , "value" : "1.2.3.4" } ]
` ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
if importFile == "" {
2022-04-27 09:04:12 +00:00
log . Fatalf ( "Please provide a input file containing decisions with -i flag" )
2021-12-15 10:39:37 +00:00
}
csvData , err := os . ReadFile ( importFile )
if err != nil {
log . Fatalf ( "unable to open '%s': %s" , importFile , err )
}
type decisionRaw struct {
Duration string ` csv:"duration,omitempty" json:"duration,omitempty" `
Origin string ` csv:"origin,omitempty" json:"origin,omitempty" `
Scenario string ` csv:"reason,omitempty" json:"reason,omitempty" `
Scope string ` csv:"scope,omitempty" json:"scope,omitempty" `
Type string ` csv:"type,omitempty" json:"type,omitempty" `
Value string ` csv:"value" json:"value" `
}
var decisionsListRaw [ ] decisionRaw
switch fileFormat := filepath . Ext ( importFile ) ; fileFormat {
case ".json" :
if err := json . Unmarshal ( csvData , & decisionsListRaw ) ; err != nil {
log . Fatalf ( "unable to unmarshall json: '%s'" , err )
}
case ".csv" :
if err := csvutil . Unmarshal ( csvData , & decisionsListRaw ) ; err != nil {
log . Fatalf ( "unable to unmarshall csv: '%s'" , err )
}
default :
log . Fatalf ( "file format not supported for '%s'. supported format are 'json' and 'csv'" , importFile )
}
decisionsList := make ( [ ] * models . Decision , 0 )
for i , decisionLine := range decisionsListRaw {
line := i + 2
if decisionLine . Value == "" {
log . Fatalf ( "please provide a 'value' in your csv line %d" , line )
}
/*deal with defaults and cli-override*/
if decisionLine . Duration == "" {
decisionLine . Duration = defaultDuration
log . Debugf ( "No 'duration' line %d, using default value: '%s'" , line , defaultDuration )
}
if importDuration != "" {
decisionLine . Duration = importDuration
log . Debugf ( "'duration' line %d, using supplied value: '%s'" , line , importDuration )
}
2023-01-31 13:47:44 +00:00
decisionLine . Origin = types . CscliImportOrigin
2021-12-15 10:39:37 +00:00
if decisionLine . Scenario == "" {
decisionLine . Scenario = defaultReason
log . Debugf ( "No 'reason' line %d, using value: '%s'" , line , decisionLine . Scenario )
}
if importReason != "" {
decisionLine . Scenario = importReason
log . Debugf ( "No 'reason' line %d, using supplied value: '%s'" , line , importReason )
}
if decisionLine . Type == "" {
decisionLine . Type = defaultType
log . Debugf ( "No 'type' line %d, using default value: '%s'" , line , decisionLine . Type )
}
if importType != "" {
decisionLine . Type = importType
log . Debugf ( "'type' line %d, using supplied value: '%s'" , line , importType )
}
if decisionLine . Scope == "" {
decisionLine . Scope = defaultScope
log . Debugf ( "No 'scope' line %d, using default value: '%s'" , line , decisionLine . Scope )
}
if importScope != "" {
decisionLine . Scope = importScope
log . Debugf ( "'scope' line %d, using supplied value: '%s'" , line , importScope )
}
decision := models . Decision {
2023-05-25 13:43:39 +00:00
Value : ptr . Of ( decisionLine . Value ) ,
Duration : ptr . Of ( decisionLine . Duration ) ,
Origin : ptr . Of ( decisionLine . Origin ) ,
Scenario : ptr . Of ( decisionLine . Scenario ) ,
Type : ptr . Of ( decisionLine . Type ) ,
Scope : ptr . Of ( decisionLine . Scope ) ,
2021-12-15 10:39:37 +00:00
Simulated : new ( bool ) ,
}
decisionsList = append ( decisionsList , & decision )
}
alerts := models . AddAlertsRequest { }
2023-05-11 12:33:18 +00:00
if batchSize > 0 {
for i := 0 ; i < len ( decisionsList ) ; i += batchSize {
end := i + batchSize
if end > len ( decisionsList ) {
end = len ( decisionsList )
}
decisionBatch := decisionsList [ i : end ]
importAlert := models . Alert {
CreatedAt : time . Now ( ) . UTC ( ) . Format ( time . RFC3339 ) ,
2023-05-25 13:43:39 +00:00
Scenario : ptr . Of ( fmt . Sprintf ( "import %s : %d IPs" , importFile , len ( decisionBatch ) ) ) ,
2023-05-11 12:33:18 +00:00
2023-05-25 13:43:39 +00:00
Message : ptr . Of ( "" ) ,
2023-05-11 12:33:18 +00:00
Events : [ ] * models . Event { } ,
Source : & models . Source {
2023-05-25 13:43:39 +00:00
Scope : ptr . Of ( "" ) ,
Value : ptr . Of ( "" ) ,
2023-05-11 12:33:18 +00:00
} ,
2023-05-25 13:43:39 +00:00
StartAt : ptr . Of ( time . Now ( ) . UTC ( ) . Format ( time . RFC3339 ) ) ,
StopAt : ptr . Of ( time . Now ( ) . UTC ( ) . Format ( time . RFC3339 ) ) ,
Capacity : ptr . Of ( int32 ( 0 ) ) ,
Simulated : ptr . Of ( false ) ,
EventsCount : ptr . Of ( int32 ( len ( decisionBatch ) ) ) ,
Leakspeed : ptr . Of ( "" ) ,
ScenarioHash : ptr . Of ( "" ) ,
ScenarioVersion : ptr . Of ( "" ) ,
2023-05-11 12:33:18 +00:00
Decisions : decisionBatch ,
}
alerts = append ( alerts , & importAlert )
}
} else {
importAlert := models . Alert {
CreatedAt : time . Now ( ) . UTC ( ) . Format ( time . RFC3339 ) ,
2023-05-25 13:43:39 +00:00
Scenario : ptr . Of ( fmt . Sprintf ( "import %s : %d IPs" , importFile , len ( decisionsList ) ) ) ,
Message : ptr . Of ( "" ) ,
2023-05-11 12:33:18 +00:00
Events : [ ] * models . Event { } ,
Source : & models . Source {
2023-05-25 13:43:39 +00:00
Scope : ptr . Of ( "" ) ,
Value : ptr . Of ( "" ) ,
2023-05-11 12:33:18 +00:00
} ,
2023-05-25 13:43:39 +00:00
StartAt : ptr . Of ( time . Now ( ) . UTC ( ) . Format ( time . RFC3339 ) ) ,
StopAt : ptr . Of ( time . Now ( ) . UTC ( ) . Format ( time . RFC3339 ) ) ,
Capacity : ptr . Of ( int32 ( 0 ) ) ,
Simulated : ptr . Of ( false ) ,
EventsCount : ptr . Of ( int32 ( len ( decisionsList ) ) ) ,
Leakspeed : ptr . Of ( "" ) ,
ScenarioHash : ptr . Of ( "" ) ,
ScenarioVersion : ptr . Of ( "" ) ,
2023-05-11 12:33:18 +00:00
Decisions : decisionsList ,
}
alerts = append ( alerts , & importAlert )
}
2021-12-15 10:39:37 +00:00
if len ( decisionsList ) > 1000 {
log . Infof ( "You are about to add %d decisions, this may take a while" , len ( decisionsList ) )
}
_ , _ , err = Client . Alerts . Add ( context . Background ( ) , alerts )
if err != nil {
2022-11-29 08:16:07 +00:00
log . Fatal ( err )
2021-12-15 10:39:37 +00:00
}
log . Infof ( "%d decisions successfully imported" , len ( decisionsList ) )
} ,
}
cmdDecisionImport . Flags ( ) . SortFlags = false
cmdDecisionImport . Flags ( ) . StringVarP ( & importFile , "input" , "i" , "" , "Input file" )
cmdDecisionImport . Flags ( ) . StringVarP ( & importDuration , "duration" , "d" , "" , "Decision duration (ie. 1h,4h,30m)" )
cmdDecisionImport . Flags ( ) . StringVar ( & importScope , "scope" , types . Ip , "Decision scope (ie. ip,range,username)" )
cmdDecisionImport . Flags ( ) . StringVarP ( & importReason , "reason" , "R" , "" , "Decision reason (ie. scenario-name)" )
cmdDecisionImport . Flags ( ) . StringVarP ( & importType , "type" , "t" , "" , "Decision type (ie. ban,captcha,throttle)" )
2023-05-11 12:33:18 +00:00
cmdDecisionImport . Flags ( ) . IntVar ( & batchSize , "batch" , 0 , "Split import in batches of N decisions" )
2021-12-15 10:39:37 +00:00
2023-01-19 10:02:00 +00:00
return cmdDecisionImport
2020-11-30 09:37:17 +00:00
}