parent
4dbbd4b3c4
commit
950759f6d6
60 changed files with 4249 additions and 263 deletions
4
.github/workflows/ci_functests-install.yml
vendored
4
.github/workflows/ci_functests-install.yml
vendored
|
@ -21,10 +21,10 @@ jobs:
|
|||
name: Install generated release and perform functional tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.16
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.16
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
|
4
.github/workflows/ci_go-test.yml
vendored
4
.github/workflows/ci_go-test.yml
vendored
|
@ -52,10 +52,10 @@ jobs:
|
|||
--health-timeout=5s
|
||||
--health-retries=3
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.16
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.16
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
|
4
.github/workflows/ci_hub-tests.yml
vendored
4
.github/workflows/ci_hub-tests.yml
vendored
|
@ -19,10 +19,10 @@ jobs:
|
|||
name: Hub Parser/Scenario tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.16
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.16
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
|
|
@ -10,10 +10,10 @@ jobs:
|
|||
name: Build and upload binary package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.16
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.16
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
@ -29,10 +29,10 @@ jobs:
|
|||
name: Build and upload binary package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.16
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.16
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020-2021 crowdsecurity
|
||||
Copyright (c) 2020-2021 Crowdsec
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
116
Makefile
116
Makefile
|
@ -15,6 +15,15 @@ DATA_PREFIX = $(PREFIX)"/var/run/crowdsec/"
|
|||
PID_DIR = $(PREFIX)"/var/run/"
|
||||
CROWDSEC_FOLDER = "./cmd/crowdsec"
|
||||
CSCLI_FOLDER = "./cmd/crowdsec-cli/"
|
||||
HTTP_PLUGIN_FOLDER = "./plugins/notifications/http"
|
||||
SLACK_PLUGIN_FOLDER = "./plugins/notifications/slack"
|
||||
SPLUNK_PLUGIN_FOLDER = "./plugins/notifications/splunk"
|
||||
HTTP_PLUGIN_BIN = "notification-http"
|
||||
SLACK_PLUGIN_BIN = "notification-slack"
|
||||
SPLUNK_PLUGIN_BIN = "notification-splunk"
|
||||
HTTP_PLUGIN_CONFIG = "http.yaml"
|
||||
SLACK_PLUGIN_CONFIG = "slack.yaml"
|
||||
SPLUNK_PLUGIN_CONFIG = "splunk.yaml"
|
||||
CROWDSEC_BIN = "crowdsec"
|
||||
CSCLI_BIN = "cscli"
|
||||
BUILD_CMD = "build"
|
||||
|
@ -53,9 +62,13 @@ RELDIR = crowdsec-$(BUILD_VERSION)
|
|||
|
||||
all: clean test build
|
||||
|
||||
build: goversion crowdsec cscli
|
||||
build: goversion crowdsec cscli plugins
|
||||
|
||||
static: goversion crowdsec_static cscli_static
|
||||
static: crowdsec_static cscli_static plugins_static
|
||||
|
||||
plugins: http-plugin slack-plugin splunk-plugin
|
||||
|
||||
plugins_static: http-plugin_static slack-plugin_static splunk-plugin_static
|
||||
|
||||
goversion:
|
||||
@if [ $(GO_MAJOR_VERSION) -gt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \
|
||||
|
@ -76,74 +89,67 @@ clean:
|
|||
@rm -f *.log
|
||||
@rm -f crowdsec-release.tgz
|
||||
|
||||
cscli:
|
||||
ifeq ($(lastword $(RESPECT_VERSION)), $(CURRENT_GOVERSION))
|
||||
cscli: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CSCLI_FOLDER) build --no-print-directory
|
||||
else
|
||||
@echo "Required golang version is $(REQUIRE_GOVERSION). The current one is $(CURRENT_GOVERSION). Exiting.."
|
||||
@exit 1;
|
||||
endif
|
||||
|
||||
|
||||
crowdsec:
|
||||
ifeq ($(lastword $(RESPECT_VERSION)), $(CURRENT_GOVERSION))
|
||||
crowdsec: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CROWDSEC_FOLDER) build --no-print-directory
|
||||
else
|
||||
@echo "Required golang version is $(REQUIRE_GOVERSION). The current one is $(CURRENT_GOVERSION). Exiting.."
|
||||
@exit 1;
|
||||
endif
|
||||
|
||||
http-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(HTTP_PLUGIN_FOLDER) build --no-print-directory
|
||||
|
||||
slack-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SLACK_PLUGIN_FOLDER) build --no-print-directory
|
||||
|
||||
splunk-plugin: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) build --no-print-directory
|
||||
|
||||
|
||||
cscli_static:
|
||||
ifeq ($(lastword $(RESPECT_VERSION)), $(CURRENT_GOVERSION))
|
||||
cscli_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CSCLI_FOLDER) static --no-print-directory
|
||||
else
|
||||
@echo "Required golang version is $(REQUIRE_GOVERSION). The current one is $(CURRENT_GOVERSION). Exiting.."
|
||||
@exit 1;
|
||||
endif
|
||||
|
||||
|
||||
crowdsec_static:
|
||||
ifeq ($(lastword $(RESPECT_VERSION)), $(CURRENT_GOVERSION))
|
||||
crowdsec_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CROWDSEC_FOLDER) static --no-print-directory
|
||||
else
|
||||
@echo "Required golang version is $(REQUIRE_GOVERSION). The current one is $(CURRENT_GOVERSION). Exiting.."
|
||||
@exit 1;
|
||||
endif
|
||||
|
||||
#.PHONY: test
|
||||
test:
|
||||
ifeq ($(lastword $(RESPECT_VERSION)), $(CURRENT_GOVERSION))
|
||||
http-plugin_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(HTTP_PLUGIN_FOLDER) static --no-print-directory
|
||||
|
||||
slack-plugin_static: goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SLACK_PLUGIN_FOLDER) static --no-print-directory
|
||||
|
||||
splunk-plugin_static:goversion
|
||||
@GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) static --no-print-directory
|
||||
|
||||
test: goversion
|
||||
@$(MAKE) -C $(CROWDSEC_FOLDER) test --no-print-directory
|
||||
else
|
||||
@echo "Required golang version is $(REQUIRE_GOVERSION). The current one is $(CURRENT_GOVERSION). Exiting.."
|
||||
@exit 1;
|
||||
endif
|
||||
|
||||
package:
|
||||
@echo Building Release to dir $(RELDIR)
|
||||
@mkdir -p $(RELDIR)/cmd/crowdsec
|
||||
@mkdir -p $(RELDIR)/cmd/crowdsec-cli
|
||||
@mkdir -p $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER))
|
||||
@mkdir -p $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER))
|
||||
@mkdir -p $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER))
|
||||
|
||||
@cp $(CROWDSEC_FOLDER)/$(CROWDSEC_BIN) $(RELDIR)/cmd/crowdsec
|
||||
@cp $(CSCLI_FOLDER)/$(CSCLI_BIN) $(RELDIR)/cmd/crowdsec-cli
|
||||
@cp $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER))
|
||||
@cp $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER))
|
||||
@cp $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER))
|
||||
@cp $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER))
|
||||
@cp $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER))
|
||||
@cp $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER))
|
||||
@cp -R ./config/ $(RELDIR)
|
||||
@cp wizard.sh $(RELDIR)
|
||||
@cp scripts/test_env.sh $(RELDIR)
|
||||
@tar cvzf crowdsec-release.tgz $(RELDIR)
|
||||
|
||||
.PHONY: check_release
|
||||
check_release:
|
||||
@if [ -d $(RELDIR) ]; then echo "$(RELDIR) already exists, abort" ; exit 1 ; fi
|
||||
|
||||
.PHONY:
|
||||
release: check_release build
|
||||
@echo Building Release to dir $(RELDIR)
|
||||
@mkdir -p $(RELDIR)/cmd/crowdsec
|
||||
@mkdir -p $(RELDIR)/cmd/crowdsec-cli
|
||||
@cp $(CROWDSEC_FOLDER)/$(CROWDSEC_BIN) $(RELDIR)/cmd/crowdsec
|
||||
@cp $(CSCLI_FOLDER)/$(CSCLI_BIN) $(RELDIR)/cmd/crowdsec-cli
|
||||
@cp -R ./config/ $(RELDIR)
|
||||
@cp wizard.sh $(RELDIR)
|
||||
@cp scripts/test_env.sh $(RELDIR)
|
||||
@tar cvzf crowdsec-release.tgz $(RELDIR)
|
||||
release: check_release build package
|
||||
|
||||
.PHONY:
|
||||
release_static: check_release static
|
||||
@echo Building Release to dir $(RELDIR)
|
||||
@mkdir -p $(RELDIR)/cmd/crowdsec
|
||||
@mkdir -p $(RELDIR)/cmd/crowdsec-cli
|
||||
@cp $(CROWDSEC_FOLDER)/$(CROWDSEC_BIN) $(RELDIR)/cmd/crowdsec
|
||||
@cp $(CSCLI_FOLDER)/$(CSCLI_BIN) $(RELDIR)/cmd/crowdsec-cli
|
||||
@cp -R ./config/ $(RELDIR)
|
||||
@cp wizard.sh $(RELDIR)
|
||||
@cp scripts/test_env.sh $(RELDIR)
|
||||
@tar cvzf crowdsec-release-static.tgz $(RELDIR)
|
||||
release_static: check_release static package
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/apiserver"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -15,6 +16,21 @@ func initAPIServer(cConfig *csconfig.Config) (*apiserver.APIServer, error) {
|
|||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
|
||||
if hasPlugins(cConfig.API.Server.Profiles) {
|
||||
log.Info("initiating plugin broker")
|
||||
err = pluginBroker.Init(cConfig.API.Server.Profiles, cConfig.ConfigPaths)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
log.Info("initiated plugin broker")
|
||||
apiServer.AttachPluginBroker(&pluginBroker)
|
||||
}
|
||||
|
||||
err = apiServer.InitController()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to run local API")
|
||||
}
|
||||
|
||||
return apiServer, nil
|
||||
}
|
||||
|
||||
|
@ -27,7 +43,14 @@ func serveAPIServer(apiServer *apiserver.APIServer) {
|
|||
log.Fatalf(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
pluginTomb.Go(func() error {
|
||||
pluginBroker.Run(&pluginTomb)
|
||||
return nil
|
||||
})
|
||||
|
||||
<-apiTomb.Dying() // lock until go routine is dying
|
||||
pluginTomb.Kill(nil)
|
||||
log.Infof("serve: shutting down api server")
|
||||
if err := apiServer.Shutdown(); err != nil {
|
||||
return err
|
||||
|
@ -35,3 +58,12 @@ func serveAPIServer(apiServer *apiserver.APIServer) {
|
|||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func hasPlugins(profiles []*csconfig.ProfileCfg) bool {
|
||||
for _, profile := range profiles {
|
||||
if len(profile.Notifications) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
_ "net/http/pprof"
|
||||
"time"
|
||||
|
||||
"sort"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
||||
leaky "github.com/crowdsecurity/crowdsec/pkg/leakybucket"
|
||||
|
@ -33,6 +33,7 @@ var (
|
|||
outputsTomb tomb.Tomb
|
||||
apiTomb tomb.Tomb
|
||||
crowdsecTomb tomb.Tomb
|
||||
pluginTomb tomb.Tomb
|
||||
|
||||
flags *Flags
|
||||
|
||||
|
@ -44,6 +45,7 @@ var (
|
|||
outputEventChan chan types.Event //the buckets init returns its own chan that is used for multiplexing
|
||||
/*settings*/
|
||||
lastProcessedItem time.Time /*keep track of last item timestamp in time-machine. it is used to GC buckets when we dump them.*/
|
||||
pluginBroker csplugin.PluginBroker
|
||||
)
|
||||
|
||||
type Flags struct {
|
||||
|
|
|
@ -49,6 +49,7 @@ func reloadHandler(sig os.Signal, cConfig *csconfig.Config) error {
|
|||
outputsTomb = tomb.Tomb{}
|
||||
apiTomb = tomb.Tomb{}
|
||||
crowdsecTomb = tomb.Tomb{}
|
||||
pluginTomb = tomb.Tomb{}
|
||||
|
||||
cConfig, err = csconfig.NewConfig(flags.ConfigFile, flags.DisableAgent, flags.DisableAPI)
|
||||
if err != nil {
|
||||
|
@ -217,7 +218,7 @@ func Serve(cConfig *csconfig.Config) error {
|
|||
outputsTomb = tomb.Tomb{}
|
||||
apiTomb = tomb.Tomb{}
|
||||
crowdsecTomb = tomb.Tomb{}
|
||||
|
||||
pluginTomb = tomb.Tomb{}
|
||||
if !cConfig.DisableAPI {
|
||||
apiServer, err := initAPIServer(cConfig)
|
||||
if err != nil {
|
||||
|
@ -240,6 +241,7 @@ func Serve(cConfig *csconfig.Config) error {
|
|||
}
|
||||
if flags.TestMode {
|
||||
log.Infof("test done")
|
||||
pluginBroker.Kill()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ config_paths:
|
|||
simulation_path: /etc/crowdsec/simulation.yaml
|
||||
hub_dir: /etc/crowdsec/hub/
|
||||
index_path: /etc/crowdsec/hub/.index.json
|
||||
notification_dir: /etc/crowdsec/notifications/
|
||||
plugin_dir: /var/lib/crowdsec/plugins/
|
||||
crowdsec_service:
|
||||
acquisition_path: /etc/crowdsec/acquis.yaml
|
||||
parser_routines: 1
|
||||
|
|
|
@ -5,4 +5,8 @@ filters:
|
|||
decisions:
|
||||
- type: ban
|
||||
duration: 4h
|
||||
# notifications:
|
||||
# - slack_default # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
|
||||
# - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this.
|
||||
# - http_default # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this.
|
||||
on_success: break
|
||||
|
|
13
debian/rules
vendored
13
debian/rules
vendored
|
@ -28,6 +28,19 @@ override_dh_auto_install:
|
|||
mkdir -p debian/crowdsec/usr/share/crowdsec
|
||||
mkdir -p debian/crowdsec/etc/crowdsec/hub/
|
||||
mkdir -p debian/crowdsec/usr/share/crowdsec/config
|
||||
|
||||
|
||||
mkdir -p debian/crowdsec/var/lib/crowdsec/plugins/
|
||||
mkdir -p debian/crowdsec/etc/crowdsec/notifications/
|
||||
|
||||
install -m 551 plugins/notifications/slack/notification-slack debian/crowdsec/var/lib/crowdsec/plugins/
|
||||
install -m 551 plugins/notifications/http/notification-http debian/crowdsec/var/lib/crowdsec/plugins/
|
||||
install -m 551 plugins/notifications/splunk/notification-splunk debian/crowdsec/var/lib/crowdsec/plugins/
|
||||
|
||||
cp plugins/notifications/slack/slack.yaml debian/crowdsec/etc/crowdsec/notifications/
|
||||
cp plugins/notifications/http/http.yaml debian/crowdsec/etc/crowdsec/notifications/
|
||||
cp plugins/notifications/splunk/splunk.yaml debian/crowdsec/etc/crowdsec/notifications/
|
||||
|
||||
cp cmd/crowdsec/crowdsec debian/crowdsec/usr/bin
|
||||
cp cmd/crowdsec-cli/cscli debian/crowdsec/usr/bin
|
||||
cp wizard.sh debian/crowdsec/usr/share/crowdsec
|
||||
|
|
11
go.mod
11
go.mod
|
@ -5,6 +5,9 @@ go 1.13
|
|||
require (
|
||||
entgo.io/ent v0.7.0
|
||||
github.com/AlecAivazis/survey/v2 v2.2.7
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/alexliesenfeld/health v0.5.1
|
||||
github.com/antonmedv/expr v1.8.9
|
||||
|
@ -32,7 +35,11 @@ require (
|
|||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-querystring v1.0.0
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e
|
||||
github.com/hashicorp/go-hclog v0.14.1
|
||||
github.com/hashicorp/go-plugin v1.4.2
|
||||
github.com/hashicorp/go-version v1.2.1
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/influxdata/go-syslog/v3 v3.0.0
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lib/pq v1.10.0
|
||||
|
@ -41,6 +48,7 @@ require (
|
|||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
|
@ -68,7 +76,8 @@ require (
|
|||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f // indirect
|
||||
google.golang.org/grpc v1.35.0 // indirect
|
||||
google.golang.org/grpc v1.35.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
|
34
go.sum
34
go.sum
|
@ -24,6 +24,12 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
|
|||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
||||
|
@ -144,6 +150,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
|
@ -308,6 +315,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
|
@ -364,9 +372,13 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
|
|||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-plugin v1.4.2 h1:yFvG3ufXXpqiMiZx9HLcaK3XbIqQ1WJFR/F1a2CuVw0=
|
||||
github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
|
@ -383,16 +395,24 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
|||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/go-syslog/v3 v3.0.0 h1:jichmjSZlYK0VMmlz+k4WeOQd7z745YLsvGMqwtYt4I=
|
||||
github.com/influxdata/go-syslog/v3 v3.0.0/go.mod h1:tulsOp+CecTAYC27u9miMgq21GqXRW6VdKbOG+QSP4Q=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
|
@ -459,11 +479,13 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI
|
|||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
|
@ -482,8 +504,12 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ
|
|||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
|
@ -494,6 +520,8 @@ github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
|||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -521,6 +549,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||
github.com/nxadm/tail v1.4.6 h1:11TGpSHY7Esh/i/qnq02Jo5oVrI1Gue8Slbq0ujPZFQ=
|
||||
github.com/nxadm/tail v1.4.6/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
|
@ -743,6 +772,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -813,6 +843,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -898,6 +929,7 @@ google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
@ -911,6 +943,7 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
|
|||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f h1:izedQ6yVIc5mZsRuXzmSreCOlzI0lCU1HpG8yEdMiKw=
|
||||
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
|
@ -923,6 +956,7 @@ google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
|
|
|
@ -7,9 +7,11 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
|
@ -23,11 +25,18 @@ func InitMachineTest() (*gin.Engine, models.WatcherAuthResponse, error) {
|
|||
return nil, models.WatcherAuthResponse{}, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
|
||||
body, err := CreateTestMachine(router)
|
||||
loginResp, err := LoginToTestAPI(router)
|
||||
if err != nil {
|
||||
return nil, models.WatcherAuthResponse{}, fmt.Errorf("%s", err.Error())
|
||||
}
|
||||
return router, loginResp, nil
|
||||
}
|
||||
|
||||
func LoginToTestAPI(router *gin.Engine) (models.WatcherAuthResponse, error) {
|
||||
body, err := CreateTestMachine(router)
|
||||
if err != nil {
|
||||
return models.WatcherAuthResponse{}, fmt.Errorf("%s", err.Error())
|
||||
}
|
||||
err = ValidateMachine("test")
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
|
@ -41,10 +50,14 @@ func InitMachineTest() (*gin.Engine, models.WatcherAuthResponse, error) {
|
|||
loginResp := models.WatcherAuthResponse{}
|
||||
err = json.NewDecoder(w.Body).Decode(&loginResp)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
return models.WatcherAuthResponse{}, fmt.Errorf("%s", err.Error())
|
||||
}
|
||||
return loginResp, nil
|
||||
}
|
||||
|
||||
return router, loginResp, nil
|
||||
func AddAuthHeaders(request *http.Request, authResponse models.WatcherAuthResponse) {
|
||||
request.Header.Add("User-Agent", UserAgent)
|
||||
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authResponse.Token))
|
||||
}
|
||||
|
||||
func TestSimulatedAlert(t *testing.T) {
|
||||
|
@ -61,15 +74,13 @@ func TestSimulatedAlert(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
//exclude decision in simulation mode
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?simulated=false", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), `"message":"Ip 91.121.79.178 performed crowdsecurity/ssh-bf (6 events over `)
|
||||
|
@ -77,8 +88,7 @@ func TestSimulatedAlert(t *testing.T) {
|
|||
//include decision in simulation mode
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?simulated=true", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), `"message":"Ip 91.121.79.178 performed crowdsecurity/ssh-bf (6 events over `)
|
||||
|
@ -94,8 +104,7 @@ func TestCreateAlert(t *testing.T) {
|
|||
// Create Alert with invalid format
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader("test"))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 400, w.Code)
|
||||
|
@ -110,8 +119,7 @@ func TestCreateAlert(t *testing.T) {
|
|||
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("POST", "/v1/alerts", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 500, w.Code)
|
||||
|
@ -126,14 +134,56 @@ func TestCreateAlert(t *testing.T) {
|
|||
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("POST", "/v1/alerts", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 201, w.Code)
|
||||
assert.Equal(t, "[\"1\"]", w.Body.String())
|
||||
}
|
||||
|
||||
func TestCreateAlertChannels(t *testing.T) {
|
||||
|
||||
apiServer, err := NewAPIServer()
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
apiServer.controller.PluginChannel = make(chan csplugin.ProfileAlert)
|
||||
apiServer.InitController()
|
||||
|
||||
loginResp, err := LoginToTestAPI(apiServer.router)
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
|
||||
alertContentBytes, err := ioutil.ReadFile("./tests/alert_ssh-bf.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
alertContent := string(alertContentBytes)
|
||||
|
||||
var pd csplugin.ProfileAlert
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
pd = <-apiServer.controller.PluginChannel
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(alertContent))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
apiServer.controller.Router.ServeHTTP(w, req)
|
||||
break
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
assert.Equal(t, len(pd.Alert.Decisions), 1)
|
||||
apiServer.Close()
|
||||
}
|
||||
|
||||
func TestAlertListFilters(t *testing.T) {
|
||||
router, loginResp, err := InitMachineTest()
|
||||
if err != nil {
|
||||
|
@ -163,15 +213,13 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//create one alert
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
//bad filter
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?test=test", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 500, w.Code)
|
||||
assert.Equal(t, "{\"message\":\"Filter parameter 'test' is unknown (=test): invalid filter\"}", w.Body.String())
|
||||
|
@ -179,8 +227,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//get without filters
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
//check alert and decision
|
||||
|
@ -190,8 +237,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test decision_type filter (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?decision_type=ban", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -200,8 +246,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test decision_type filter (bad value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?decision_type=ratata", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -209,8 +254,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test scope (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?scope=Ip", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -219,8 +263,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test scope (bad value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?scope=rarara", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -228,8 +271,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test scenario (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?scenario=crowdsecurity/ssh-bf", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -238,8 +280,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test scenario (bad value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?scenario=crowdsecurity/nope", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -247,8 +288,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test ip (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?ip=91.121.79.195", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -257,8 +297,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test ip (bad value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?ip=99.122.77.195", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -266,8 +305,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test ip (invalid value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?ip=gruueq", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 500, w.Code)
|
||||
assert.Equal(t, `{"message":"unable to convert 'gruueq' to int: invalid address: invalid ip address / range"}`, w.Body.String())
|
||||
|
@ -275,8 +313,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test range (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?range=91.121.79.0/24&contains=false", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -285,8 +322,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test range
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?range=99.122.77.0/24&contains=false", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -294,8 +330,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test range (invalid value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?range=ratata", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 500, w.Code)
|
||||
assert.Equal(t, `{"message":"unable to convert 'ratata' to int: invalid address: invalid ip address / range"}`, w.Body.String())
|
||||
|
@ -303,8 +338,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test since (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?since=1h", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -313,8 +347,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test since (ok but yelds no results)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?since=1ns", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -322,8 +355,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test since (invalid value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?since=1zuzu", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 500, w.Code)
|
||||
assert.Contains(t, w.Body.String(), `{"message":"while parsing duration: time: unknown unit`)
|
||||
|
@ -331,8 +363,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test until (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?until=1ns", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -341,8 +372,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test until (ok but no return)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?until=1m", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -350,8 +380,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test until (invalid value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?until=1zuzu", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 500, w.Code)
|
||||
assert.Contains(t, w.Body.String(), `{"message":"while parsing duration: time: unknown unit`)
|
||||
|
@ -359,8 +388,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test simulated (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?simulated=true", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -369,8 +397,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test simulated (ok)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?simulated=false", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -379,8 +406,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test has active decision
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?has_active_decision=true", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
||||
|
@ -389,8 +415,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test has active decision
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?has_active_decision=false", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "null", w.Body.String())
|
||||
|
@ -398,8 +423,7 @@ func TestAlertListFilters(t *testing.T) {
|
|||
//test has active decision (invalid value)
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?has_active_decision=ratatqata", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 500, w.Code)
|
||||
assert.Equal(t, `{"message":"'ratatqata' is not a boolean: strconv.ParseBool: parsing \"ratatqata\": invalid syntax: unable to parse type"}`, w.Body.String())
|
||||
|
@ -421,14 +445,12 @@ func TestAlertBulkInsert(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
}
|
||||
|
@ -447,15 +469,13 @@ func TestListAlert(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// List Alert with invalid filter
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts?test=test", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 500, w.Code)
|
||||
assert.Equal(t, "{\"message\":\"Filter parameter 'test' is unknown (=test): invalid filter\"}", w.Body.String())
|
||||
|
@ -463,8 +483,7 @@ func TestListAlert(t *testing.T) {
|
|||
// List Alert
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/v1/alerts", nil)
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
|
@ -515,15 +534,13 @@ func TestDeleteAlert(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(alertContent))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Fail Delete Alert
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/alerts", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
req.RemoteAddr = "127.0.0.2:4242"
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
|
@ -533,8 +550,7 @@ func TestDeleteAlert(t *testing.T) {
|
|||
// Delete Alert
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/alerts", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
req.RemoteAddr = "127.0.0.1:4242"
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -186,10 +187,6 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
|||
controller.CAPIChan = nil
|
||||
}
|
||||
|
||||
if err := controller.Init(); err != nil {
|
||||
return &APIServer{}, err
|
||||
}
|
||||
|
||||
return &APIServer{
|
||||
URL: config.ListenURI,
|
||||
TLS: config.TLS,
|
||||
|
@ -288,3 +285,12 @@ func (s *APIServer) Shutdown() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *APIServer) AttachPluginBroker(broker *csplugin.PluginBroker) {
|
||||
s.controller.PluginChannel = broker.PluginChannel
|
||||
}
|
||||
|
||||
func (s *APIServer) InitController() error {
|
||||
err := s.controller.Init()
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -87,9 +87,8 @@ func LoadTestConfigForwardedFor() csconfig.Config {
|
|||
return config
|
||||
}
|
||||
|
||||
func NewAPITest() (*gin.Engine, error) {
|
||||
func NewAPIServer() (*APIServer, error) {
|
||||
config := LoadTestConfig()
|
||||
|
||||
os.Remove("./ent")
|
||||
apiServer, err := NewServer(config.API.Server)
|
||||
if err != nil {
|
||||
|
@ -97,6 +96,18 @@ func NewAPITest() (*gin.Engine, error) {
|
|||
}
|
||||
log.Printf("Creating new API server")
|
||||
gin.SetMode(gin.TestMode)
|
||||
return apiServer, nil
|
||||
}
|
||||
|
||||
func NewAPITest() (*gin.Engine, error) {
|
||||
apiServer, err := NewAPIServer()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
err = apiServer.InitController()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
router, err := apiServer.Router()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
|
@ -112,6 +123,10 @@ func NewAPITestForwardedFor() (*gin.Engine, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
err = apiServer.InitController()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to run local API: %s", err)
|
||||
}
|
||||
log.Printf("Creating new API server")
|
||||
gin.SetMode(gin.TestMode)
|
||||
router, err := apiServer.Router()
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/alexliesenfeld/health"
|
||||
v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -18,6 +19,7 @@ type Controller struct {
|
|||
Router *gin.Engine
|
||||
Profiles []*csconfig.ProfileCfg
|
||||
CAPIChan chan []*models.Alert
|
||||
PluginChannel chan csplugin.ProfileAlert
|
||||
Log *log.Logger
|
||||
}
|
||||
|
||||
|
@ -49,7 +51,7 @@ func serveHealth() http.HandlerFunc {
|
|||
}
|
||||
|
||||
func (c *Controller) NewV1() error {
|
||||
handlerV1, err := v1.New(c.DBClient, c.Ectx, c.Profiles, c.CAPIChan)
|
||||
handlerV1, err := v1.New(c.DBClient, c.Ectx, c.Profiles, c.CAPIChan, c.PluginChannel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csprofiles"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
|
@ -96,6 +97,15 @@ func FormatAlerts(result []*ent.Alert) models.AddAlertsRequest {
|
|||
return data
|
||||
}
|
||||
|
||||
func (c *Controller) sendAlertToPluginChannel(alert *models.Alert, profileID uint) {
|
||||
select {
|
||||
case c.PluginChannel <- csplugin.ProfileAlert{ProfileID: uint(profileID), Alert: alert}:
|
||||
log.Debugf("alert sent to Plugin channel")
|
||||
default:
|
||||
log.Warningf("Cannot send alert to Plugin channel")
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAlert : write received alerts in body to the database
|
||||
func (c *Controller) CreateAlert(gctx *gin.Context) {
|
||||
|
||||
|
@ -115,13 +125,36 @@ func (c *Controller) CreateAlert(gctx *gin.Context) {
|
|||
}
|
||||
|
||||
for _, alert := range input {
|
||||
if len(alert.Decisions) == 0 {
|
||||
decisions, err := csprofiles.EvaluateProfiles(c.Profiles, alert)
|
||||
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
|
||||
}
|
||||
alert.Decisions = decisions
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
c.sendAlertToPluginChannel(alert, uint(pIdx))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for pIdx, profile := range c.Profiles {
|
||||
profileDecisions, matched, err := csprofiles.EvaluateProfile(profile, alert)
|
||||
if err != nil {
|
||||
gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
||||
return
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
alert.Decisions = append(alert.Decisions, profileDecisions...)
|
||||
profileAlert := *alert
|
||||
c.sendAlertToPluginChannel(&profileAlert, uint(pIdx))
|
||||
if profile.OnSuccess == "break" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,11 +168,10 @@ func (c *Controller) CreateAlert(gctx *gin.Context) {
|
|||
}
|
||||
select {
|
||||
case c.CAPIChan <- input:
|
||||
log.Debugf("alert send to CAPI channel")
|
||||
log.Debugf("alert sent to CAPI channel")
|
||||
default:
|
||||
log.Warningf("Cannot send alert to Central API channel")
|
||||
}
|
||||
|
||||
gctx.JSON(http.StatusCreated, alerts)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
)
|
||||
|
@ -16,9 +17,10 @@ type Controller struct {
|
|||
Middlewares *middlewares.Middlewares
|
||||
Profiles []*csconfig.ProfileCfg
|
||||
CAPIChan chan []*models.Alert
|
||||
PluginChannel chan csplugin.ProfileAlert
|
||||
}
|
||||
|
||||
func New(dbClient *database.Client, ctx context.Context, profiles []*csconfig.ProfileCfg, capiChan chan []*models.Alert) (*Controller, error) {
|
||||
func New(dbClient *database.Client, ctx context.Context, profiles []*csconfig.ProfileCfg, capiChan chan []*models.Alert, pluginChannel chan csplugin.ProfileAlert) (*Controller, error) {
|
||||
var err error
|
||||
v1 := &Controller{
|
||||
Ectx: ctx,
|
||||
|
@ -26,6 +28,7 @@ func New(dbClient *database.Client, ctx context.Context, profiles []*csconfig.Pr
|
|||
APIKeyHeader: middlewares.APIKeyHeader,
|
||||
Profiles: profiles,
|
||||
CAPIChan: capiChan,
|
||||
PluginChannel: pluginChannel,
|
||||
}
|
||||
v1.Middlewares, err = middlewares.NewMiddlewares(dbClient)
|
||||
if err != nil {
|
||||
|
|
|
@ -43,15 +43,13 @@ func TestDeleteDecisionRange(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// delete by ip wrong
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions?range=1.2.3.0/24", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, `{"nbDeleted":"0"}`, w.Body.String())
|
||||
|
@ -59,8 +57,7 @@ func TestDeleteDecisionRange(t *testing.T) {
|
|||
// delete by range
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions?range=91.121.79.0/24&contains=false", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, `{"nbDeleted":"2"}`, w.Body.String())
|
||||
|
@ -68,8 +65,7 @@ func TestDeleteDecisionRange(t *testing.T) {
|
|||
// delete by range : ensure it was already deleted
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions?range=91.121.79.0/24", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, `{"nbDeleted":"0"}`, w.Body.String())
|
||||
|
@ -103,15 +99,13 @@ func TestDeleteDecisionFilter(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// delete by ip wrong
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions?ip=1.2.3.4", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, `{"nbDeleted":"0"}`, w.Body.String())
|
||||
|
@ -119,8 +113,7 @@ func TestDeleteDecisionFilter(t *testing.T) {
|
|||
// delete by ip good
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions?ip=91.121.79.179", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, `{"nbDeleted":"1"}`, w.Body.String())
|
||||
|
@ -128,8 +121,7 @@ func TestDeleteDecisionFilter(t *testing.T) {
|
|||
// delete by scope/value
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions?scope=Ip&value=91.121.79.178", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, `{"nbDeleted":"1"}`, w.Body.String())
|
||||
|
@ -163,8 +155,7 @@ func TestGetDecisionFilters(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
APIKey, err := CreateTestBouncer()
|
||||
|
@ -250,8 +241,7 @@ func TestGetDecision(t *testing.T) {
|
|||
}
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
APIKey, err := CreateTestBouncer()
|
||||
|
@ -308,15 +298,13 @@ func TestDeleteDecisionByID(t *testing.T) {
|
|||
}
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Delete alert with Invalid ID
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions/test", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 400, w.Code)
|
||||
|
@ -325,8 +313,7 @@ func TestDeleteDecisionByID(t *testing.T) {
|
|||
// Delete alert with ID that not exist
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions/100", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 500, w.Code)
|
||||
|
@ -335,8 +322,7 @@ func TestDeleteDecisionByID(t *testing.T) {
|
|||
// Delete alert with valid ID
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions/1", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
|
@ -372,15 +358,13 @@ func TestDeleteDecision(t *testing.T) {
|
|||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/v1/alerts", strings.NewReader(string(alertContent)))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Delete alert with Invalid filter
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions?test=test", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 500, w.Code)
|
||||
|
@ -389,8 +373,7 @@ func TestDeleteDecision(t *testing.T) {
|
|||
// Delete alert
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("DELETE", "/v1/decisions", strings.NewReader(""))
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", loginResp.Token))
|
||||
AddAuthHeaders(req, loginResp)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
|
|
|
@ -13,6 +13,8 @@ type ConfigurationPaths struct {
|
|||
SimulationFilePath string `yaml:"simulation_path,omitempty"`
|
||||
HubIndexFile string `yaml:"index_path,omitempty"` //path of the .index.json
|
||||
HubDir string `yaml:"hub_dir,omitempty"`
|
||||
PluginDir string `yaml:"plugin_dir,omitempty"`
|
||||
NotificationDir string `yaml:"notification_dir,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Config) LoadConfigurationPaths() error {
|
||||
|
@ -39,6 +41,8 @@ func (c *Config) LoadConfigurationPaths() error {
|
|||
&c.ConfigPaths.ConfigDir,
|
||||
&c.ConfigPaths.DataDir,
|
||||
&c.ConfigPaths.SimulationFilePath,
|
||||
&c.ConfigPaths.PluginDir,
|
||||
&c.ConfigPaths.NotificationDir,
|
||||
}
|
||||
for _, k := range configPathsCleanup {
|
||||
if *k == "" {
|
||||
|
|
|
@ -24,6 +24,7 @@ type ProfileCfg struct {
|
|||
Decisions []models.Decision `yaml:"decisions,omitempty"`
|
||||
OnSuccess string `yaml:"on_success,omitempty"` //continue or break
|
||||
OnFailure string `yaml:"on_failure,omitempty"` //continue or break
|
||||
Notifications []string `yaml:"notifications,omitempty"`
|
||||
}
|
||||
|
||||
func (c *LocalApiServerCfg) LoadProfiles() error {
|
||||
|
|
444
pkg/csplugin/broker.go
Normal file
444
pkg/csplugin/broker.go
Normal file
|
@ -0,0 +1,444 @@
|
|||
package csplugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/protobufs"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/tomb.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var testMode bool = false
|
||||
var pluginMutex sync.Mutex
|
||||
|
||||
const (
|
||||
PluginProtocolVersion uint = 1
|
||||
CrowdsecPluginKey string = "CROWDSEC_PLUGIN_KEY"
|
||||
)
|
||||
|
||||
type PluginBroker struct {
|
||||
PluginChannel chan ProfileAlert
|
||||
alertsByPluginName map[string][]*models.Alert
|
||||
profileConfigs []*csconfig.ProfileCfg
|
||||
pluginConfigByName map[string]PluginConfig
|
||||
pluginMap map[string]plugin.Plugin
|
||||
notificationConfigsByPluginType map[string][][]byte // "slack" -> []{config1, config2}
|
||||
notificationPluginByName map[string]Notifier
|
||||
watcher PluginWatcher
|
||||
pluginKillMethods []func()
|
||||
}
|
||||
|
||||
// holder to determine where to dispatch config and how to format messages
|
||||
type PluginConfig struct {
|
||||
Type string `yaml:"type"`
|
||||
Name string `yaml:"name"`
|
||||
GroupWait time.Duration `yaml:"group_wait"`
|
||||
GroupThreshold int `yaml:"group_threshold"`
|
||||
MaxRetry int `yaml:"max_retry"`
|
||||
TimeOut time.Duration `yaml:"timeout"`
|
||||
|
||||
Format string `yaml:"format"` // specific to notification plugins
|
||||
|
||||
Config map[string]interface{} `yaml:",inline"` //to keep the plugin-specific config
|
||||
|
||||
}
|
||||
|
||||
type ProfileAlert struct {
|
||||
ProfileID uint
|
||||
Alert *models.Alert
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) Init(profileConfigs []*csconfig.ProfileCfg, configPaths *csconfig.ConfigurationPaths) error {
|
||||
pb.PluginChannel = make(chan ProfileAlert)
|
||||
pb.notificationConfigsByPluginType = make(map[string][][]byte)
|
||||
pb.notificationPluginByName = make(map[string]Notifier)
|
||||
pb.pluginMap = make(map[string]plugin.Plugin)
|
||||
pb.pluginConfigByName = make(map[string]PluginConfig)
|
||||
pb.alertsByPluginName = make(map[string][]*models.Alert)
|
||||
pb.profileConfigs = profileConfigs
|
||||
if err := pb.loadConfig(configPaths.NotificationDir); err != nil {
|
||||
return errors.Wrap(err, "while loading plugin config")
|
||||
}
|
||||
if err := pb.loadPlugins(configPaths.PluginDir); err != nil {
|
||||
return errors.Wrap(err, "while loading plugin")
|
||||
}
|
||||
pb.watcher = PluginWatcher{}
|
||||
pb.watcher.Init(pb.pluginConfigByName, pb.alertsByPluginName)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) Kill() {
|
||||
for _, kill := range pb.pluginKillMethods {
|
||||
kill()
|
||||
}
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) Run(tomb *tomb.Tomb) {
|
||||
pb.watcher.Start(tomb)
|
||||
for {
|
||||
select {
|
||||
case profileAlert := <-pb.PluginChannel:
|
||||
pb.addProfileAlert(profileAlert)
|
||||
|
||||
case pluginName := <-pb.watcher.PluginEvents:
|
||||
// this can be ran in goroutine, but then locks will be needed
|
||||
pluginMutex.Lock()
|
||||
tmpAlerts := pb.alertsByPluginName[pluginName]
|
||||
pb.alertsByPluginName[pluginName] = make([]*models.Alert, 0)
|
||||
pluginMutex.Unlock()
|
||||
go func() {
|
||||
if err := pb.pushNotificationsToPlugin(pluginName, tmpAlerts); err != nil {
|
||||
log.WithField("plugin:", pluginName).Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
case <-tomb.Dying():
|
||||
log.Info("killing all plugins")
|
||||
pb.Kill()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func (pb *PluginBroker) addProfileAlert(profileAlert ProfileAlert) {
|
||||
for _, pluginName := range pb.profileConfigs[profileAlert.ProfileID].Notifications {
|
||||
if _, ok := pb.pluginConfigByName[pluginName]; !ok {
|
||||
log.Errorf("plugin %s is not configured properly.", pluginName)
|
||||
continue
|
||||
}
|
||||
pluginMutex.Lock()
|
||||
pb.alertsByPluginName[pluginName] = append(pb.alertsByPluginName[pluginName], profileAlert.Alert)
|
||||
pluginMutex.Unlock()
|
||||
pb.watcher.Inserts <- pluginName
|
||||
}
|
||||
}
|
||||
func (pb *PluginBroker) profilesContainPlugin(pluginName string) bool {
|
||||
for _, profileCfg := range pb.profileConfigs {
|
||||
for _, name := range profileCfg.Notifications {
|
||||
if pluginName == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (pb *PluginBroker) loadConfig(path string) error {
|
||||
files, err := listFilesAtPath(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, configFilePath := range files {
|
||||
if !strings.HasSuffix(configFilePath, ".yaml") && !strings.HasSuffix(configFilePath, ".yml") {
|
||||
continue
|
||||
}
|
||||
|
||||
pluginConfigs, err := parsePluginConfigFile(configFilePath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "got error while parsing %s", configFilePath)
|
||||
}
|
||||
for _, pluginConfig := range pluginConfigs {
|
||||
if !pb.profilesContainPlugin(pluginConfig.Name) {
|
||||
continue
|
||||
}
|
||||
setRequiredFields(&pluginConfig)
|
||||
if _, ok := pb.pluginConfigByName[pluginConfig.Name]; ok {
|
||||
log.Warnf("several configs for notification %s found ", pluginConfig.Name)
|
||||
}
|
||||
pb.pluginConfigByName[pluginConfig.Name] = pluginConfig
|
||||
}
|
||||
}
|
||||
err = pb.verifyPluginConfigsWithProfile()
|
||||
return err
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) verifyPluginConfigsWithProfile() error {
|
||||
for _, profileCfg := range pb.profileConfigs {
|
||||
for _, pluginName := range profileCfg.Notifications {
|
||||
if _, ok := pb.pluginConfigByName[pluginName]; !ok {
|
||||
return fmt.Errorf("config file for plugin %s not found", pluginName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) loadPlugins(path string) error {
|
||||
binaryPaths, err := listFilesAtPath(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, binaryPath := range binaryPaths {
|
||||
if err := pluginIsValid(binaryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
pType, pSubtype, err := getPluginTypeAndSubtypeFromPath(binaryPath) // eg pType="notification" , pSubtype="slack"
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pType != "notification" {
|
||||
continue
|
||||
}
|
||||
|
||||
pluginClient, err := pb.loadNotificationPlugin(pSubtype, binaryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pc := range pb.pluginConfigByName {
|
||||
if pc.Type != pSubtype {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := yaml.Marshal(pc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = pluginClient.Configure(context.Background(), &protobufs.Config{Config: data})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "while configuring %s", pc.Name)
|
||||
}
|
||||
log.Infof("registered plugin %s", pc.Name)
|
||||
pb.notificationPluginByName[pc.Name] = pluginClient
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) loadNotificationPlugin(name string, binaryPath string) (Notifier, error) {
|
||||
handshake, err := getHandshake()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := exec.Command(binaryPath)
|
||||
cmd.SysProcAttr, err = getProccessAtr()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "while getting process attributes")
|
||||
}
|
||||
pb.pluginMap[name] = &NotifierPlugin{}
|
||||
l := log.New()
|
||||
err = types.ConfigureLogger(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger := NewHCLogAdapter(l, "")
|
||||
c := plugin.NewClient(&plugin.ClientConfig{
|
||||
HandshakeConfig: handshake,
|
||||
Plugins: pb.pluginMap,
|
||||
Cmd: cmd,
|
||||
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
|
||||
Logger: logger,
|
||||
})
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw, err := client.Dispense(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pb.pluginKillMethods = append(pb.pluginKillMethods, c.Kill)
|
||||
return raw.(Notifier), nil
|
||||
}
|
||||
|
||||
func (pb *PluginBroker) pushNotificationsToPlugin(pluginName string, alerts []*models.Alert) error {
|
||||
if len(alerts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
message, err := formatAlerts(pb.pluginConfigByName[pluginName].Format, alerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plugin := pb.notificationPluginByName[pluginName]
|
||||
backoffDuration := time.Second
|
||||
for i := 1; i <= pb.pluginConfigByName[pluginName].MaxRetry; i++ {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), pb.pluginConfigByName[pluginName].TimeOut)
|
||||
defer cancel()
|
||||
_, err = plugin.Notify(
|
||||
ctx,
|
||||
&protobufs.Notification{
|
||||
Text: message,
|
||||
Name: pluginName,
|
||||
},
|
||||
)
|
||||
if err == nil {
|
||||
return err
|
||||
}
|
||||
log.WithField("plugin", pluginName).Errorf("%s error, retry num %d", err.Error(), i)
|
||||
time.Sleep(backoffDuration)
|
||||
backoffDuration *= 2
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func parsePluginConfigFile(path string) ([]PluginConfig, error) {
|
||||
parsedConfigs := make([]PluginConfig, 0)
|
||||
yamlFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return parsedConfigs, errors.Wrapf(err, "while opening %s", path)
|
||||
}
|
||||
dec := yaml.NewDecoder(yamlFile)
|
||||
for {
|
||||
pc := PluginConfig{}
|
||||
err = dec.Decode(&pc)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Errorf("while decoding %s got error %s", path, err.Error())
|
||||
continue
|
||||
}
|
||||
parsedConfigs = append(parsedConfigs, pc)
|
||||
}
|
||||
return parsedConfigs, nil
|
||||
}
|
||||
|
||||
func setRequiredFields(pluginCfg *PluginConfig) {
|
||||
if pluginCfg.MaxRetry == 0 {
|
||||
pluginCfg.MaxRetry++
|
||||
}
|
||||
|
||||
if pluginCfg.TimeOut == time.Second*0 {
|
||||
pluginCfg.TimeOut = time.Second * 5
|
||||
}
|
||||
|
||||
if pluginCfg.GroupWait == time.Second*0 {
|
||||
pluginCfg.GroupWait = time.Second * 1
|
||||
}
|
||||
}
|
||||
|
||||
func pluginIsValid(path string) error {
|
||||
if testMode {
|
||||
return nil
|
||||
}
|
||||
var details fs.FileInfo
|
||||
var err error
|
||||
|
||||
// check if it exists
|
||||
if details, err = os.Stat(path); err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("plugin at %s does not exist", path))
|
||||
}
|
||||
|
||||
// check if it is owned by root
|
||||
stat := details.Sys().(*syscall.Stat_t)
|
||||
if stat.Uid != 0 || stat.Gid != 0 {
|
||||
return fmt.Errorf("plugin at %s is not owned by root user and group", path)
|
||||
}
|
||||
|
||||
if (int(details.Mode()) & 2) != 0 {
|
||||
return fmt.Errorf("plugin at %s is world writable, world writable plugins are invalid", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// helper which gives paths to all files in the given directory non-recursively
|
||||
func listFilesAtPath(path string) ([]string, error) {
|
||||
filePaths := make([]string, 0)
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
filePaths = append(filePaths, filepath.Join(path, file.Name()))
|
||||
}
|
||||
return filePaths, nil
|
||||
}
|
||||
|
||||
func getPluginTypeAndSubtypeFromPath(path string) (string, string, error) {
|
||||
pluginFileName := filepath.Base(path)
|
||||
parts := strings.Split(pluginFileName, "-")
|
||||
if len(parts) < 2 {
|
||||
return "", "", fmt.Errorf("plugin name %s is invalid. Name should be like {type-name}", path)
|
||||
}
|
||||
return strings.Join(parts[:len(parts)-1], "-"), parts[len(parts)-1], nil
|
||||
}
|
||||
|
||||
func getProccessAtr() (*syscall.SysProcAttr, error) {
|
||||
u, err := user.Lookup("nobody")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, err := user.LookupGroup("nogroup")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uid, err := strconv.Atoi(u.Uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if uid < 0 && uid > math.MaxUint32 {
|
||||
return nil, fmt.Errorf("out of bound uid")
|
||||
}
|
||||
gid, err := strconv.Atoi(g.Gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gid < 0 && gid > math.MaxUint32 {
|
||||
return nil, fmt.Errorf("out of bound gid")
|
||||
}
|
||||
return &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uint32(uid),
|
||||
Gid: uint32(gid),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getUUID() (string, error) {
|
||||
if d, err := os.ReadFile("/proc/sys/kernel/random/uuid"); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return string(d), nil
|
||||
}
|
||||
}
|
||||
|
||||
func getHandshake() (plugin.HandshakeConfig, error) {
|
||||
uuid, err := getUUID()
|
||||
if err != nil {
|
||||
return plugin.HandshakeConfig{}, err
|
||||
}
|
||||
handshake := plugin.HandshakeConfig{
|
||||
ProtocolVersion: PluginProtocolVersion,
|
||||
MagicCookieKey: CrowdsecPluginKey,
|
||||
MagicCookieValue: uuid,
|
||||
}
|
||||
return handshake, nil
|
||||
}
|
||||
|
||||
func formatAlerts(format string, alerts []*models.Alert) (string, error) {
|
||||
template, err := template.New("").Funcs(sprig.TxtFuncMap()).Parse(format)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b := new(strings.Builder)
|
||||
err = template.Execute(b, alerts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
142
pkg/csplugin/broker_test.go
Normal file
142
pkg/csplugin/broker_test.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
package csplugin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testPath string
|
||||
|
||||
func Test_getPluginNameAndTypeFromPath(t *testing.T) {
|
||||
setUp()
|
||||
defer tearDown()
|
||||
type args struct {
|
||||
path string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
want1 string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid plugin name, single dash",
|
||||
args: args{
|
||||
path: path.Join(testPath, "notification-gitter"),
|
||||
},
|
||||
want: "notification",
|
||||
want1: "gitter",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid plugin name",
|
||||
args: args{
|
||||
path: "./tests/gitter",
|
||||
},
|
||||
want: "",
|
||||
want1: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid plugin name, multiple dash",
|
||||
args: args{
|
||||
path: "./tests/notification-instant-slack",
|
||||
},
|
||||
want: "notification-instant",
|
||||
want1: "slack",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, got1, err := getPluginTypeAndSubtypeFromPath(tt.args.path)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getPluginNameAndTypeFromPath() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("getPluginNameAndTypeFromPath() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
if got1 != tt.want1 {
|
||||
t.Errorf("getPluginNameAndTypeFromPath() got1 = %v, want %v", got1, tt.want1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listFilesAtPath(t *testing.T) {
|
||||
setUp()
|
||||
defer tearDown()
|
||||
type args struct {
|
||||
path string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid directory",
|
||||
args: args{
|
||||
path: testPath,
|
||||
},
|
||||
want: []string{
|
||||
path.Join(testPath, "notification-gitter"),
|
||||
path.Join(testPath, "slack"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid directory",
|
||||
args: args{
|
||||
path: "./foo/bar/",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := listFilesAtPath(tt.args.path)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("listFilesAtPath() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("listFilesAtPath() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setUp() {
|
||||
testMode = true
|
||||
dir, err := ioutil.TempDir("./", "cs_plugin_test")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = os.Create(path.Join(dir, "slack"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = os.Create(path.Join(dir, "notification-gitter"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = os.Mkdir(path.Join(dir, "dummy_dir"), 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
testPath = dir
|
||||
}
|
||||
|
||||
func tearDown() {
|
||||
err := os.RemoveAll(testPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
213
pkg/csplugin/hclog_adapter.go
Normal file
213
pkg/csplugin/hclog_adapter.go
Normal file
|
@ -0,0 +1,213 @@
|
|||
// Copyright 2021 Workrise Technologies Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package csplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// NewHCLogAdapter takes an instance of a Logrus logger and returns an hclog
|
||||
// logger in the form of an HCLogAdapter.
|
||||
func NewHCLogAdapter(l *logrus.Logger, name string) hclog.Logger {
|
||||
return &HCLogAdapter{l, name, nil}
|
||||
}
|
||||
|
||||
// HCLogAdapter implements the hclog interface. Plugins use hclog to send
|
||||
// log entries back to ephemeral-iam and this adapter allows for those logs
|
||||
// to be handled by ephemeral-iam's Logrus logger.
|
||||
type HCLogAdapter struct {
|
||||
log *logrus.Logger
|
||||
name string
|
||||
|
||||
impliedArgs []interface{}
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
|
||||
switch level {
|
||||
case hclog.NoLevel:
|
||||
return
|
||||
case hclog.Trace:
|
||||
h.Trace(msg, args...)
|
||||
case hclog.Debug:
|
||||
h.Debug(msg, args...)
|
||||
case hclog.Info:
|
||||
h.Info(msg, args...)
|
||||
case hclog.Warn:
|
||||
h.Warn(msg, args...)
|
||||
case hclog.Error:
|
||||
h.Error(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Trace(msg string, args ...interface{}) {
|
||||
h.log.WithFields(toLogrusFields(args)).Trace(msg)
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Debug(msg string, args ...interface{}) {
|
||||
h.log.WithFields(toLogrusFields(args)).Debug(msg)
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Info(msg string, args ...interface{}) {
|
||||
h.log.WithFields(toLogrusFields(args)).Info(msg)
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Warn(msg string, args ...interface{}) {
|
||||
h.log.WithFields(toLogrusFields(args)).Warn(msg)
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Error(msg string, args ...interface{}) {
|
||||
h.log.WithFields(toLogrusFields(args)).Error(msg)
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) IsTrace() bool {
|
||||
return h.log.GetLevel() >= logrus.TraceLevel
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) IsDebug() bool {
|
||||
return h.log.GetLevel() >= logrus.DebugLevel
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) IsInfo() bool {
|
||||
return h.log.GetLevel() >= logrus.InfoLevel
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) IsWarn() bool {
|
||||
return h.log.GetLevel() >= logrus.WarnLevel
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) IsError() bool {
|
||||
return h.log.GetLevel() >= logrus.ErrorLevel
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) ImpliedArgs() []interface{} {
|
||||
// Not supported.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) With(args ...interface{}) hclog.Logger {
|
||||
return &h
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Name() string {
|
||||
return h.name
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) Named(name string) hclog.Logger {
|
||||
return NewHCLogAdapter(h.log, name)
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) ResetNamed(name string) hclog.Logger {
|
||||
return &h
|
||||
}
|
||||
|
||||
func (h *HCLogAdapter) SetLevel(level hclog.Level) {
|
||||
h.log.SetLevel(convertLevel(level))
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
|
||||
if opts == nil {
|
||||
opts = &hclog.StandardLoggerOptions{}
|
||||
}
|
||||
return log.New(h.StandardWriter(opts), "", 0)
|
||||
}
|
||||
|
||||
func (h HCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
|
||||
return os.Stderr
|
||||
}
|
||||
|
||||
// convertLevel maps hclog levels to Logrus levels.
|
||||
func convertLevel(level hclog.Level) logrus.Level {
|
||||
switch level {
|
||||
case hclog.NoLevel:
|
||||
// Logrus does not have NoLevel, so use Info instead.
|
||||
return logrus.InfoLevel
|
||||
case hclog.Trace:
|
||||
return logrus.TraceLevel
|
||||
case hclog.Debug:
|
||||
return logrus.DebugLevel
|
||||
case hclog.Info:
|
||||
return logrus.InfoLevel
|
||||
case hclog.Warn:
|
||||
return logrus.WarnLevel
|
||||
case hclog.Error:
|
||||
return logrus.ErrorLevel
|
||||
default:
|
||||
return logrus.InfoLevel
|
||||
}
|
||||
}
|
||||
|
||||
// toLogrusFields takes a list of key/value pairs passed to the hclog logger
|
||||
// and converts them to a map to be used as Logrus fields.
|
||||
func toLogrusFields(kvPairs []interface{}) map[string]interface{} {
|
||||
m := map[string]interface{}{}
|
||||
if len(kvPairs) == 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
if len(kvPairs)%2 == 1 {
|
||||
// There are an odd number of key/value pairs so append nil as the final value.
|
||||
kvPairs = append(kvPairs, nil)
|
||||
}
|
||||
|
||||
for i := 0; i < len(kvPairs); i += 2 {
|
||||
// hclog automatically adds the timestamp field, ignore it.
|
||||
if kvPairs[i] != "timestamp" {
|
||||
merge(m, kvPairs[i], kvPairs[i+1])
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// merge takes a key/value pair and converts them to strings then adds them to
|
||||
// the dst map.
|
||||
func merge(dst map[string]interface{}, k, v interface{}) {
|
||||
var key string
|
||||
|
||||
switch x := k.(type) {
|
||||
case string:
|
||||
key = x
|
||||
case fmt.Stringer:
|
||||
key = safeString(x)
|
||||
default:
|
||||
key = fmt.Sprint(x)
|
||||
}
|
||||
|
||||
dst[key] = v
|
||||
}
|
||||
|
||||
// safeString takes an interface that implements the String() function and calls it
|
||||
// to attempt to convert it to a string. If a panic occurs, and it's caused by a
|
||||
// nil pointer, the value will be set to "NULL".
|
||||
func safeString(str fmt.Stringer) (s string) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
s = "NULL"
|
||||
} else {
|
||||
panic(panicVal)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
s = str.String()
|
||||
return
|
||||
}
|
59
pkg/csplugin/notifier.go
Normal file
59
pkg/csplugin/notifier.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package csplugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/protobufs"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Notifier interface {
|
||||
Notify(ctx context.Context, notification *protobufs.Notification) (*protobufs.Empty, error)
|
||||
Configure(ctx context.Context, cfg *protobufs.Config) (*protobufs.Empty, error)
|
||||
}
|
||||
|
||||
type NotifierPlugin struct {
|
||||
plugin.Plugin
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
type GRPCClient struct{ client protobufs.NotifierClient }
|
||||
|
||||
func (m *GRPCClient) Notify(ctx context.Context, notification *protobufs.Notification) (*protobufs.Empty, error) {
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
_, err := m.client.Notify(
|
||||
context.Background(), &protobufs.Notification{Text: notification.Text, Name: notification.Name},
|
||||
)
|
||||
done <- err
|
||||
}()
|
||||
select {
|
||||
case err := <-done:
|
||||
return &protobufs.Empty{}, err
|
||||
|
||||
case <-ctx.Done():
|
||||
return &protobufs.Empty{}, fmt.Errorf("timeout exceeded")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *GRPCClient) Configure(ctx context.Context, config *protobufs.Config) (*protobufs.Empty, error) {
|
||||
_, err := m.client.Configure(
|
||||
context.Background(), config,
|
||||
)
|
||||
return &protobufs.Empty{}, err
|
||||
}
|
||||
|
||||
type GRPCServer struct {
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
||||
protobufs.RegisterNotifierServer(s, p.Impl)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
return &GRPCClient{client: protobufs.NewNotifierClient(c)}, nil
|
||||
}
|
76
pkg/csplugin/watcher.go
Normal file
76
pkg/csplugin/watcher.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package csplugin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"gopkg.in/tomb.v2"
|
||||
)
|
||||
|
||||
type PluginWatcher struct {
|
||||
PluginConfigByName map[string]PluginConfig
|
||||
AlertCountByPluginName map[string]int
|
||||
PluginEvents chan string
|
||||
Inserts chan string
|
||||
tomb *tomb.Tomb
|
||||
}
|
||||
|
||||
func (pw *PluginWatcher) Init(configs map[string]PluginConfig, alertsByPluginName map[string][]*models.Alert) {
|
||||
pw.PluginConfigByName = configs
|
||||
pw.PluginEvents = make(chan string)
|
||||
pw.AlertCountByPluginName = make(map[string]int)
|
||||
pw.Inserts = make(chan string)
|
||||
for name := range alertsByPluginName {
|
||||
pw.AlertCountByPluginName[name] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (pw *PluginWatcher) Start(tomb *tomb.Tomb) {
|
||||
pw.tomb = tomb
|
||||
for name := range pw.PluginConfigByName {
|
||||
pname := name
|
||||
pw.tomb.Go(func() error {
|
||||
pw.watchPluginTicker(pname)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
pw.tomb.Go(func() error {
|
||||
pw.watchPluginAlertCounts()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (pw *PluginWatcher) watchPluginTicker(pluginName string) {
|
||||
if pw.PluginConfigByName[pluginName].GroupWait <= time.Second*0 {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(pw.PluginConfigByName[pluginName].GroupWait)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
pw.PluginEvents <- pluginName
|
||||
|
||||
case <-pw.tomb.Dying():
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pw *PluginWatcher) watchPluginAlertCounts() {
|
||||
for {
|
||||
select {
|
||||
case pluginName := <-pw.Inserts:
|
||||
if threshold := pw.PluginConfigByName[pluginName].GroupThreshold; threshold > 0 {
|
||||
pw.AlertCountByPluginName[pluginName]++
|
||||
if pw.AlertCountByPluginName[pluginName] >= threshold {
|
||||
pw.PluginEvents <- pluginName
|
||||
pw.AlertCountByPluginName[pluginName] = 0
|
||||
}
|
||||
}
|
||||
case <-pw.tomb.Dying():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,10 +57,9 @@ func GenerateDecisionFromProfile(Profile *csconfig.ProfileCfg, Alert *models.Ale
|
|||
|
||||
var clog *log.Entry
|
||||
|
||||
//EvaluateProfiles is going to evaluate an Alert against a set of profiles to generate Decisions
|
||||
func EvaluateProfiles(Profiles []*csconfig.ProfileCfg, Alert *models.Alert) ([]*models.Decision, error) {
|
||||
//EvaluateProfile is going to evaluate an Alert against a profile to generate Decisions
|
||||
func EvaluateProfile(profile *csconfig.ProfileCfg, Alert *models.Alert) ([]*models.Decision, bool, error) {
|
||||
var decisions []*models.Decision
|
||||
|
||||
if clog == nil {
|
||||
xlog := log.New()
|
||||
if err := types.ConfigureLogger(xlog); err != nil {
|
||||
|
@ -71,20 +70,13 @@ func EvaluateProfiles(Profiles []*csconfig.ProfileCfg, Alert *models.Alert) ([]*
|
|||
"type": "profile",
|
||||
})
|
||||
}
|
||||
|
||||
if !Alert.Remediation {
|
||||
return nil, nil
|
||||
}
|
||||
PROFILE_LOOP:
|
||||
for _, profile := range Profiles {
|
||||
matched := false
|
||||
for eIdx, expression := range profile.RuntimeFilters {
|
||||
output, err := expr.Run(expression, exprhelpers.GetExprEnv(map[string]interface{}{"Alert": Alert}))
|
||||
if err != nil {
|
||||
log.Warningf("failed to run whitelist expr : %v", err)
|
||||
return nil, errors.Wrapf(err, "while running expression %s", profile.Filters[eIdx])
|
||||
return nil, matched, errors.Wrapf(err, "while running expression %s", profile.Filters[eIdx])
|
||||
}
|
||||
|
||||
switch out := output.(type) {
|
||||
case bool:
|
||||
if profile.Debug != nil && *profile.Debug {
|
||||
|
@ -95,28 +87,22 @@ PROFILE_LOOP:
|
|||
/*the expression matched, create the associated decision*/
|
||||
subdecisions, err := GenerateDecisionFromProfile(profile, Alert)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "while generating decision from profile %s", profile.Name)
|
||||
return nil, matched, errors.Wrapf(err, "while generating decision from profile %s", profile.Name)
|
||||
}
|
||||
|
||||
decisions = append(decisions, subdecisions...)
|
||||
} else {
|
||||
log.Debugf("Profile %s filter is unsuccessful", profile.Name)
|
||||
if profile.OnFailure == "break" {
|
||||
break PROFILE_LOOP
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected type %t (%v) while running '%s'", output, output, profile.Filters[eIdx])
|
||||
return nil, matched, fmt.Errorf("unexpected type %t (%v) while running '%s'", output, output, profile.Filters[eIdx])
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if matched {
|
||||
if profile.OnSuccess == "break" {
|
||||
break PROFILE_LOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
return decisions, nil
|
||||
return decisions, matched, nil
|
||||
}
|
||||
|
|
104
pkg/csprofiles/csprofiles_test.go
Normal file
104
pkg/csprofiles/csprofiles_test.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
package csprofiles
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/antonmedv/expr/vm"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
)
|
||||
|
||||
var (
|
||||
scope = "Country"
|
||||
typ = "ban"
|
||||
simulated = false
|
||||
duration = "1h"
|
||||
|
||||
value = "CH"
|
||||
scenario = "ssh-bf"
|
||||
)
|
||||
|
||||
func TestEvaluateProfile(t *testing.T) {
|
||||
type args struct {
|
||||
profile *csconfig.ProfileCfg
|
||||
Alert *models.Alert
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedDecisionCount int // count of expected decisions
|
||||
expectedMatchStatus bool
|
||||
}{
|
||||
{
|
||||
name: "simple pass single expr",
|
||||
args: args{
|
||||
profile: &csconfig.ProfileCfg{
|
||||
Filters: []string{fmt.Sprintf("Alert.GetScenario() == \"%s\"", scenario)},
|
||||
RuntimeFilters: []*vm.Program{},
|
||||
},
|
||||
Alert: &models.Alert{Remediation: true, Scenario: &scenario},
|
||||
},
|
||||
expectedDecisionCount: 0,
|
||||
expectedMatchStatus: true,
|
||||
},
|
||||
{
|
||||
name: "simple fail single expr",
|
||||
args: args{
|
||||
profile: &csconfig.ProfileCfg{
|
||||
Filters: []string{"Alert.GetScenario() == \"Foo\""},
|
||||
RuntimeFilters: []*vm.Program{},
|
||||
},
|
||||
Alert: &models.Alert{Remediation: true},
|
||||
},
|
||||
expectedDecisionCount: 0,
|
||||
expectedMatchStatus: false,
|
||||
},
|
||||
{
|
||||
name: "1 expr fail 1 expr pass should still eval to match",
|
||||
args: args{
|
||||
profile: &csconfig.ProfileCfg{
|
||||
Filters: []string{"1==1", "1!=1"},
|
||||
RuntimeFilters: []*vm.Program{},
|
||||
},
|
||||
Alert: &models.Alert{Remediation: true},
|
||||
},
|
||||
expectedDecisionCount: 0,
|
||||
expectedMatchStatus: true,
|
||||
},
|
||||
{
|
||||
name: "simple filter with 2 decision",
|
||||
args: args{
|
||||
profile: &csconfig.ProfileCfg{
|
||||
Filters: []string{"1==1"},
|
||||
RuntimeFilters: []*vm.Program{},
|
||||
Decisions: []models.Decision{
|
||||
{Type: &typ, Scope: &scope, Simulated: &simulated, Duration: &duration},
|
||||
{Type: &typ, Scope: &scope, Simulated: &simulated, Duration: &duration},
|
||||
},
|
||||
},
|
||||
Alert: &models.Alert{Remediation: true, Scenario: &scenario, Source: &models.Source{Value: &value}},
|
||||
},
|
||||
expectedDecisionCount: 2,
|
||||
expectedMatchStatus: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for _, filter := range tt.args.profile.Filters {
|
||||
runtimeFilter, _ := expr.Compile(filter, expr.Env(exprhelpers.GetExprEnv(map[string]interface{}{"Alert": &models.Alert{}})))
|
||||
tt.args.profile.RuntimeFilters = append(tt.args.profile.RuntimeFilters, runtimeFilter)
|
||||
}
|
||||
got, got1, _ := EvaluateProfile(tt.args.profile, tt.args.Alert)
|
||||
if !reflect.DeepEqual(len(got), tt.expectedDecisionCount) {
|
||||
t.Errorf("EvaluateProfile() got = %+v, want %+v", got, tt.expectedDecisionCount)
|
||||
}
|
||||
if got1 != tt.expectedMatchStatus {
|
||||
t.Errorf("EvaluateProfile() got1 = %v, want %v", got1, tt.expectedMatchStatus)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
8
pkg/protobufs/README.md
Normal file
8
pkg/protobufs/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
To generate go code for the `notifier.proto` files, run :
|
||||
|
||||
```
|
||||
protoc --go_out=. --go_opt=paths=source_relative \
|
||||
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
|
||||
proto/alert.proto`
|
||||
```
|
||||
|
395
pkg/protobufs/notifier.pb.go
Normal file
395
pkg/protobufs/notifier.pb.go
Normal file
|
@ -0,0 +1,395 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.12.4
|
||||
// source: notifier.proto
|
||||
|
||||
package protobufs
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Notification) Reset() {
|
||||
*x = Notification{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Notification) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Notification) ProtoMessage() {}
|
||||
|
||||
func (x *Notification) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
||||
func (*Notification) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Notification) GetText() string {
|
||||
if x != nil {
|
||||
return x.Text
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetConfig() []byte {
|
||||
if x != nil {
|
||||
return x.Config
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Empty struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
var File_notifier_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_notifier_proto_rawDesc = []byte{
|
||||
0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22,
|
||||
0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x61, 0x0a, 0x08, 0x4e, 0x6f,
|
||||
0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79,
|
||||
0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65,
|
||||
0x12, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a,
|
||||
0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x0d, 0x5a,
|
||||
0x0b, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x73, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_notifier_proto_rawDescOnce sync.Once
|
||||
file_notifier_proto_rawDescData = file_notifier_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_notifier_proto_rawDescGZIP() []byte {
|
||||
file_notifier_proto_rawDescOnce.Do(func() {
|
||||
file_notifier_proto_rawDescData = protoimpl.X.CompressGZIP(file_notifier_proto_rawDescData)
|
||||
})
|
||||
return file_notifier_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_notifier_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_notifier_proto_goTypes = []interface{}{
|
||||
(*Notification)(nil), // 0: proto.Notification
|
||||
(*Config)(nil), // 1: proto.Config
|
||||
(*Empty)(nil), // 2: proto.Empty
|
||||
}
|
||||
var file_notifier_proto_depIdxs = []int32{
|
||||
0, // 0: proto.Notifier.Notify:input_type -> proto.Notification
|
||||
1, // 1: proto.Notifier.Configure:input_type -> proto.Config
|
||||
2, // 2: proto.Notifier.Notify:output_type -> proto.Empty
|
||||
2, // 3: proto.Notifier.Configure:output_type -> proto.Empty
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_notifier_proto_init() }
|
||||
func file_notifier_proto_init() {
|
||||
if File_notifier_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_notifier_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Notification); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Empty); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_notifier_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_notifier_proto_goTypes,
|
||||
DependencyIndexes: file_notifier_proto_depIdxs,
|
||||
MessageInfos: file_notifier_proto_msgTypes,
|
||||
}.Build()
|
||||
File_notifier_proto = out.File
|
||||
file_notifier_proto_rawDesc = nil
|
||||
file_notifier_proto_goTypes = nil
|
||||
file_notifier_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// NotifierClient is the client API for Notifier service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type NotifierClient interface {
|
||||
Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error)
|
||||
Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type notifierClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewNotifierClient(cc grpc.ClientConnInterface) NotifierClient {
|
||||
return ¬ifierClient{cc}
|
||||
}
|
||||
|
||||
func (c *notifierClient) Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Notify", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierClient) Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Configure", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NotifierServer is the server API for Notifier service.
|
||||
type NotifierServer interface {
|
||||
Notify(context.Context, *Notification) (*Empty, error)
|
||||
Configure(context.Context, *Config) (*Empty, error)
|
||||
}
|
||||
|
||||
// UnimplementedNotifierServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedNotifierServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedNotifierServer) Notify(context.Context, *Notification) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Notify not implemented")
|
||||
}
|
||||
func (*UnimplementedNotifierServer) Configure(context.Context, *Config) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented")
|
||||
}
|
||||
|
||||
func RegisterNotifierServer(s *grpc.Server, srv NotifierServer) {
|
||||
s.RegisterService(&_Notifier_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Notifier_Notify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Notification)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Notify(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Notify",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Notify(ctx, req.(*Notification))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Notifier_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Config)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Configure(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Configure",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Configure(ctx, req.(*Config))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Notifier_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.Notifier",
|
||||
HandlerType: (*NotifierServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Notify",
|
||||
Handler: _Notifier_Notify_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Configure",
|
||||
Handler: _Notifier_Configure_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "notifier.proto",
|
||||
}
|
19
pkg/protobufs/notifier.proto
Normal file
19
pkg/protobufs/notifier.proto
Normal file
|
@ -0,0 +1,19 @@
|
|||
syntax = "proto3" ;
|
||||
package proto;
|
||||
option go_package = ".;protobufs";
|
||||
|
||||
message Notification {
|
||||
string text = 1 ;
|
||||
string name = 2 ;
|
||||
}
|
||||
|
||||
message Config {
|
||||
bytes config = 2 ;
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
|
||||
service Notifier {
|
||||
rpc Notify(Notification) returns (Empty);
|
||||
rpc Configure(Config) returns (Empty);
|
||||
}
|
|
@ -41,7 +41,6 @@ func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level
|
|||
log.SetLevel(logLevel)
|
||||
logFormatter = &log.TextFormatter{TimestampFormat: "02-01-2006 15:04:05", FullTimestamp: true}
|
||||
log.SetFormatter(logFormatter)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
21
plugins/notifications/http/LICENSE
Normal file
21
plugins/notifications/http/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Crowdsec
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
16
plugins/notifications/http/Makefile
Normal file
16
plugins/notifications/http/Makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-http
|
||||
|
||||
clean:
|
||||
@rm -f $(BINARY_NAME)
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
||||
static: clean
|
||||
$(GOBUILD) $(LD_OPTS_STATIC) -o $(BINARY_NAME) -v -a -tags netgo
|
12
plugins/notifications/http/go.mod
Normal file
12
plugins/notifications/http/go.mod
Normal file
|
@ -0,0 +1,12 @@
|
|||
module github.com/crowdsecurity/http-plugin
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/hashicorp/go-hclog v0.14.1
|
||||
github.com/hashicorp/go-plugin v1.4.2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
google.golang.org/grpc v1.39.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
145
plugins/notifications/http/go.sum
Normal file
145
plugins/notifications/http/go.sum
Normal file
|
@ -0,0 +1,145 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-plugin v1.4.2 h1:yFvG3ufXXpqiMiZx9HLcaK3XbIqQ1WJFR/F1a2CuVw0=
|
||||
github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
25
plugins/notifications/http/http.yaml
Normal file
25
plugins/notifications/http/http.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Don't change this
|
||||
type: http
|
||||
|
||||
name: http_default # this must match with the registered plugin in the profile
|
||||
log_level: info # Options include: trace, debug, info, warn, error, off
|
||||
|
||||
format: | # This template receives list of models.Alert objects. The request body would contain this.
|
||||
{{.|toJson}}
|
||||
|
||||
url: <HTTP_url> # plugin will make requests to this url. Eg value https://www.example.com/
|
||||
|
||||
method: POST # eg either of "POST", "GET", "PUT" and other http verbs is valid value.
|
||||
|
||||
# headers:
|
||||
# Authorization: token 0x64312313
|
||||
|
||||
# skip_tls_verification: # either true or false. Default is false
|
||||
|
||||
# group_wait: # duration to wait collecting alerts before sending to this plugin, eg "30s"
|
||||
|
||||
# group_threshold: # if alerts exceed this, then the plugin will be sent the message. eg "10"
|
||||
|
||||
# max_retry: # number of tries to attempt to send message to plugins in case of error.
|
||||
|
||||
# timeout: # duration to wait for response from plugin before considering this attempt a failure. eg "10s"
|
58
plugins/notifications/http/interface.go
Normal file
58
plugins/notifications/http/interface.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Handshake is a common handshake that is shared by plugin and host.
|
||||
var Handshake = plugin.HandshakeConfig{
|
||||
// This isn't required when using VersionedPlugins
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "BASIC_PLUGIN",
|
||||
MagicCookieValue: "hello",
|
||||
}
|
||||
|
||||
// KV is the interface that we're exposing as a plugin.
|
||||
type Notifier interface {
|
||||
Notify(ctx context.Context, notification *Notification) (*Empty, error)
|
||||
Configure(ctx context.Context, config *Config) (*Empty, error)
|
||||
}
|
||||
|
||||
// This is the implementation of plugin.NotifierPlugin so we can serve/consume this.
|
||||
type NotifierPlugin struct {
|
||||
// GRPCPlugin must still implement the Plugin interface
|
||||
plugin.Plugin
|
||||
// Concrete implementation, written in Go. This is only used for plugins
|
||||
// that are written in Go.
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
type GRPCClient struct{ client NotifierClient }
|
||||
|
||||
func (m *GRPCClient) Notify(ctx context.Context, notification *Notification) (*Empty, error) {
|
||||
_, err := m.client.Notify(context.Background(), notification)
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
func (m *GRPCClient) Configure(ctx context.Context, config *Config) (*Empty, error) {
|
||||
_, err := m.client.Configure(context.Background(), config)
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
// Here is the gRPC server that GRPCClient talks to.
|
||||
type GRPCServer struct {
|
||||
// This is the real implementation
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
||||
RegisterNotifierServer(s, p.Impl)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
return &GRPCClient{client: NewNotifierClient(c)}, nil
|
||||
}
|
109
plugins/notifications/http/main.go
Normal file
109
plugins/notifications/http/main.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type PluginConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
URL string `yaml:"url"`
|
||||
Headers map[string]string `yaml:"headers"`
|
||||
SkipTLSVerification bool `yaml:"skip_tls_verification"`
|
||||
Method string `yaml:"method"`
|
||||
LogLevel *string `yaml:"log_level"`
|
||||
}
|
||||
|
||||
type HTTPPlugin struct {
|
||||
PluginConfigByName map[string]PluginConfig
|
||||
}
|
||||
|
||||
var logger hclog.Logger = hclog.New(&hclog.LoggerOptions{
|
||||
Name: "http-plugin",
|
||||
Level: hclog.LevelFromString("DEBUG"),
|
||||
Output: os.Stderr,
|
||||
JSONFormat: true,
|
||||
})
|
||||
|
||||
func (s *HTTPPlugin) Notify(ctx context.Context, notification *Notification) (*Empty, error) {
|
||||
if _, ok := s.PluginConfigByName[notification.Name]; !ok {
|
||||
return nil, fmt.Errorf("invalid plugin config name %s", notification.Name)
|
||||
}
|
||||
cfg := s.PluginConfigByName[notification.Name]
|
||||
if cfg.LogLevel != nil && *cfg.LogLevel != "" {
|
||||
logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel))
|
||||
} else {
|
||||
logger.SetLevel(hclog.Info)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("received signal for %s config", notification.Name))
|
||||
client := http.Client{}
|
||||
|
||||
if cfg.SkipTLSVerification {
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(cfg.Method, cfg.URL, bytes.NewReader([]byte(notification.Text)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for headerName, headerValue := range cfg.Headers {
|
||||
request.Header.Add(headerName, headerValue)
|
||||
}
|
||||
logger.Debug(fmt.Sprintf("making HTTP %s call to %s with body %s", cfg.Method, cfg.URL, string(notification.Text)))
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, err
|
||||
}
|
||||
respData, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return &Empty{}, fmt.Errorf("failed to read response body got error %s", string(err.Error()))
|
||||
}
|
||||
logger.Debug(fmt.Sprintf("got response %s", string(respData)))
|
||||
|
||||
return &Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *HTTPPlugin) Configure(ctx context.Context, config *Config) (*Empty, error) {
|
||||
d := PluginConfig{}
|
||||
err := yaml.Unmarshal(config.Config, &d)
|
||||
s.PluginConfigByName[d.Name] = d
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
func main() {
|
||||
var handshake = plugin.HandshakeConfig{
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "CROWDSEC_PLUGIN_KEY",
|
||||
MagicCookieValue: os.Getenv("CROWDSEC_PLUGIN_KEY"),
|
||||
}
|
||||
|
||||
sp := &HTTPPlugin{PluginConfigByName: make(map[string]PluginConfig)}
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: handshake,
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
"http": &NotifierPlugin{
|
||||
Impl: sp,
|
||||
},
|
||||
},
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
Logger: logger,
|
||||
})
|
||||
}
|
394
plugins/notifications/http/notifier.pb.go
Normal file
394
plugins/notifications/http/notifier.pb.go
Normal file
|
@ -0,0 +1,394 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.12.4
|
||||
// source: notifier.proto
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Notification) Reset() {
|
||||
*x = Notification{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Notification) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Notification) ProtoMessage() {}
|
||||
|
||||
func (x *Notification) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
||||
func (*Notification) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Notification) GetText() string {
|
||||
if x != nil {
|
||||
return x.Text
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetConfig() []byte {
|
||||
if x != nil {
|
||||
return x.Config
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Empty struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
var File_notifier_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_notifier_proto_rawDesc = []byte{
|
||||
0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22,
|
||||
0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x61, 0x0a, 0x08, 0x4e, 0x6f,
|
||||
0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79,
|
||||
0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65,
|
||||
0x12, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a,
|
||||
0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x08, 0x5a,
|
||||
0x06, 0x2e, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_notifier_proto_rawDescOnce sync.Once
|
||||
file_notifier_proto_rawDescData = file_notifier_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_notifier_proto_rawDescGZIP() []byte {
|
||||
file_notifier_proto_rawDescOnce.Do(func() {
|
||||
file_notifier_proto_rawDescData = protoimpl.X.CompressGZIP(file_notifier_proto_rawDescData)
|
||||
})
|
||||
return file_notifier_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_notifier_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_notifier_proto_goTypes = []interface{}{
|
||||
(*Notification)(nil), // 0: proto.Notification
|
||||
(*Config)(nil), // 1: proto.Config
|
||||
(*Empty)(nil), // 2: proto.Empty
|
||||
}
|
||||
var file_notifier_proto_depIdxs = []int32{
|
||||
0, // 0: proto.Notifier.Notify:input_type -> proto.Notification
|
||||
1, // 1: proto.Notifier.Configure:input_type -> proto.Config
|
||||
2, // 2: proto.Notifier.Notify:output_type -> proto.Empty
|
||||
2, // 3: proto.Notifier.Configure:output_type -> proto.Empty
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_notifier_proto_init() }
|
||||
func file_notifier_proto_init() {
|
||||
if File_notifier_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_notifier_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Notification); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Empty); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_notifier_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_notifier_proto_goTypes,
|
||||
DependencyIndexes: file_notifier_proto_depIdxs,
|
||||
MessageInfos: file_notifier_proto_msgTypes,
|
||||
}.Build()
|
||||
File_notifier_proto = out.File
|
||||
file_notifier_proto_rawDesc = nil
|
||||
file_notifier_proto_goTypes = nil
|
||||
file_notifier_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// NotifierClient is the client API for Notifier service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type NotifierClient interface {
|
||||
Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error)
|
||||
Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type notifierClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewNotifierClient(cc grpc.ClientConnInterface) NotifierClient {
|
||||
return ¬ifierClient{cc}
|
||||
}
|
||||
|
||||
func (c *notifierClient) Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Notify", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierClient) Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Configure", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NotifierServer is the server API for Notifier service.
|
||||
type NotifierServer interface {
|
||||
Notify(context.Context, *Notification) (*Empty, error)
|
||||
Configure(context.Context, *Config) (*Empty, error)
|
||||
}
|
||||
|
||||
// UnimplementedNotifierServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedNotifierServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedNotifierServer) Notify(context.Context, *Notification) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Notify not implemented")
|
||||
}
|
||||
func (*UnimplementedNotifierServer) Configure(context.Context, *Config) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented")
|
||||
}
|
||||
|
||||
func RegisterNotifierServer(s *grpc.Server, srv NotifierServer) {
|
||||
s.RegisterService(&_Notifier_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Notifier_Notify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Notification)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Notify(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Notify",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Notify(ctx, req.(*Notification))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Notifier_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Config)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Configure(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Configure",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Configure(ctx, req.(*Config))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Notifier_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.Notifier",
|
||||
HandlerType: (*NotifierServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Notify",
|
||||
Handler: _Notifier_Notify_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Configure",
|
||||
Handler: _Notifier_Configure_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "notifier.proto",
|
||||
}
|
21
plugins/notifications/slack/LICENSE
Normal file
21
plugins/notifications/slack/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Crowdsec
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
18
plugins/notifications/slack/Makefile
Normal file
18
plugins/notifications/slack/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-slack
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
||||
clean:
|
||||
@rm -f $(BINARY_NAME)
|
||||
|
||||
|
||||
static: clean
|
||||
$(GOBUILD) $(LD_OPTS_STATIC) -o $(BINARY_NAME) -v -a -tags netgo
|
13
plugins/notifications/slack/go.mod
Normal file
13
plugins/notifications/slack/go.mod
Normal file
|
@ -0,0 +1,13 @@
|
|||
module github.com/crowdsecurity/slack-plugin
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/hashicorp/go-hclog v0.14.1
|
||||
github.com/hashicorp/go-plugin v1.4.2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/slack-go/slack v0.9.2
|
||||
google.golang.org/grpc v1.39.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
152
plugins/notifications/slack/go.sum
Normal file
152
plugins/notifications/slack/go.sum
Normal file
|
@ -0,0 +1,152 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-plugin v1.4.2 h1:yFvG3ufXXpqiMiZx9HLcaK3XbIqQ1WJFR/F1a2CuVw0=
|
||||
github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/slack-go/slack v0.9.2 h1:tjIrKKYUCOmWeEAktWShKW+3UjLTH/wmgmCkAGAf8wM=
|
||||
github.com/slack-go/slack v0.9.2/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
58
plugins/notifications/slack/interface.go
Normal file
58
plugins/notifications/slack/interface.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Handshake is a common handshake that is shared by plugin and host.
|
||||
var Handshake = plugin.HandshakeConfig{
|
||||
// This isn't required when using VersionedPlugins
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "BASIC_PLUGIN",
|
||||
MagicCookieValue: "hello",
|
||||
}
|
||||
|
||||
// KV is the interface that we're exposing as a plugin.
|
||||
type Notifier interface {
|
||||
Notify(ctx context.Context, notification *Notification) (*Empty, error)
|
||||
Configure(ctx context.Context, config *Config) (*Empty, error)
|
||||
}
|
||||
|
||||
// This is the implementation of plugin.NotifierPlugin so we can serve/consume this.
|
||||
type NotifierPlugin struct {
|
||||
// GRPCPlugin must still implement the Plugin interface
|
||||
plugin.Plugin
|
||||
// Concrete implementation, written in Go. This is only used for plugins
|
||||
// that are written in Go.
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
type GRPCClient struct{ client NotifierClient }
|
||||
|
||||
func (m *GRPCClient) Notify(ctx context.Context, notification *Notification) (*Empty, error) {
|
||||
_, err := m.client.Notify(context.Background(), notification)
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
func (m *GRPCClient) Configure(ctx context.Context, config *Config) (*Empty, error) {
|
||||
_, err := m.client.Configure(context.Background(), config)
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
// Here is the gRPC server that GRPCClient talks to.
|
||||
type GRPCServer struct {
|
||||
// This is the real implementation
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
||||
RegisterNotifierServer(s, p.Impl)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
return &GRPCClient{client: NewNotifierClient(c)}, nil
|
||||
}
|
394
plugins/notifications/slack/notifier.pb.go
Normal file
394
plugins/notifications/slack/notifier.pb.go
Normal file
|
@ -0,0 +1,394 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.12.4
|
||||
// source: notifier.proto
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Notification) Reset() {
|
||||
*x = Notification{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Notification) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Notification) ProtoMessage() {}
|
||||
|
||||
func (x *Notification) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
||||
func (*Notification) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Notification) GetText() string {
|
||||
if x != nil {
|
||||
return x.Text
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetConfig() []byte {
|
||||
if x != nil {
|
||||
return x.Config
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Empty struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
var File_notifier_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_notifier_proto_rawDesc = []byte{
|
||||
0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22,
|
||||
0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x61, 0x0a, 0x08, 0x4e, 0x6f,
|
||||
0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79,
|
||||
0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65,
|
||||
0x12, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a,
|
||||
0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x08, 0x5a,
|
||||
0x06, 0x2e, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_notifier_proto_rawDescOnce sync.Once
|
||||
file_notifier_proto_rawDescData = file_notifier_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_notifier_proto_rawDescGZIP() []byte {
|
||||
file_notifier_proto_rawDescOnce.Do(func() {
|
||||
file_notifier_proto_rawDescData = protoimpl.X.CompressGZIP(file_notifier_proto_rawDescData)
|
||||
})
|
||||
return file_notifier_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_notifier_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_notifier_proto_goTypes = []interface{}{
|
||||
(*Notification)(nil), // 0: proto.Notification
|
||||
(*Config)(nil), // 1: proto.Config
|
||||
(*Empty)(nil), // 2: proto.Empty
|
||||
}
|
||||
var file_notifier_proto_depIdxs = []int32{
|
||||
0, // 0: proto.Notifier.Notify:input_type -> proto.Notification
|
||||
1, // 1: proto.Notifier.Configure:input_type -> proto.Config
|
||||
2, // 2: proto.Notifier.Notify:output_type -> proto.Empty
|
||||
2, // 3: proto.Notifier.Configure:output_type -> proto.Empty
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_notifier_proto_init() }
|
||||
func file_notifier_proto_init() {
|
||||
if File_notifier_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_notifier_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Notification); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Empty); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_notifier_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_notifier_proto_goTypes,
|
||||
DependencyIndexes: file_notifier_proto_depIdxs,
|
||||
MessageInfos: file_notifier_proto_msgTypes,
|
||||
}.Build()
|
||||
File_notifier_proto = out.File
|
||||
file_notifier_proto_rawDesc = nil
|
||||
file_notifier_proto_goTypes = nil
|
||||
file_notifier_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// NotifierClient is the client API for Notifier service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type NotifierClient interface {
|
||||
Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error)
|
||||
Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type notifierClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewNotifierClient(cc grpc.ClientConnInterface) NotifierClient {
|
||||
return ¬ifierClient{cc}
|
||||
}
|
||||
|
||||
func (c *notifierClient) Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Notify", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierClient) Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Configure", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NotifierServer is the server API for Notifier service.
|
||||
type NotifierServer interface {
|
||||
Notify(context.Context, *Notification) (*Empty, error)
|
||||
Configure(context.Context, *Config) (*Empty, error)
|
||||
}
|
||||
|
||||
// UnimplementedNotifierServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedNotifierServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedNotifierServer) Notify(context.Context, *Notification) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Notify not implemented")
|
||||
}
|
||||
func (*UnimplementedNotifierServer) Configure(context.Context, *Config) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented")
|
||||
}
|
||||
|
||||
func RegisterNotifierServer(s *grpc.Server, srv NotifierServer) {
|
||||
s.RegisterService(&_Notifier_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Notifier_Notify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Notification)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Notify(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Notify",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Notify(ctx, req.(*Notification))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Notifier_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Config)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Configure(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Configure",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Configure(ctx, req.(*Config))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Notifier_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.Notifier",
|
||||
HandlerType: (*NotifierServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Notify",
|
||||
Handler: _Notifier_Notify_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Configure",
|
||||
Handler: _Notifier_Configure_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "notifier.proto",
|
||||
}
|
27
plugins/notifications/slack/slack.yaml
Normal file
27
plugins/notifications/slack/slack.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Don't change this
|
||||
type: slack
|
||||
|
||||
name: slack_default # this must match with the registered plugin in the profile
|
||||
log_level: info # Options include: trace, debug, info, warn, error, off
|
||||
|
||||
format: | # This template receives list of models.Alert objects
|
||||
{{range . -}}
|
||||
{{$alert := . -}}
|
||||
{{range .Decisions -}}
|
||||
{{if $alert.Source.Cn -}}
|
||||
:flag-{{$alert.Source.Cn}}: <https://www.whois.com/whois/{{.Value}}|{{.Value}}> will get {{.Type}} for next {{.Duration}} for triggering {{.Scenario}}. <https://www.shodan.io/host/{{.Value}}|Shodan>{{end}}
|
||||
{{if not $alert.Source.Cn -}}
|
||||
:pirate_flag: <https://www.whois.com/whois/{{.Value}}|{{.Value}}> will get {{.Type}} for next {{.Duration}} for triggering {{.Scenario}}. <https://www.shodan.io/host/{{.Value}}|Shodan>{{end}}
|
||||
{{end -}}
|
||||
{{end -}}
|
||||
|
||||
|
||||
webhook: <WEBHOOK_URL>
|
||||
|
||||
# group_wait: # duration to wait collecting alerts before sending to this plugin, eg "30s"
|
||||
|
||||
# group_threshold: # if alerts exceed this, then the plugin will be sent the message. eg "10"
|
||||
|
||||
# max_retry: # number of tries to attempt to send message to plugins in case of error.
|
||||
|
||||
# timeout: # duration to wait for response from plugin before considering this attempt a failure. eg "10s"
|
80
plugins/notifications/slack/slack_plugin.go
Normal file
80
plugins/notifications/slack/slack_plugin.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
|
||||
"github.com/slack-go/slack"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type PluginConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Webhook string `yaml:"webhook"`
|
||||
LogLevel *string `yaml:"log_level"`
|
||||
}
|
||||
type Notify struct {
|
||||
ConfigByName map[string]PluginConfig
|
||||
}
|
||||
|
||||
var logger hclog.Logger = hclog.New(&hclog.LoggerOptions{
|
||||
Name: "slack-plugin",
|
||||
Level: hclog.LevelFromString("DEBUG"),
|
||||
Output: os.Stderr,
|
||||
JSONFormat: true,
|
||||
})
|
||||
|
||||
func (n *Notify) Notify(ctx context.Context, notification *Notification) (*Empty, error) {
|
||||
if _, ok := n.ConfigByName[notification.Name]; !ok {
|
||||
return nil, fmt.Errorf("invalid plugin config name %s", notification.Name)
|
||||
}
|
||||
cfg := n.ConfigByName[notification.Name]
|
||||
if cfg.LogLevel != nil && *cfg.LogLevel != "" {
|
||||
logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel))
|
||||
} else {
|
||||
logger.SetLevel(hclog.Info)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("found notify signal for %s config", notification.Name))
|
||||
logger.Debug(fmt.Sprintf("posting to %s webhook, message %s", cfg.Webhook, notification.Text))
|
||||
err := slack.PostWebhook(n.ConfigByName[notification.Name].Webhook, &slack.WebhookMessage{
|
||||
Text: notification.Text,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
func (n *Notify) Configure(ctx context.Context, config *Config) (*Empty, error) {
|
||||
d := PluginConfig{}
|
||||
if err := yaml.Unmarshal(config.Config, &d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.ConfigByName[d.Name] = d
|
||||
return &Empty{}, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var handshake = plugin.HandshakeConfig{
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "CROWDSEC_PLUGIN_KEY",
|
||||
MagicCookieValue: os.Getenv("CROWDSEC_PLUGIN_KEY"),
|
||||
}
|
||||
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: handshake,
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
"slack": &NotifierPlugin{
|
||||
Impl: &Notify{ConfigByName: make(map[string]PluginConfig)},
|
||||
},
|
||||
},
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
Logger: logger,
|
||||
})
|
||||
}
|
21
plugins/notifications/splunk/LICENSE
Normal file
21
plugins/notifications/splunk/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Crowdsec
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
17
plugins/notifications/splunk/Makefile
Normal file
17
plugins/notifications/splunk/Makefile
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
# Go parameters
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
BINARY_NAME=notification-splunk
|
||||
|
||||
build: clean
|
||||
@$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v
|
||||
|
||||
clean:
|
||||
@rm -f $(BINARY_NAME)
|
||||
|
||||
static: clean
|
||||
$(GOBUILD) $(LD_OPTS_STATIC) -o $(BINARY_NAME) -v -a -tags netgo
|
12
plugins/notifications/splunk/go.mod
Normal file
12
plugins/notifications/splunk/go.mod
Normal file
|
@ -0,0 +1,12 @@
|
|||
module github.com/crowdsecurity/splunk-plugin
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/hashicorp/go-hclog v0.14.1
|
||||
github.com/hashicorp/go-plugin v1.4.2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
google.golang.org/grpc v1.39.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
145
plugins/notifications/splunk/go.sum
Normal file
145
plugins/notifications/splunk/go.sum
Normal file
|
@ -0,0 +1,145 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-plugin v1.4.2 h1:yFvG3ufXXpqiMiZx9HLcaK3XbIqQ1WJFR/F1a2CuVw0=
|
||||
github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
58
plugins/notifications/splunk/interface.go
Normal file
58
plugins/notifications/splunk/interface.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Handshake is a common handshake that is shared by plugin and host.
|
||||
var Handshake = plugin.HandshakeConfig{
|
||||
// This isn't required when using VersionedPlugins
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "BASIC_PLUGIN",
|
||||
MagicCookieValue: "hello",
|
||||
}
|
||||
|
||||
// KV is the interface that we're exposing as a plugin.
|
||||
type Notifier interface {
|
||||
Notify(ctx context.Context, notification *Notification) (*Empty, error)
|
||||
Configure(ctx context.Context, config *Config) (*Empty, error)
|
||||
}
|
||||
|
||||
// This is the implementation of plugin.NotifierPlugin so we can serve/consume this.
|
||||
type NotifierPlugin struct {
|
||||
// GRPCPlugin must still implement the Plugin interface
|
||||
plugin.Plugin
|
||||
// Concrete implementation, written in Go. This is only used for plugins
|
||||
// that are written in Go.
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
type GRPCClient struct{ client NotifierClient }
|
||||
|
||||
func (m *GRPCClient) Notify(ctx context.Context, notification *Notification) (*Empty, error) {
|
||||
_, err := m.client.Notify(context.Background(), notification)
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
func (m *GRPCClient) Configure(ctx context.Context, config *Config) (*Empty, error) {
|
||||
_, err := m.client.Configure(context.Background(), config)
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
// Here is the gRPC server that GRPCClient talks to.
|
||||
type GRPCServer struct {
|
||||
// This is the real implementation
|
||||
Impl Notifier
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
||||
RegisterNotifierServer(s, p.Impl)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *NotifierPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
return &GRPCClient{client: NewNotifierClient(c)}, nil
|
||||
}
|
117
plugins/notifications/splunk/main.go
Normal file
117
plugins/notifications/splunk/main.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var logger hclog.Logger = hclog.New(&hclog.LoggerOptions{
|
||||
Name: "splunk-plugin",
|
||||
Level: hclog.LevelFromString("DEBUG"),
|
||||
Output: os.Stderr,
|
||||
JSONFormat: true,
|
||||
})
|
||||
|
||||
type PluginConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
URL string `yaml:"url"`
|
||||
Token string `yaml:"token"`
|
||||
LogLevel *string `yaml:"log_level"`
|
||||
}
|
||||
|
||||
type Splunk struct {
|
||||
PluginConfigByName map[string]PluginConfig
|
||||
Client http.Client
|
||||
}
|
||||
|
||||
type Payload struct {
|
||||
Event string `json:"event"`
|
||||
}
|
||||
|
||||
func (s *Splunk) Notify(ctx context.Context, notification *Notification) (*Empty, error) {
|
||||
if _, ok := s.PluginConfigByName[notification.Name]; !ok {
|
||||
return &Empty{}, fmt.Errorf("splunk invalid config name %s", notification.Name)
|
||||
}
|
||||
cfg := s.PluginConfigByName[notification.Name]
|
||||
if cfg.LogLevel != nil && *cfg.LogLevel != "" {
|
||||
logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel))
|
||||
} else {
|
||||
logger.SetLevel(hclog.Info)
|
||||
}
|
||||
logger.Info(fmt.Sprintf("received notify signal for %s config", notification.Name))
|
||||
|
||||
p := Payload{Event: notification.Text}
|
||||
data, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", cfg.URL, strings.NewReader(string(data)))
|
||||
if err != nil {
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Splunk %s", cfg.Token))
|
||||
logger.Debug(fmt.Sprintf("posting event %s to %s", string(data), req.URL))
|
||||
resp, err := s.Client.Do(req)
|
||||
if err != nil {
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return &Empty{}, fmt.Errorf("got non 200 response and failed to read error %s", string(err.Error()))
|
||||
}
|
||||
return &Empty{}, fmt.Errorf("got non 200 response %s", string(content))
|
||||
}
|
||||
respData, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return &Empty{}, fmt.Errorf("failed to read response body got error %s", string(err.Error()))
|
||||
}
|
||||
logger.Debug(fmt.Sprintf("got response %s", string(respData)))
|
||||
return &Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *Splunk) Configure(ctx context.Context, config *Config) (*Empty, error) {
|
||||
d := PluginConfig{}
|
||||
err := yaml.Unmarshal(config.Config, &d)
|
||||
s.PluginConfigByName[d.Name] = d
|
||||
return &Empty{}, err
|
||||
}
|
||||
|
||||
func main() {
|
||||
var handshake = plugin.HandshakeConfig{
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "CROWDSEC_PLUGIN_KEY",
|
||||
MagicCookieValue: os.Getenv("CROWDSEC_PLUGIN_KEY"),
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
sp := &Splunk{PluginConfigByName: make(map[string]PluginConfig), Client: *client}
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: handshake,
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
"splunk": &NotifierPlugin{
|
||||
Impl: sp,
|
||||
},
|
||||
},
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
Logger: logger,
|
||||
})
|
||||
}
|
394
plugins/notifications/splunk/notifier.pb.go
Normal file
394
plugins/notifications/splunk/notifier.pb.go
Normal file
|
@ -0,0 +1,394 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.12.4
|
||||
// source: notifier.proto
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Notification) Reset() {
|
||||
*x = Notification{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Notification) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Notification) ProtoMessage() {}
|
||||
|
||||
func (x *Notification) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
||||
func (*Notification) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Notification) GetText() string {
|
||||
if x != nil {
|
||||
return x.Text
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Config []byte `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetConfig() []byte {
|
||||
if x != nil {
|
||||
return x.Config
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Empty struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_notifier_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_notifier_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
var File_notifier_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_notifier_proto_rawDesc = []byte{
|
||||
0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22,
|
||||
0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x61, 0x0a, 0x08, 0x4e, 0x6f,
|
||||
0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79,
|
||||
0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65,
|
||||
0x12, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a,
|
||||
0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x08, 0x5a,
|
||||
0x06, 0x2e, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_notifier_proto_rawDescOnce sync.Once
|
||||
file_notifier_proto_rawDescData = file_notifier_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_notifier_proto_rawDescGZIP() []byte {
|
||||
file_notifier_proto_rawDescOnce.Do(func() {
|
||||
file_notifier_proto_rawDescData = protoimpl.X.CompressGZIP(file_notifier_proto_rawDescData)
|
||||
})
|
||||
return file_notifier_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_notifier_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_notifier_proto_goTypes = []interface{}{
|
||||
(*Notification)(nil), // 0: proto.Notification
|
||||
(*Config)(nil), // 1: proto.Config
|
||||
(*Empty)(nil), // 2: proto.Empty
|
||||
}
|
||||
var file_notifier_proto_depIdxs = []int32{
|
||||
0, // 0: proto.Notifier.Notify:input_type -> proto.Notification
|
||||
1, // 1: proto.Notifier.Configure:input_type -> proto.Config
|
||||
2, // 2: proto.Notifier.Notify:output_type -> proto.Empty
|
||||
2, // 3: proto.Notifier.Configure:output_type -> proto.Empty
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_notifier_proto_init() }
|
||||
func file_notifier_proto_init() {
|
||||
if File_notifier_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_notifier_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Notification); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_notifier_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Empty); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_notifier_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_notifier_proto_goTypes,
|
||||
DependencyIndexes: file_notifier_proto_depIdxs,
|
||||
MessageInfos: file_notifier_proto_msgTypes,
|
||||
}.Build()
|
||||
File_notifier_proto = out.File
|
||||
file_notifier_proto_rawDesc = nil
|
||||
file_notifier_proto_goTypes = nil
|
||||
file_notifier_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// NotifierClient is the client API for Notifier service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type NotifierClient interface {
|
||||
Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error)
|
||||
Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type notifierClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewNotifierClient(cc grpc.ClientConnInterface) NotifierClient {
|
||||
return ¬ifierClient{cc}
|
||||
}
|
||||
|
||||
func (c *notifierClient) Notify(ctx context.Context, in *Notification, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Notify", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *notifierClient) Configure(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Notifier/Configure", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NotifierServer is the server API for Notifier service.
|
||||
type NotifierServer interface {
|
||||
Notify(context.Context, *Notification) (*Empty, error)
|
||||
Configure(context.Context, *Config) (*Empty, error)
|
||||
}
|
||||
|
||||
// UnimplementedNotifierServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedNotifierServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedNotifierServer) Notify(context.Context, *Notification) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Notify not implemented")
|
||||
}
|
||||
func (*UnimplementedNotifierServer) Configure(context.Context, *Config) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented")
|
||||
}
|
||||
|
||||
func RegisterNotifierServer(s *grpc.Server, srv NotifierServer) {
|
||||
s.RegisterService(&_Notifier_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Notifier_Notify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Notification)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Notify(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Notify",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Notify(ctx, req.(*Notification))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Notifier_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Config)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NotifierServer).Configure(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Notifier/Configure",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NotifierServer).Configure(ctx, req.(*Config))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Notifier_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.Notifier",
|
||||
HandlerType: (*NotifierServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Notify",
|
||||
Handler: _Notifier_Notify_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Configure",
|
||||
Handler: _Notifier_Configure_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "notifier.proto",
|
||||
}
|
20
plugins/notifications/splunk/splunk.yaml
Normal file
20
plugins/notifications/splunk/splunk.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Don't change this
|
||||
type: splunk
|
||||
|
||||
name: splunk_default # this must match with the registered plugin in the profile
|
||||
log_level: info # Options include: trace, debug, info, warn, error, off
|
||||
|
||||
format: | # This template receives list of models.Alert objects
|
||||
{{.|toJson}}
|
||||
|
||||
url: <SPLUNK_HTTP_URL>
|
||||
token: <SPLUNK_TOKEN>
|
||||
|
||||
|
||||
# group_wait: # duration to wait collecting alerts before sending to this plugin, eg "30s"
|
||||
|
||||
# group_threshold: # if alerts exceed this, then the plugin will be sent the message. eg "10"
|
||||
|
||||
# max_retry: # number of tries to attempt to send message to plugins in case of error.
|
||||
|
||||
# timeout: # duration to wait for response from plugin before considering this attempt a failure. eg "10s"
|
|
@ -47,6 +47,12 @@ mkdir -p %{buildroot}/etc/crowdsec/hub
|
|||
mkdir -p %{buildroot}/etc/crowdsec/patterns
|
||||
mkdir -p %{buildroot}%{_sharedstatedir}/%{name}/data
|
||||
mkdir -p %{buildroot}%{_presetdir}
|
||||
|
||||
mkdir -p %{buildroot}%{_sharedstatedir}/%{name}/plugins
|
||||
mkdir -p %{buildroot}%{_sysconfdir}/crowdsec/notifications/
|
||||
|
||||
|
||||
|
||||
install -m 755 -D cmd/crowdsec/crowdsec %{buildroot}%{_bindir}/%{name}
|
||||
install -m 755 -D cmd/crowdsec-cli/cscli %{buildroot}%{_bindir}/cscli
|
||||
install -m 755 -D wizard.sh %{buildroot}/usr/share/crowdsec/wizard.sh
|
||||
|
@ -57,6 +63,16 @@ install -m 644 -D config/simulation.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
|||
install -m 644 -D config/profiles.yaml %{buildroot}%{_sysconfdir}/crowdsec
|
||||
install -m 644 -D %{SOURCE1} %{buildroot}%{_presetdir}
|
||||
|
||||
install -m 551 plugins/notifications/slack/notification-slack %{buildroot}%{_sharedstatedir}/%{name}/plugins/
|
||||
install -m 551 plugins/notifications/http/notification-http %{buildroot}%{_sharedstatedir}/%{name}/plugins/
|
||||
install -m 551 plugins/notifications/splunk/notification-splunk %{buildroot}%{_sharedstatedir}/%{name}/plugins/
|
||||
|
||||
install -m 644 plugins/notifications/slack/slack.yaml %{buildroot}%{_sysconfdir}/crowdsec/notifications/
|
||||
install -m 644 plugins/notifications/http/http.yaml %{buildroot}%{_sysconfdir}/crowdsec/notifications/
|
||||
install -m 644 plugins/notifications/splunk/splunk.yaml %{buildroot}%{_sysconfdir}/crowdsec/notifications/
|
||||
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
|
|
34
wizard.sh
34
wizard.sh
|
@ -23,7 +23,7 @@ CROWDSEC_PATH="/etc/crowdsec"
|
|||
CROWDSEC_CONFIG_PATH="${CROWDSEC_PATH}"
|
||||
CROWDSEC_LOG_FILE="/var/log/crowdsec.log"
|
||||
LAPI_LOG_FILE="/var/log/crowdsec_api.log"
|
||||
|
||||
CROWDSEC_PLUGIN_DIR="/var/lib/crowdsec/plugins/"
|
||||
|
||||
CROWDSEC_BIN="./cmd/crowdsec/crowdsec"
|
||||
CSCLI_BIN="./cmd/crowdsec-cli/cscli"
|
||||
|
@ -64,6 +64,15 @@ telnet
|
|||
smb
|
||||
'
|
||||
|
||||
|
||||
HTTP_PLUGIN_BINARY="./plugins/notifications/http/notification-http"
|
||||
SLACK_PLUGIN_BINARY="./plugins/notifications/slack/notification-slack"
|
||||
SPLUNK_PLUGIN_BINARY="./plugins/notifications/splunk/notification-splunk"
|
||||
|
||||
HTTP_PLUGIN_CONFIG="./plugins/notifications/http/http.yaml"
|
||||
SLACK_PLUGIN_CONFIG="./plugins/notifications/slack/slack.yaml"
|
||||
SPLUNK_PLUGIN_CONFIG="./plugins/notifications/splunk/splunk.yaml"
|
||||
|
||||
BACKUP_DIR=$(mktemp -d)
|
||||
rm -rf $BACKUP_DIR
|
||||
|
||||
|
@ -451,6 +460,11 @@ install_bins() {
|
|||
log_dbg "Installing crowdsec binaries"
|
||||
install -v -m 755 -D "${CROWDSEC_BIN}" "${CROWDSEC_BIN_INSTALLED}" 1> /dev/null || exit
|
||||
install -v -m 755 -D "${CSCLI_BIN}" "${CSCLI_BIN_INSTALLED}" 1> /dev/null || exit
|
||||
systemctl is-active --quiet crowdsec
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl stop crowdsec
|
||||
fi
|
||||
install_plugins
|
||||
symlink_bins
|
||||
}
|
||||
|
||||
|
@ -469,6 +483,24 @@ delete_bins() {
|
|||
rm -f ${CSCLI_BIN_INSTALLED}
|
||||
}
|
||||
|
||||
delete_plugins() {
|
||||
rm -rf ${CROWDSEC_PLUGIN_DIR}
|
||||
}
|
||||
|
||||
install_plugins(){
|
||||
mkdir -p ${CROWDSEC_PLUGIN_DIR}
|
||||
mkdir -p /etc/crowdsec/notifications
|
||||
|
||||
cp ${SLACK_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
|
||||
cp -n ${SLACK_PLUGIN_CONFIG} /etc/crowdsec/notifications
|
||||
|
||||
cp ${SPLUNK_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
|
||||
cp -n ${SPLUNK_PLUGIN_CONFIG} /etc/crowdsec/notifications
|
||||
|
||||
cp ${HTTP_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
|
||||
cp -n ${HTTP_PLUGIN_CONFIG} /etc/crowdsec/notifications
|
||||
}
|
||||
|
||||
check_running_bouncers() {
|
||||
#when uninstalling, check if user still has bouncers
|
||||
BOUNCERS_COUNT=$(${CSCLI_BIN} bouncers list -o=json | jq '. | length')
|
||||
|
|
Loading…
Reference in a new issue