diff --git a/cmd/crowdsec-cli/hubcontext.go b/cmd/crowdsec-cli/hubcontext.go new file mode 100644 index 000000000..3fdd3aac5 --- /dev/null +++ b/cmd/crowdsec-cli/hubcontext.go @@ -0,0 +1,40 @@ +package main + +import ( + "github.com/crowdsecurity/crowdsec/pkg/cwhub" +) + +func NewContextCLI() *itemCLI { + return &itemCLI{ + name: cwhub.CONTEXTS, + singular: "context", + oneOrMore: "context(s)", + help: cliHelp{ + example: `cscli contexts list -a +cscli contexts install crowdsecurity/yyy crowdsecurity/zzz +cscli contexts inspect crowdsecurity/yyy crowdsecurity/zzz +cscli contexts upgrade crowdsecurity/yyy crowdsecurity/zzz +cscli contexts remove crowdsecurity/yyy crowdsecurity/zzz +`, + }, + installHelp: cliHelp{ + example: `cscli contexts install crowdsecurity/yyy crowdsecurity/zzz`, + }, + removeHelp: cliHelp{ + example: `cscli contexts remove crowdsecurity/yyy crowdsecurity/zzz`, + }, + upgradeHelp: cliHelp{ + example: `cscli contexts upgrade crowdsecurity/yyy crowdsecurity/zzz`, + }, + inspectHelp: cliHelp{ + example: `cscli contexts inspect crowdsecurity/yyy crowdsecurity/zzz`, + }, + listHelp: cliHelp{ + example: `cscli contexts list +cscli contexts list -a +cscli contexts list crowdsecurity/yyy crowdsecurity/zzz + +List only enabled contexts unless "-a" or names are specified.`, + }, + } +} diff --git a/cmd/crowdsec-cli/main.go b/cmd/crowdsec-cli/main.go index c9b7d7030..284c7cd31 100644 --- a/cmd/crowdsec-cli/main.go +++ b/cmd/crowdsec-cli/main.go @@ -93,7 +93,7 @@ func initConfig() { } var validArgs = []string{ - "scenarios", "parsers", "collections", "capi", "lapi", "postoverflows", "machines", + "scenarios", "parsers", "collections", "capi", "contexts", "lapi", "postoverflows", "machines", "metrics", "bouncers", "alerts", "decisions", "simulation", "hub", "dashboard", "config", "completion", "version", "console", "notifications", "support", } @@ -246,6 +246,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall rootCmd.AddCommand(NewParserCLI().NewCommand()) rootCmd.AddCommand(NewScenarioCLI().NewCommand()) rootCmd.AddCommand(NewPostOverflowCLI().NewCommand()) + rootCmd.AddCommand(NewContextCLI().NewCommand()) rootCmd.AddCommand(NewAppsecConfigCLI().NewCommand()) rootCmd.AddCommand(NewAppsecRuleCLI().NewCommand()) diff --git a/cmd/crowdsec-cli/support.go b/cmd/crowdsec-cli/support.go index 6395ad6d7..4836f340b 100644 --- a/cmd/crowdsec-cli/support.go +++ b/cmd/crowdsec-cli/support.go @@ -37,6 +37,7 @@ const ( SUPPORT_OS_INFO_PATH = "osinfo.txt" SUPPORT_PARSERS_PATH = "hub/parsers.txt" SUPPORT_SCENARIOS_PATH = "hub/scenarios.txt" + SUPPORT_CONTEXTS_PATH = "hub/scenarios.txt" SUPPORT_COLLECTIONS_PATH = "hub/collections.txt" SUPPORT_POSTOVERFLOWS_PATH = "hub/postoverflows.txt" SUPPORT_BOUNCERS_PATH = "lapi/bouncers.txt" @@ -260,6 +261,7 @@ func NewSupportCmd() *cobra.Command { - Installed parsers list - Installed scenarios list - Installed postoverflows list +- Installed context list - Bouncers list - Machines list - CAPI status @@ -309,6 +311,7 @@ cscli support dump -f /tmp/crowdsec-support.zip infos[SUPPORT_PARSERS_PATH] = []byte(err.Error()) infos[SUPPORT_SCENARIOS_PATH] = []byte(err.Error()) infos[SUPPORT_POSTOVERFLOWS_PATH] = []byte(err.Error()) + infos[SUPPORT_CONTEXTS_PATH] = []byte(err.Error()) infos[SUPPORT_COLLECTIONS_PATH] = []byte(err.Error()) } @@ -344,6 +347,7 @@ cscli support dump -f /tmp/crowdsec-support.zip infos[SUPPORT_PARSERS_PATH] = collectHubItems(hub, cwhub.PARSERS) infos[SUPPORT_SCENARIOS_PATH] = collectHubItems(hub, cwhub.SCENARIOS) infos[SUPPORT_POSTOVERFLOWS_PATH] = collectHubItems(hub, cwhub.POSTOVERFLOWS) + infos[SUPPORT_CONTEXTS_PATH] = collectHubItems(hub, cwhub.POSTOVERFLOWS) infos[SUPPORT_COLLECTIONS_PATH] = collectHubItems(hub, cwhub.COLLECTIONS) } diff --git a/docker/README.md b/docker/README.md index 4d1182fa4..84e65ec07 100644 --- a/docker/README.md +++ b/docker/README.md @@ -320,10 +320,12 @@ config.yaml) each time the container is run. | `PARSERS` | | Parsers to install, separated by space | | `SCENARIOS` | | Scenarios to install, separated by space | | `POSTOVERFLOWS` | | Postoverflows to install, separated by space | +| `CONTEXTS` | | Context files to install, separated by space | | `DISABLE_COLLECTIONS` | | Collections to remove, separated by space: `-e DISABLE_COLLECTIONS="crowdsecurity/linux crowdsecurity/nginx"` | | `DISABLE_PARSERS` | | Parsers to remove, separated by space | | `DISABLE_SCENARIOS` | | Scenarios to remove, separated by space | | `DISABLE_POSTOVERFLOWS` | | Postoverflows to remove, separated by space | +| `DISABLE_POSTOVERFLOWS` | | Context files to remove, separated by space | | | | | | __Log verbosity__ | | | | `LEVEL_INFO` | false | Force INFO level for the container log | diff --git a/docker/docker_start.sh b/docker/docker_start.sh index 61d695f2c..fe0a81f31 100755 --- a/docker/docker_start.sh +++ b/docker/docker_start.sh @@ -300,7 +300,7 @@ fi conf_set_if "$PLUGIN_DIR" '.config_paths.plugin_dir = strenv(PLUGIN_DIR)' -## Install collections, parsers, scenarios & postoverflows +## Install hub items cscli hub update cscli_if_clean collections upgrade crowdsecurity/linux @@ -328,6 +328,11 @@ if [ "$POSTOVERFLOWS" != "" ]; then cscli_if_clean postoverflows install "$(difference "$POSTOVERFLOWS" "$DISABLE_POSTOVERFLOWS")" fi +if [ "$CONTEXTS" != "" ]; then + # shellcheck disable=SC2086 + cscli_if_clean contexts install "$(difference "$CONTEXTS" "$DISABLE_CONTEXTS")" +fi + ## Remove collections, parsers, scenarios & postoverflows if [ "$DISABLE_COLLECTIONS" != "" ]; then # shellcheck disable=SC2086 @@ -349,6 +354,11 @@ if [ "$DISABLE_POSTOVERFLOWS" != "" ]; then cscli_if_clean postoverflows remove "$DISABLE_POSTOVERFLOWS" --force fi +if [ "$DISABLE_CONTEXTS" != "" ]; then + # shellcheck disable=SC2086 + cscli_if_clean contexts remove "$DISABLE_CONTEXTS" --force +fi + ## Register bouncers via env for BOUNCER in $(compgen -A variable | grep -i BOUNCER_KEY); do KEY=$(printf '%s' "${!BOUNCER}") diff --git a/pkg/cwhub/item.go b/pkg/cwhub/item.go index 4d588da3a..7dbe3ebb3 100644 --- a/pkg/cwhub/item.go +++ b/pkg/cwhub/item.go @@ -16,6 +16,7 @@ const ( PARSERS = "parsers" POSTOVERFLOWS = "postoverflows" SCENARIOS = "scenarios" + CONTEXTS = "contexts" APPSEC_CONFIGS = "appsec-configs" APPSEC_RULES = "appsec-rules" ) @@ -29,7 +30,7 @@ const ( var ( // The order is important, as it is used to range over sub-items in collections. - ItemTypes = []string{PARSERS, POSTOVERFLOWS, SCENARIOS, APPSEC_CONFIGS, APPSEC_RULES, COLLECTIONS} + ItemTypes = []string{PARSERS, POSTOVERFLOWS, SCENARIOS, CONTEXTS, APPSEC_CONFIGS, APPSEC_RULES, COLLECTIONS} ) type HubItems map[string]map[string]*Item @@ -120,6 +121,7 @@ type Item struct { PostOverflows []string `json:"postoverflows,omitempty" yaml:"postoverflows,omitempty"` Scenarios []string `json:"scenarios,omitempty" yaml:"scenarios,omitempty"` Collections []string `json:"collections,omitempty" yaml:"collections,omitempty"` + Contexts []string `json:"contexts,omitempty" yaml:"contexts,omitempty"` AppsecConfigs []string `json:"appsec-configs,omitempty" yaml:"appsec-configs,omitempty"` AppsecRules []string `json:"appsec-rules,omitempty" yaml:"appsec-rules,omitempty"` } @@ -231,6 +233,15 @@ func (i *Item) SubItems() []*Item { sub = append(sub, s) } + for _, name := range i.Contexts { + s := i.hub.GetItem(CONTEXTS, name) + if s == nil { + continue + } + + sub = append(sub, s) + } + for _, name := range i.AppsecConfigs { s := i.hub.GetItem(APPSEC_CONFIGS, name) if s == nil { @@ -284,6 +295,12 @@ func (i *Item) logMissingSubItems() { } } + for _, subName := range i.Contexts { + if i.hub.GetItem(CONTEXTS, subName) == nil { + log.Errorf("can't find %s in %s, required by %s", subName, CONTEXTS, i.Name) + } + } + for _, subName := range i.AppsecConfigs { if i.hub.GetItem(APPSEC_CONFIGS, subName) == nil { log.Errorf("can't find %s in %s, required by %s", subName, APPSEC_CONFIGS, i.Name) diff --git a/pkg/cwhub/sync.go b/pkg/cwhub/sync.go index ed3abc2c5..18a93cef3 100644 --- a/pkg/cwhub/sync.go +++ b/pkg/cwhub/sync.go @@ -346,7 +346,7 @@ func (i *Item) checkSubItemVersions() error { // syncDir scans a directory for items, and updates the Hub state accordingly. func (h *Hub) syncDir(dir string) error { - // For each, scan PARSERS, POSTOVERFLOWS, SCENARIOS and COLLECTIONS last + // For each, scan PARSERS, POSTOVERFLOWS... and COLLECTIONS last for _, scan := range ItemTypes { // cpath: top-level item directory, either downloaded or installed items. // i.e. /etc/crowdsec/parsers, /etc/crowdsec/hub/parsers, ... diff --git a/test/bats.mk b/test/bats.mk index 35241b03e..cb452e2b8 100644 --- a/test/bats.mk +++ b/test/bats.mk @@ -83,7 +83,7 @@ bats-build: bats-environment # Create a reusable package with initial configuration + data bats-fixture: bats-check-requirements bats-update-tools - @echo "Creating functional test fixture..." + @echo "Creating functional test fixture." @$(TEST_DIR)/instance-data make # Remove the local crowdsec installation and the fixture config + data diff --git a/test/bats/20_hub.bats b/test/bats/20_hub.bats index 0b222dde0..010431a13 100644 --- a/test/bats/20_hub.bats +++ b/test/bats/20_hub.bats @@ -36,7 +36,7 @@ teardown() { rune -0 cscli hub list assert_output "No items to display" rune -0 cscli hub list -o json - assert_json '{"appsec-configs":[],"appsec-rules":[],parsers:[],scenarios:[],collections:[],postoverflows:[]}' + assert_json '{"appsec-configs":[],"appsec-rules":[],parsers:[],scenarios:[],collections:[],contexts:[],postoverflows:[]}' rune -0 cscli hub list -o raw assert_output 'name,status,version,description,type' @@ -47,6 +47,7 @@ teardown() { assert_output --regexp ".*PARSERS.*crowdsecurity/whitelists.*SCENARIOS.*crowdsecurity/telnet-bf.*" refute_output --partial 'POSTOVERFLOWS' refute_output --partial 'COLLECTIONS' + rune -0 cscli hub list -o json rune -0 jq -e '(.parsers | length == 1) and (.scenarios | length == 1)' <(output) rune -0 cscli hub list -o raw @@ -55,8 +56,11 @@ teardown() { refute_output --partial 'crowdsecurity/iptables' # all items + mkdir -p "$CONFIG_DIR/contexts" + # there are no contexts yet, so we create a local one + touch "$CONFIG_DIR/contexts/mycontext.yaml" rune -0 cscli hub list -a - assert_output --regexp ".*PARSERS.*crowdsecurity/whitelists.*POSTOVERFLOWS.*SCENARIOS.*crowdsecurity/telnet-bf.*COLLECTIONS.*crowdsecurity/iptables.*" + assert_output --regexp ".*PARSERS.*crowdsecurity/whitelists.*POSTOVERFLOWS.*SCENARIOS.*crowdsecurity/telnet-bf.*CONTEXTS.*mycontext.yaml.*COLLECTIONS.*crowdsecurity/iptables.*" rune -0 cscli hub list -a -o json rune -0 jq -e '(.parsers | length > 1) and (.scenarios | length > 1)' <(output) rune -0 cscli hub list -a -o raw @@ -107,6 +111,8 @@ teardown() { assert_stderr --partial "Upgraded 0 postoverflows" assert_stderr --partial "Upgrading scenarios" assert_stderr --partial "Upgraded 0 scenarios" + assert_stderr --partial "Upgrading contexts" + assert_stderr --partial "Upgraded 0 contexts" assert_stderr --partial "Upgrading collections" assert_stderr --partial "Upgraded 0 collections" @@ -134,10 +140,11 @@ teardown() { assert_line "parsers" assert_line "postoverflows" assert_line "scenarios" + assert_line "contexts" assert_line "collections" rune -0 cscli hub types -o human rune -0 yq -o json <(output) - assert_json '["parsers","postoverflows","scenarios","appsec-configs","appsec-rules","collections"]' + assert_json '["parsers","postoverflows","scenarios","contexts","appsec-configs","appsec-rules","collections"]' rune -0 cscli hub types -o json - assert_json '["parsers","postoverflows","scenarios","appsec-configs","appsec-rules","collections"]' + assert_json '["parsers","postoverflows","scenarios","contexts","appsec-configs","appsec-rules","collections"]' } diff --git a/test/lib/setup_file.sh b/test/lib/setup_file.sh index a70d14be3..d841bbe7c 100755 --- a/test/lib/setup_file.sh +++ b/test/lib/setup_file.sh @@ -247,12 +247,14 @@ hub_purge_all() { "$CONFIG_DIR"/collections/* \ "$CONFIG_DIR"/parsers/*/* \ "$CONFIG_DIR"/scenarios/* \ - "$CONFIG_DIR"/postoverflows/* + "$CONFIG_DIR"/postoverflows/* \ + "$CONFIG_DIR"/contexts/* rm -rf \ "$CONFIG_DIR"/hub/collections/* \ "$CONFIG_DIR"/hub/parsers/*/* \ "$CONFIG_DIR"/hub/scenarios/* \ - "$CONFIG_DIR"/hub/postoverflows/* + "$CONFIG_DIR"/hub/postoverflows/* \ + "$CONFIG_DIR"/hub/contexts/* local DATA_DIR DATA_DIR=$(config_get .config_paths.data_dir) # should remove everything except the db (find $DATA_DIR -not -name "crowdsec.db*" -delete),